c array of structures - c

This is a lot of code but I'm at ends here trying to figure out why its not working.
This is the code I'm running:
struct dict {
struct word **tbl;
int (*hash_fcn)(struct word*);
void (*add_fcn)(struct word*);
void (*remove_fcn)(struct word*);
void (*toString_fcn)(struct word*);
};
struct word {
char *s;
struct word *next;
};
this is part of the main function:
hashtbl=malloc(sizeof(struct dict));
//hashtbl->tbl=malloc(sizeof(struct word*)*256);
int i;
for(i=0;i<256;i++)
{
hashtbl->tbl[i] = malloc(sizeof(struct word));
hashtbl->tbl[i] = NULL;
//hashtbl->tbl[i]=NULL;
}
hashtbl->add_fcn=&add_word;
hashtbl->remove_fcn=&remove_word;
hashtbl->hash_fcn=&hash_function;
hashtbl->toString_fcn=&toString;
FILE *p;
p = fopen("a.txt", "r");
if(p == NULL) { printf("ERROR!\n"); return 0; }
char line[MAXBYTES];
while(fgets(line, MAXBYTES, p))
{
char *token = strtok(line, " ");
while(token != NULL)
{
struct word *words = malloc(sizeof(struct word));
words->s = token;
words->next=NULL;
trim(words);
hashtbl->add_fcn(words);
token = strtok(NULL, " ");
printf("....\n");
hashtbl->toString_fcn(NULL);
printf("....\n\n");
}
free(token);
}
Here are two functions used (to String just prints it out)
void add_word(struct word *insert)
{
if(strcmp(insert->s, "\n") == 0) {return;}
int hash = hashtbl->hash_fcn(insert);
if(hash==0) { return; }
struct word *word = hashtbl->tbl[hash];
if(word==NULL)
{
struct word *newword = malloc(sizeof(struct word));
newword->s=insert->s;
newword->next=NULL;
hashtbl->tbl[hash]=newword;
printf("() %d %s \n", hash, hashtbl->tbl[hash]->s);
}
else
{
struct word *tp = word;
while(word->next != NULL)
word=word->next;
struct word *newword = malloc(sizeof(struct word));
newword->s=insert->s;
newword->next=NULL;
word->next=newword;
hashtbl->tbl[hash]=tp;
}
}
int hash_function(struct word *string)
{
char *firstletter = string->s;
char c = *firstletter;
int ascii = (int)c;
return ascii;
}
a.txt is
cat
dog
mouse
and it prints out the following:
() 99 cat
....
99 : cat
....
() 100 dog
....
99 : dog
100 : dog
....
() 109 mouse
....
99 : mouse
100 : mouse
109 : mouse
....
it should be printing out
99:cat
100:dog
109:mouse
thanks

You have many problems here.
First:
hashtbl=malloc(sizeof(struct dict));
//hashtbl->tbl=malloc(sizeof(struct word*)*256);
You need to uncomment that line, since without it you don't allocate the array of struct pointers in hashtbl->tbl, and leave it uninitialized. It'll probably cause a segfault as is.
Next:
for(i=0;i<256;i++)
{
hashtbl->tbl[i] = malloc(sizeof(struct word));
hashtbl->tbl[i] = NULL;
//hashtbl->tbl[i]=NULL;
}
This is leaking memory. You're allocating a struct word to each element of hashtbl->tbl, but then overwriting the pointer with NULL. So you leak all the memory you allocated, and leave each array element set to NULL. Get rid of the malloc() and just set them to NULL, since you'll allocate the structs later when you actually add a word.
In this part:
hashtbl->add_fcn=&add_word;
hashtbl->remove_fcn=&remove_word;
hashtbl->hash_fcn=&hash_function;
hashtbl->toString_fcn=&toString;
...the & are not needed -- the function names without parentheses are good enough. As long as the parentheses are omitted, it will be interpreted as the address of the function and not a function call.
And then here:
char *token = strtok(line, " ");
while(token != NULL)
{
struct word *words = malloc(sizeof(struct word));
words->s = token;
...the char * returned by strtok() typically points within the string you're processing, i.e. within the buffer you read lines into. It will not remain intact while you continue processing the file. You need to make a copy of the string, not just save the pointer.
free(token);
...are you sure you should free this?

Related

Array of structure overwritten while reading from a file in C

I am trying to build an array of structures with a string and starting address of a linked list of another structure. Several strings from each line of a file is to be filled into this data structure. But whenever I am coming back to the next line, all the variables of the array and LL that I have filled up so far is being changed to the variables in the current line. As a result all the elements of the array as well as the corresponding linked lists are giving the same result in each element of the array. Here is the code.
struct node
{
char* nodedata;
struct node* link;
};
struct motief
{
char* motiefname;
struct node* link;
};
void add_unique_nodes(struct motief*,int,char *);
int unique_motief(struct motief*,char*);
void add_unique_nodes(struct motief* motiefs,int motief_no,char * token)
{
struct node *temp,*r;
r = malloc(sizeof(struct node));
r->nodedata = token;
//for the first node
if(motiefs[motief_no].link == NULL)
{
motiefs[motief_no].link = r;
}
else
{
temp = motiefs[motief_no].link;
while(temp->link != NULL && token != temp->nodedata)
temp = temp->link;
if(token != temp->nodedata)
temp->link = r;
}
r->link = NULL;
}
void main()
{
struct motief motiefs[100];
FILE *fp, *ft;
fp = fopen("dump.txt","r");
ft = fopen("motief_nodes","w");
char line[100] ={0};
char seps[] = ",";
char* token;
int motief_no = 0;
int i,j;//loop variable
//read the database
while(!feof(fp))
{
if( fgets(line, sizeof(line), fp ))
{
if(motief_no == 1)
printf("for start of 2nd step %s\t",motiefs[0].motiefname);//????
printf("for line %d\t",motief_no);
//get the motief from each input line as a token
token = strtok (line, seps);
//store it in motief array
motiefs[motief_no].motiefname = token;
printf("%s\n",motiefs[motief_no].motiefname);
if(motief_no == 1)
printf("for zero %s\n",motiefs[0].motiefname);
motiefs[motief_no].link = NULL;
//get and store all the nodes
while (token != NULL)
{
//get the node
token = strtok (NULL, seps);
if(token != NULL)
add_unique_nodes(motiefs,motief_no,token);
}
if(motief_no == 0)
printf("for end of 1st step %s\n",motiefs[0].motiefname);
motief_no++;
if(motief_no == 2)//break at 2nd loop, at 1
break;
}
I am new to C programming. I cannot find why it is happening. Please help me to find where I am going wrong and why the file is read into the array besides the specified variable for that purpose in my code. Thanks in advance. Here are few lines from the file to be read.
000100110,1,95,89
000100111,1,95,87
000100110,1,95,74
000100110,1,95,51
I am displaying the structure with the following code
struct node* temp;
for(j=0;j<2;j++)
{
printf("turn %d\t",j);
printf("%s\t",motiefs[j].motiefname);
temp = motiefs[j].link;
printf("%s\t",temp->nodedata);
do
{
temp = temp->link;
printf("%s\t",temp->nodedata);
}while(temp->link != NULL);
printf("\n");
}
And the it shows the following overall result
for line 0 000100110
for end of 1st step 000100110
for start of 2nd step 000100111,1,95,87
for line 1 000100111
for zero 000100111
turn 0 000100111 1 95 87
turn 1 000100111 1 95 87
You keep changing the same memory space when you read into 'line'. you need to allocate new memory space each time you want a different array.
Picture 'line' as pointing to a specific chunk of 100 bytes in a row. You keep telling the fgets function to write to that location, and you also keep copying the address of that location into your structs when you assign the 'token' to moteifname.
Then when you change what's at that address of course it's overwriting what the struct points to as well!
You need to choose to allocate the space in each struct instead of having an internal pointer OR you need to dynamically allocate space each iteration using malloc and then free() all the pointers at the end.
https://www.codingunit.com/c-tutorial-the-functions-malloc-and-free

How to allocate memory for time_t in a string in C

I'm trying to build a string that will go into a logfile with this format: "Executable: Time:... Error: ...". However, I am having trouble allocating for my time variable in my data structure. I have been able to code it so that the time can go before the string later but I cannot figure out how to have the time be between the executable and the error message. If anyone could tell me what I'm doing wrong I'd appreciate it.
Code:
log.h
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include <time.h>
typedef struct data_struct
{
time_t time;
char *string;
} data_t;
int addmsg(data_t data, char *arg0);
void clearlog(void);
char *getlog(void);
int savelog(char *filename);
loglib.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include "log.h"
//Basic template from Excercise 2.13
typedef struct list_struct
{
data_t item;
struct list_struct *next;
} log_t;
static log_t *headPtr = NULL;
static log_t *tailPtr = NULL;
//Like book example with add on for executable name
int addmsg(data_t data, char *arg0)
{
log_t *newnode;
int nodesize;
char timeString[] = ": Time: ";
char errorString[] = " Error: ";
//Allocate size for "Executable: time: Error: "
nodesize = sizeof(log_t) + strlen(data.string) + strlen(arg0) + sizeof(time_t) + strlen(timeString) + strlen(errorString) + 1;
if((newnode = (log_t *)(malloc(nodesize))) == NULL)
{
perror("Malloc failed: ");
return -1;
}
//Makes string "Executable: time: Error: "
newnode->item.time = data.time;
char *timeCode = ctime(&newnode->item.time);
newnode->item.string = (char *)newnode + sizeof(log_t);
strcpy(newnode->item.string, arg0);
newnode->item.string = strcat(newnode->item.string, timeString);
newnode->item.string = strcat(newnode->item.string, timeCode);
newnode->item.string = strcat(newnode->item.string, errorString);
newnode->item.string = strcat(newnode->item.string, data.string);
newnode->next = NULL;
//Puts string as EOF
if(headPtr == NULL)
{
headPtr = newnode;
}
else
{
tailPtr->next = newnode;
}
tailPtr = newnode;
return 0;
}
//Clears log
void clearlog(void)
{
log_t *nextPtr = headPtr;
//Loop through and clear the log
while(nextPtr != NULL)
{
nextPtr = headPtr->next;
free(headPtr);
headPtr = nextPtr;
}
}
char *getlog(void)
{
int size = 1;//Set to 1 because of null terminator
int entryNum = 0; //Number of error logs
log_t *node = headPtr; //Start at beggining
char *wholeLog = NULL; //Used to return the entire log
char *entryString = NULL;
//Get size
while(node != NULL)
{
entryNum++;
size += strlen(node->item.string);
node = node->next;
}
//Memory allocation
wholeLog = malloc(sizeof(char)*(size + 1 + (entryNum * 2)));
if(wholeLog == NULL)
{
perror("Malloc failed: ");
return NULL;
}
//Reset node to beggining
node = headPtr;
//Builds the entire log to return
while(node != NULL)
{
entryString = strcat(entryString, node->item.string);
wholeLog = strcat(wholeLog, entryString);
wholeLog = strcat(wholeLog, "\n"); //New line
node = node->next;
}
//Make space
wholeLog += (strlen(wholeLog) - size - (entryNum-1));
return wholeLog;
}
int savelog(char *filename)
{
FILE *f;
char *logPrinter;
f = fopen(filename, "a");
if(!f)
{
perror("Error opening file: ");
return -1;
}
//Get the log to print
logPrinter = getlog();
if(logPrinter == NULL)
{
printf("Empty Log\n");
return 0;
}
fprintf(f, "%s\n", logPrinter);
fclose(f);
return 0;
}
Your code seems bent on calculating the size of a memory buffer that would hold both the log_t node structure and the concatenated message parts, having the string pointer within the data_t member of the linked list node point within the single memory block, just passed the linked list node content, where the message is stored. In short, a single allocation holding both the node and the message.
That said, exploit the fact that there are standard library API's, notably snprintf that can calculate formatted message buffer length requirements for you, and you can then skip most of the string management malaise in favor of the real purpose of this, managing the linked list structure and the dynamic message content with a single invoke to malloc (and by circumstance, a single invoke to free() when this fiasco needs to be undone):
Determine the length of the formatted string data
Allocate a buffer large enough to hold that data, and the structure that will precede it.
Position the string pointer in the structure to the first char just passed the structure layout.
Perform the formatted message dump into the memory pointed to by that string pointer.
The result is a single allocation of dynamic length, depending on the content of the message being formatted.
Using snprintf Instead
int addmsg(data_t data, const char *arg0)
{
static const char fmt[] = "%s: Time: %s Error: %s";
char stime[32] = ""; // per ctime docs, must be at least 26 chars
int res = -1;
// get time string, trim newline
ctime_r(&data.time, stime);
if (*stime)
stime[strlen(stime)-1] = 0;
// find needed message size
int req = snprintf(NULL, 0, fmt, data.string, stime, arg0);
if (req > 0)
{
// include size of log record, formatted message, and terminator
log_t *node = malloc(sizeof (log_t) + req + 1);
if (node != NULL)
{
node->item.string = (char*)(node+1); // NOTE: see below
node->item.time = data.time;
node->next = NULL;
snprintf(node->item.string, req+1, fmt, data.string, stime, arg0);
// link into list
if (!headPtr)
headPtr = node;
else
tailPtr->next = node;
tailPtr = node;
// good return value
res = 0;
}
else
{
perror("Failed to allocate memory for log mesage: ");
}
}
else
{
perror("Failed to perform message formatting: ");
}
return res;
}
Everything above is fairly straight forward, save for possible NOTE, which I'll explain now. It uses pointer arithmetic. Given a pointer node of some type log_t* the expression:
(node + 1)
calculates the next position in memory where a subsequent log_t object could reside, or the "one-past" memory position in the case of the end of a sequence. When we allocated our memory, we did so to a layout that looks like this:
node ---> |=== log_t ===|===== message text =====|
the expression (node+1), using typed pointer arithmetic, give us:
node ---> |=== log_t ===|===== message text =====|
(node+1)-----------------^
which is then cast to char*, saved in node->data.string and used as the target for the final message formatting using snprintf. Short version: node->item.string points to the extra memory we allocated following the log_t structure pointed to by node.
That's it. The result is a single allocation to a linked list node that contains both the node management data, and also contains a pointer to the dynamic message string stored in the suffix memory of the single allocation past the log_t core data.
If you replaced the log_t construction piece of addmsg with something perhaps like this you would get better results. Your calculation of required memory size is a little off. Might want to also avoid assuming things about memory with your malloc (i.e. Allocating extra memory to store both a structure and the contents of a pointer member of that structure could easily get you into trouble)
...
log_t *newnode = NULL;
void *vp = NULL;
if (NULL == (vp = malloc(sizeof(log_t)))) {
perror("malloc failed (sizeof(log_t))");
return -1;
}
newnode = (log_t *)vp;
newnode->item.time = data.time;
char *timeCode = ctime(&newnode->item.time);
int msgsize = strlen(": Time: Error: ")
+ strlen(arg0)
+ strlen(timeCode)
+ strlen(data.string)
+ 1;
if (NULL == (vp = malloc(msgsize))) {
perror("malloc failed (msgsize)");
free(newnode);
return -1;
}
newnode->item.string = (char *)vp;
sprintf(newnode->item.string, "%s: Time: %s Error: %s", arg0, timeCode, data.string);
...

Why does some characters change?

I'm programming with C language in CodeBlocks with GNU GCC compiler.I was writing a function to create some Link List consisting token as nodes.for example, for the below text file:
main ( )
{
int a ;
int b ;
}
the link list of tokens would be
main -> ( -> ) -> { -> int -> a -> ; -> int -> b -> ; -> }
for which the delimiter is the space character.
Then i decided to make some other link list called line. Each line consisting successive tokens separated by space finished by ; character. For example , at the same text file with the relevant tokens the lines would be:
main ( ) { int a ; -> int b ;-> }
you see my code below:
//including the related Header files
typedef struct token {
char *tok;
struct token *next;
int tp;
} token;
typedef struct line {
char *ls;
struct line *next;
int parent;
} line;
token *start;
line *lstart;
void addline (line * a);
void showline (void);
void setline (void);
int main (void ) {
int i = 0;
// the next 4 lines allocates some space for start(pointer of type token)
// and lstart(pointer of type line) as the first node in the link
// list.The first meaningful data of each type are stored in the nodes
// after the start and lstart node
start = (token *) malloc (sizeof (token));
start->next = NULL;
lstart = (line *) malloc (sizeof (line));
lstart->next = NULL;
FILE *p;
p = fopen ("sample.txt", "r+");
if (p == NULL) {
printf ("Can Not Open File");
exit (1);
}
//calling some fnuction for making link list of tokens from the text
//file
setline ();
showline ();
return 0;
}
// the relevant add functions which adds a new token or
// link list at the end of the list
void showline ()
{
line *p;
p = lstart->next;
while (p != NULL) {
printf ("%s\n", p->ls);
p = p->next;
}
}
void setline (void)
{
int parent;
token *p;
p = start->next;
line *q;
q = (line *) malloc (sizeof (line));
q->ls = NULL;
while (p != NULL) {
if (p == NULL) {
break;
}
q->ls = strdup (p->tok);
strcat (q->ls, " ");
p = p->next;
while ((p != NULL)) {
if (strcmp (p->tok, ";") == 0) {
strcat (q->ls, "; ");
p = p->next;
break;
}
strcat (q->ls, p->tok);
strcat (q->ls, " ");
p = p->next;
}
printf ("%s\n", q->ls);
addline (q);
q->ls = NULL;
}
}
and I stored some data in the text file "sample.txt" :
#include <something.h>
int a , b ;
main ( )
{
int a ;
int b ;
}
I expected lines will be made correctly but something strange happened when I called showline() function (This function is used and can be seen in the main ):In some lines there was some strange characters.For example the ls of the second line node was expected to be int b ; But what Really happened was înt b ; in which the usual i character turned into î(a strange character) .Which mistake did i make when working with strings?
One of the problematic places:
q->ls=strdup(p->tok);
strcat(q->ls," ");
The strdup function allocates enough space for p->tok only, there's no space to append anything after the copies string. So calling strcat will of course write out of bounds and you will have undefined behavior.
If you want to append more characters, you need to allocate yourself (using malloc or calloc) with the size you need, and then manually copy the initial string.

Cannot access memory at address in C

I've got a linked list of nodes that contain a string of characters. The program reads characters from stdin until it reaches a new line and once it has it puts this string of characters into the a new node of the list.
I've done some debugging of the different steps involved in the program and can see the list of nodes being created correctly.
However, the printf statement doesn't seem to do anything if I'm stepping through the code. If I don't step through and just run the code through I get:
Cannot access memory at address 0x2e666564
Cannot access memory at address 0x2e666564
Cannot access memory at address 0x2e666564
My source code is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Node {
char *string;
struct Node *next;
} List;
List *addNode(List *currentList, char *character)
{
List *tempList = calloc(1, sizeof(List));
tempList->string = strdup(character);
tempList->next = currentList;
return tempList;
}
void printList(List *currentList)
{
while (currentList != NULL)
{
printf("%s", currentList->string);
currentList = currentList->next;
}
}
int main ()
{
char currentCharacter;
char *currentString;
List *mainList = NULL;
do
{
currentCharacter = getchar();
if (currentCharacter == '\n')
{
mainList = addNode(mainList, currentString);
currentString[0] = '\0';
} else {
strcat(currentString, &currentCharacter);
}
} while (currentCharacter != '.');
mainList = addNode(mainList, currentString);
printList(mainList);
return 0;
}
You're calling strcat on an invalid pointer.
Something like this would work:
char currentString[128];
currentString[0] = '\0';
currentCharacter isn't null-terminated and so strcat won't work. Use strncat instead.
Several problems here.
The main problem is that you have not allocated space for your currentString. strcat requires that there be space in the destination array (currentString).
Also problematic: when the user enters '\n', you haven't null terminated the string you are appending, so strdup will not quite work.
Your main function never initializes currentString to point to allocated memory, so your call to strcat just starts adding characters to whatever '\0'-terminated string currentString happens to be pointing to.
You don't need strcat. You do need to assign currentString at some point. Replace:
#define BUFFER_SIZE 128
/*
shouldn't need more than 128 characters for a line of console
but you can expand it later if you want
*/
int main() {
List *mainList = NULL;
char *buffer = malloc(sizeof(char)*BUFFER_SIZE);
int currentIndex = 0;
char currentCharacter;
do {
currentCharacter = getChar();
if (currentCharacter == '\n' || currentIndex == (BUFFER_SIZE-1))
buffer[currentIndex] = 0; //the null byte needs to be added before strdup is called
mainList = addNode(mainList, buffer);
currentIndex = 0;
} else {
buffer[currentIndex++] = currentCharacter;
}
} while (currentCharacter != '.');
mainList = addNode(mainList, buffer);
return 0;
}

Desperately seeking the answer to my pointer problem

I have been working on a college C assignment, and have been trying to make sense of a bug I seem to be having with my code. Basically, it seems like something is wrong with my pointers (and or memory allocation).
This assignment is primarily about linked lists, so the structs contain pointers to the next element in the list. Obviously, to traverse the list until I find that the current element has a NULL pointer to the next element (and then I change that to be the pointer to the 'new' element I want to add.
The problem I have though, is for some reason my code seems to be completely screwing with my memory pointers, because they're getting garbled somehow. They seem fine for moment but very soon go rubbish.
Here is what my watches in XCode's debugger are showing me:
The first circle shows me the values as the first element in the list, which as far as I can tell are initially set correctly, and should be "C0001\0". The second circle shows the current->nextCategory pointer which should be NULL (0x0) but instead show that weird memory address (look at the size of it!). I assume that these problems are related, but as I am new to C, I don't know how or why.
In either case, when I check current->nextCategory != NULL in my while statement, it throws EXC_BAD_ACCESS:
I've spent the past few hours pulling my hair out becasue I cannot work out what the heck is happening to my program. Am I doing something wrong with my pointers, or using malloc() improperly?
Here is the relevant part of my program:
/****************************************************************************
* Initialises the system to a safe empty state.
****************************************************************************/
void systemInit(GJCType* menu)
{
if (menu == NULL) {
fprintf(stderr, "can't initialize system with a null menu pointer.\n");
exit(EXIT_FAILURE);
}
menu->headCategory = NULL;
menu->numCategories = 0;
}
/****************************************************************************
* Loads all data into the system.
****************************************************************************/
int loadData(GJCType* menu, char* menuFile, char* submenuFile)
{
FILE *fp;
size_t len;
char *line;
char *buffer;
CategoryTypePtr category_p;
ItemTypePtr item_p;
char *catId;
if (menu == NULL)
return FALSE;
fp = fopen(menuFile, "r");
if(fp == NULL) {
fprintf(stderr, "can't open %s\n", menuFile);
return FALSE;
}
buffer = malloc(MAX_BUFFER_SIZE);
len = MAX_BUFFER_SIZE;
catId = malloc(ID_LEN + 1);
while((buffer = fgetln(fp, &len))) {
line = strtok(buffer, "\n\0");
category_p = malloc(sizeof(CategoryTypePtr));
if (!tokenizeCategory(line, category_p)) {
fprintf(stderr, "can't tokenize category:> %s\n", line);
free(category_p);
category_p = NULL;
free(buffer);
free(catId);
return FALSE;
}
pushCategory(menu, category_p);
free(category_p);
category_p = NULL;
}
fp = fopen(submenuFile, "r");
if(fp == NULL) {
fprintf(stderr, "can't open %s\n", submenuFile);
return FALSE;
}
while((buffer = fgetln(fp, &len))) {
line = strtok(buffer, "\n\0");
item_p = malloc(sizeof(ItemTypePtr));
if (!tokenizeItem(line, item_p, catId)) {
fprintf(stderr, "can't tokenize item:> %s\n", line);
free(item_p);
item_p = NULL;
free(buffer);
free(catId);
return FALSE;
}
category_p = findCategory(menu, catId);
pushItem(category_p, item_p);
free(item_p);
item_p = NULL;
}
free(buffer);
free(catId);
return TRUE;
}
void pushItem(CategoryTypePtr category, ItemTypePtr item)
{
ItemTypePtr current;
ItemTypePtr new;
if ((new = malloc(sizeof(ItemTypePtr))) == NULL) {
fprintf(stderr, "can't malloc enough memory for new item pointer.\n");
exit(EXIT_FAILURE);
}
*new = *item;
if (category->headItem == NULL) {
category->headItem = new;
} else {
current = category->headItem;
while (current->nextItem != NULL) {
current = current->nextItem;
}
current->nextItem = new;
}
category->numItems++;
}
void pushCategory(GJCType* menu, CategoryTypePtr category)
{
CategoryTypePtr current;
CategoryTypePtr new;
if ((new = malloc(sizeof(CategoryTypePtr))) == NULL) {
fprintf(stderr, "can't malloc enough memory for new category pointer.\n");
exit(EXIT_FAILURE);
}
*new = *category;
if (menu->headCategory == NULL) {
menu->headCategory = new;
} else {
current = menu->headCategory;
while (current->nextCategory != NULL) {
current = current->nextCategory;
}
current->nextCategory = new;
}
menu->numCategories++;
}
CategoryTypePtr findCategory(GJCType* menu, char* id)
{
CategoryTypePtr current;
current = menu->headCategory;
while (current != NULL) {
if (!strcmp(current->categoryID, id))
return current;
current = current->nextCategory;
}
return NULL;
}
/* This function takes the character delimited string and converts it into
* a category structure at the location of the category pointer supplied.
*/
int tokenizeCategory(char *data, CategoryTypePtr category)
{
char* buffer;
if (category == NULL || strlen(data) < 1)
return FALSE;
buffer = malloc(MAX_BUFFER_SIZE);
strcpy(buffer, data);
strcpy(category->categoryID, strtok(buffer, "|\n"));
category->drinkType = *(strtok(NULL, "|\n"));
strcpy(category->categoryName, strtok(NULL, "|\n"));
strcpy(category->categoryDescription, strtok(NULL, "|\n"));
category->numItems = 0;
category->nextCategory = NULL;
category->headItem = NULL;
free(buffer);
return TRUE;
}
/* This function takes the character delimited string and converts it into
* an item structure at the location of the item pointer supplied.
*/
int tokenizeItem(char *data, ItemTypePtr item, char* categoryId)
{
char* buffer;
int i;
if (item == NULL || strlen(data) < 1)
return FALSE;
buffer = malloc(MAX_BUFFER_SIZE);
strcpy(buffer, data);
strcpy(item->itemID, strtok(buffer, "|\n"));
strcpy(categoryId, strtok(NULL, "|\n"));
strcat(categoryId, "\0");
strcpy(item->itemName, strtok(NULL, "|\n"));
for (i = 0; i < NUM_PRICES; i++)
sscanf(strtok(NULL, "|\n"),"%d.%d",&(item->prices[i].dollars),&(item->prices[i].cents));
strcpy(item->itemDescription, strtok(NULL, "|\n"));
item->nextItem = NULL;
free(buffer);
return TRUE;
}
Header definitions:
/* System-wide constants. */
#define ID_LEN 5
#define MIN_NAME_LEN 1
#define MAX_NAME_LEN 25
#define MIN_DESC_LEN 1
#define MAX_DESC_LEN 250
#define NUM_PRICES 3
#define HOT 'H'
#define COLD 'C'
#define FALSE 0
#define TRUE 1
#define MAX_BUFFER_SIZE 1024
typedef struct category* CategoryTypePtr;
typedef struct item* ItemTypePtr;
/* Structure definitions. */
typedef struct price
{
unsigned dollars;
unsigned cents;
} PriceType;
typedef struct item
{
char itemID[ID_LEN + 1];
char itemName[MAX_NAME_LEN + 1];
PriceType prices[NUM_PRICES];
char itemDescription[MAX_DESC_LEN + 1];
ItemTypePtr nextItem;
} ItemType;
typedef struct category
{
char categoryID[ID_LEN + 1];
char categoryName[MAX_NAME_LEN + 1];
char drinkType; /* (H)ot or (C)old. */
char categoryDescription[MAX_DESC_LEN + 1];
CategoryTypePtr nextCategory;
ItemTypePtr headItem;
unsigned numItems;
} CategoryType;
typedef struct gjc
{
CategoryTypePtr headCategory;
unsigned numCategories;
} GJCType;
It looks to me like you're not allocating memory properly.
category_p = malloc(sizeof(CategoryTypePtr));
This only allocates enough memory to store a single address, not an entire category structure. Try something like:
category_p = malloc(sizeof(CategoryType));
The problem is these lines:
category_p = malloc(sizeof(CategoryTypePtr));
item_p = malloc(sizeof(ItemTypePtr));
These lines, as written, only allocate enough memory to store a pointer, not the structure you want to point to.
Try:
category_p = malloc(sizeof(CategoryType));
item_p = malloc(sizeof(ItemType));
Also, your push functions are overly complicated. There's no need to copy the list nodes before you add them to the list. Just assign the address in the pointer to the new node to the ->next... pointer in the current tail:
void pushCategory(GJCType* menu, CategoryTypePtr category)
{
CategoryTypePtr current;
// no need to allocate space just for a pointer
if (menu->headCategory == NULL) {
menu->headCategory = category;
} else {
current = menu->headCategory;
while (current->nextCategory != NULL) {
current = current->nextCategory;
}
current->nextCategory = category;
}
menu->numCategories++;
}
Then you don't want the free(item_p) and free(category_p) calls in the main routine because the memory you've allocated is now being referenced by the list. You will need to free this memory when you dispose of the list.
To debug this kind of issues, I can only suggest to use valgrind : it will provide you very valuable help on buffer overflow, out of bound writing, memory loss. It's installed in the developper package.
You have multiple issues. Besides incorrectly allocating memory you are doing
*new = *category;
in your pushCategory function expecting to automagically copy the internal contents of the category structure: that simply doesn't work. You'll need to allocate a new CategoryType object and then copy each individual element appropriately. Something like this:
void pushCategory(GJCType* menu, CategoryTypePtr category)
{
CategoryTypePtr newCategory;
CategoryTypePtr current;
if ((newCategory = malloc(sizeof(CategoryType))) == NULL) {
fprintf(stderr, "can't malloc enough memory for new category pointer.\n");
exit(EXIT_FAILURE);
}
// copy individual elements here and set values properly
newCategory->headItem = NULL;
strncpy(newCategory->categoryID, category->categoryID, ID_LEN);
// copy other strings and NULL-initialize other pointers
if (menu->headCategory == NULL) {
menu->headCategory = new;
} else {
current = menu->headCategory;
while (current->nextCategory != NULL) {
current = current->nextCategory;
}
current->nextCategory = newCategory;
}
menu->numCategories++;
}
You will need to do the same thing for pushItem.
In the following code
category_p = malloc(sizeof(CategoryTypePtr));
.
.
.
pushCategory(menu, category_p);
free(category_p);
category_p = NULL;
and
item_p = malloc(sizeof(ItemTypePtr));
.
.
.
pushItem(category_p, item_p);
free(item_p);
item_p = NULL;
You have allocated the memory first, linked it in the list, and that very address you have deallocated. I think this is the problem.
And also you have done:
item_p = malloc(sizeof(ItemTypePtr));
and
category_p = malloc(sizeof(CategoryTypePtr));
the ItemTypePtr and CategoryTypePtr are pointers, so what you allocate is only the size of the pointer, and the memory cannot accomodate the entire data of the structure. You need to do
item_p = malloc(sizeof(ItemType));
category_p = malloc(sizeof(CategoryType));
Also in other locations you need to change the ItemTypePtr to ItemType as needed. I will tell not to typedef the pointers as you have done. This can bring difficulty in reading the code. If you have complex function pointer expressions then typedef ing it okay; in my opinion.

Resources