I want to change the value in a linked list. The list is defined as
struct car_elements {
char *car_rego;
double time_parked;
struct car_elements *next;
};
typedef struct car_elements car;
I have created a linked list that has 10 elements. I can change the time_parked easily but having trouble updating char *car_rego. To create the string
char *rego_array = malloc(7*sizeof(char));
CreateCarRego(rego_array);
The definition for CreateCarRego is
void *CreateCarRego(char *rego_array)
{
int temp = 0;
for (int w = 0; w < 6; w++)
{
if (w < 3)
{
temp = GenerateRandomNumber(26.0);
temp = temp + ASC_TO_LETTER;
rego_array[w] = (char)temp;
}
else
{
temp = GenerateRandomNumber(10.0);
temp = temp + ASC_TO_NUMBER;
rego_array[w] = (char)temp;
}
rego_array[7] = '\0';
}
}
Then to change the value
car *current_carpark = head; //head holds the linked list
for (int i = 1; i < carspace_number; i++) { //I know which element I want to change
current_carpark = current_carpark->next;
}
current_carpark->car_rego = (char*)calloc(strlen(rego_array), sizeof(char));
strncpy(current_carpark->car_rego, rego_array, strlen(rego_array));
This works but now I have a memory leak - I cannot free current_carpark. With the struct definition I have what is the best way to create a string (which is just 3 random letters and 3 random number) and update car_rego. I cannot just use
current_carpark->car_rego = rego_array;
As nothing is stored. I am new to C and yes this is an assignment however I have tried and tried to get this to work with no success
Code has out of bounds char * issues.
In CreateCarRego(), change
rego_array[7] = '\0';
to
rego_array[6] = '\0';
It is good to put the NUL terminator on, but it is done in the wrong place. rego_array is 7 bytes long and the 7th byte is accessed via rego_array[6].
Change
current_carpark->car_rego = (char*)calloc(strlen(rego_array), sizeof(char));
strncpy(current_carpark->car_rego, rego_array, strlen(rego_array));
to
current_carpark->car_rego = malloc(strlen(rego_array) + 1);
strcpy(current_carpark->car_rego, rego_array);
Not enough memory is allocated using only the length of the string, one needs the length of the string + 1. The following strncpy() limit is too tight, it should also be strlen(rego_array) + 1. As it is known that the destniation is long enough, strcpy() could be used.
or
Simplify the string duplication with
current_carpark->car_rego = strdup(rego_array);
A number of improvements could also be mentioned, but the above should get rid of memeory problems.
Related
Here is the struct that I am using.
#define MAX_CAR_LEN 12 ///< length does not include the trailing NUL byte
/// Racer_S structure represents a racer's row, position and display graphic.
typedef struct Racer_S {
int row; ///< vertical row or "racing lane" of a racer
int distance; ///< column of rear of car, marking its position in race
char *graphic; ///< graphic is the drawable text of the racer figure
} Racer;
When I call this function everything works fine inside it and creates everything correctly. I am able to access the row and distance fine. When I try and print the graphic I am printed an empty row in my terminal. I believe that this might be because "graphic" in the struct is a char* but I assign it a fixed sized array of char. When this function is called and passed in the name "Tom", graphic is supposed to be "~O=Tom----o>". I am new to C what am I doing wrong?
Racer * make_racer( char *name, int row ){
//Creating a newRacer instance
Racer *newRacer = malloc(sizeof(*newRacer));
newRacer->graphic = (char*)malloc(MAX_CAR_LEN);
char car[MAX_CAR_LEN] = "~O="; //"~O=-------o>"
char *pCar;
int length = strlen(name) + 2;
//Add the name after the engine of the car
for (int i = 0; i < length; ++i)
car[i+3] = name[i];
//Calculate the amount of dashes needed
int printDashes = 7 - strlen(name);
//add the extra dashes
for (int j = 1; j <= printDashes; ++j)
car[length + j] = '-';
// creates the end of the car
car[MAX_CAR_LEN-2] = 'o';
car[MAX_CAR_LEN-1] = '>';
pCar = strdup(car);
newRacer->row = row;
newRacer->distance = 0;
newRacer->graphic = &car[0];
// printf("%s\n", car);
return newRacer;
}
This is the code I am running in my main to test it
Racer *t = make_racer("Tom", 4);
printf("%s\n", t->graphic);
You have mentioned the below statement in your question.
This is the code I am running in my main to test it
acer *t = make_racer("Tom", 4);
printf("%s\n", t->graphic);
In make_racer function you have used a local charecter array variable called car and assigned the address to newRacer->graphic . This variable(char car[MAX_CAR_LEN+1] ;) memory goes out of scope after returning from the function.
Please refer this thread to understand more about the local scope in C.
Now to resolve your problem, In make_racer function you have to dynamically allocate memory for newRacer->graphic as well.
Racer * make_racer( char *name, int row ){
:
Racer *newRacer = malloc(sizeof(*newRacer));
newRacer->graphic = (char*)malloc(MAX_CAR_LEN+1);
:
//newRacer->graphic = &car[0]; # this is wrong.
strcpy(newRacer->graphic,car); //copy the content to allocated memory.
/*
* Warning:Just i am pointing out the issue here.
* strcpy may cause buffer over run.
* You have to use snprintf/strncpy family to prevent buffer overrun.
*/
}
Make sure you free the memory in your main.
int main() {
:
free(newRacer->graphic); //before free the momory for "newRacer"
free(newRacer);
}
I have a major issue that is happening to my code, that I've been trying to fix for hours now.
The code below is the one relevant to the issue that I am having...
The method addBucket:
void addBucket(SPACE * hashmap,char * tempvalue, char * tempkey){
printf("BEGINNING OF FUNC...\n");
void *prevadd = hashmap[0];
char *value = varString(tempvalue);
char *key = varString(tempkey);
void *aftadd = hashmap[0];
printf("BUCKET %s - %s\n",value,key);
BUCKET *newBucket = malloc(sizeof(BUCKET *));
fillBucket(value,key,newBucket);
int hash = hashFunc(key);
printf("FILL, FULFILLED\n");
if(!hashmap[hash]){
hashmap[hash] = malloc(sizeof(BASE*));
hashmap[hash]->first = NULL;
}
ITEM *location;
location = hashmap[hash]->first;
//This creates a new item in the list, if there isn't any.
//It does this by initialising the base, called box.
if(!location){
hashmap[hash]->first = (ITEM *) calloc(1,sizeof(ITEM *));
hashmap[hash]->first->next = NULL;
hashmap[hash]->first->prev = NULL;
hashmap[hash]->first->data = newBucket;
}
//This instead adds a new item to the list.
else{
//This loop reaches the last ITEM in the linked list itself
while(location->next){
location = location->next;
}
//This initialises the newItem that will be added
ITEM *newItem = (ITEM *) calloc(1,sizeof(ITEM));
newItem->next = NULL;
newItem->data = newBucket;
newItem->prev = location;
location->next = newItem;
}
}
The declared structs that are used:
//Declares a struct called BUCKET.
//Serves as the bucket of the hash table.
typedef struct bucket{
char * value; //The value inputted.
char * key; //The key to be hashed.
}BUCKET;
//Declares a struct called ITEM.
//Holds the bucket, as well as the address to the next bucket.
//It also holds the address to the previous bucket.
typedef struct item{
struct bucket * data;
struct item * next;
struct item * prev;
}ITEM;
//Declares a struct called BASE.
//Serves as the base node for the linked lists.
//The amount of initialised linked lists is the same as the amount of bases.
typedef struct base{
struct item * first;
}BASE;
//Declares a struct of an array of BASES, meaning linked lists.
//Essentially defines the size of the hashspace.
typedef BASE *SPACE;
...And the method expandHashspace(); :
//Makes the size of the entire hashspace larger.
//Only takes a value larger than the current size due to possible data loss.
SPACE* expandHashspace(SPACE *hashmap, int newSize){
if(newSize>100 || newSize<hashSpaceSize){
printf("Exiting...\n");
return NULL;
}
else {
SPACE *nw = NULL;
nw = realloc(hashmap, sizeof(SPACE *) * newSize);
hashmap = nw;
hashSpaceSize = newSize;
return hashmap;
}
}
Here's also the initHashmap() method:
SPACE* hashmapInit(SPACE *hashmap){
hashmap = calloc(5, sizeof(SPACE *));
hashSpaceSize = 5;
return hashmap;
}
What I am doing here is initialising the hashmap, adding three buckets, expanding the hashmap, then adding three more buckets. Here's the order in more simple terms:
initHashmap();
addBucket(...); x3
expandHashmap();
addBucket(...); x3
However, on that last part, as soon as I run addBucket once, I get a SIGSEGV error. Checking through debugging, I realised something that was off.
Do you see the variables *prevadd and *aftadd? I added them while debugging to see what was happening to the address of hashmap[0]. Here is a picture of my results:
As you can see there, the address of hashmap[0] varied wildly during those two char * lines. Specifically, the change of address happens on the char *value line.
Please go easy on me, as I've just started learning C 3 months ago, and I am still incredibly unaccustomed to memory allocation. If the error is obvious, please point it out, and if I have some problem with the way that I am allocating memory, or freeing it, I am more than happy to hear them (my code has a pretty major heisenbug that I cannot fix for the life of me, but that's beside the point).
Thank you in advance... Sorry for all the recent questions.
update : forgot to add varString();...
char* varString(const char *origString){
size_t i;
for(i = 0;origString[(int)i]!='\0';i++){}
if(origString[i-1]=='\n') i-=2;
char *newString = malloc(i);
for(int j = 0; j <= i; j++){
newString[j] = origString[j];
}
newString[i+1] = '\0';
return newString;
}
This is not an answer, but it needed more formatting than would fit in a comment:
Note that you are writing "Value No. 1"
Note the value of aftadd is 0x756c6156
In memory, assuming a little-endian machine, the layout of the number in aftadd would be:
0x56 0x61 0x6c 0x75
In ASCII these would be:
'V' 'a' 'l' 'u'
Hint hint.
So... in the past I've been told that my questions aren't good... I believe mostly because I haven't isolated out problematic code well enough. I'll do my best to ask a pointed, concise, and to the point question in this post. I'm certainly open to suggestions about how my question could be asked better.
Thanks.
I'm working on a small project in C that will serve as a prototype for a larger, buggy program that I've been working on for some time. I'm trying to work out the details in a smaller program first. I have two structs:
struct list
{
char ownerName[20];
int ownerAge;
char sex;
}owner;
and
struct list2
{
char petName[20];
char owner[20];
char animal[4];
char breed[50];
char color[20];
}pets;
The program is supposed to fgets ownerName from user input and compare it to ".owner" in the pets struct. The ownerName and petName elements should then be copied into an array, and the name of the owner and his/her pets will be printed in a list. While I'm aware I don't need the owner struct to accomplish this, I'm using it to model the other program I'm writing.
I'm using
if (strcmp(pets[i].owner, name) == 0)
to compare the struct elements and seem to have this part down.
The variable j counts the number of records that meet this criteria, and the variable l = j + 1. I call the array using:
char *petsList[l];
The size of the array is dictated by l (j + 1) because I need j elements for the petNames + 1 element for the owner name.
I've also created a pointer to the petsList array via the following:
char *(*ptr)[l] = &petsList
The owner name is added to the array via the following command:
(*ptr)[0] = (char *)malloc(sizeof(name));
strcpy ( (*ptr)[0], name);
The petNames are added to the the array petsList using a for loop. I've initialized i = 1 to prevent petsList[0] from being overwritten and am trying to write petNames to the array via the following loop:
i = 1;
for (k=0; k < PETS; k++)
{
if (strcmp(pets[k].owner, name) == 0)
{
(*ptr)[i] = (char *)malloc(sizeof(pets[k].petName));
if (!*(ptr)[i])
{
puts("\nMemory Allocation Error");
exit (1);
}
strcpy( (*ptr)[i], pets[k].petName);
i++;
}
}
Let's say for a given input of name, I get three pets that match. The loop iterates the first two times just fine, but then on the third iteration of the loop, I get a memory allocation error. This happens on the last iteration of the loop consistently. For example, if I have 2 pets associated with the ownerName, the list will run the first iteration fine and fail on the second; if I have 4 pets associated with the ownerName, the loop will run fine the first 3 times and fail on the fourth, so it appears that the final iteration of the loop consistently fails. I've tried changing the code a number of times, but am now at a loss for how I can move forward with this program. Any help is greatly appreciated.
Thanks.
Don`t cast the malloc return value.
Since I can`t really check with a minimal example.
char *(*ptr)[l] = &petsList
why do you make such a complex construct? I am not even sure what it is supposed to accomplish. do you want all pets in the first index and the next index to contain the owner? this could be accomplished just with the petslist
what structure do you really need at the end?
is it something like:
array:
0 = owner
1 = pet 1
2 = pet 2
or something like
0,0 = owner 1,0 = owner 2 etc.
0,1 = pet 1 1,1 = pet 3
0,2 = pet 2 1,2 = pet 4
Ok here is a working example of what you want to do. you can easily extend it to do the second data arrangement. If you have any questions feel free to ask
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PETAMOUNT 40
struct list2
{
char petName[20];
char owner[20];
char animal[4];
char breed[50];
char color[20];
};
int main() {
struct list2 *pets; // list of all pets
char name[128]; // contain name of the owner, get from stdin
unsigned int i; // i and j are both counter variable
unsigned int j;
fgets(name, 128, stdin); // get string from stdin
name[strlen(name) - 1] = '\0'; // remove newline
pets = malloc(PETAMOUNT * sizeof(struct list2)); // allocate memory for the list of all pets
if (pets == NULL) {
printf("malloc err\n");
exit(1);
}
for (i = 0; i < PETAMOUNT; i++) { // initialize some pets and some owners
strcpy(pets[i].petName, "petname ");
strcpy(pets[i].owner, "owner ");
pets[i].petName[7] = i + '0'; // there are PETAMOUNT of petnames. petname0, petname1 etc
pets[i].owner[5] = (i / 4) + '0'; // there are PETAMOUNT / 4 owners. owner0 has petname0 to petname3, owner1 has petname4 to 7 etc
}
char ***petslist; // a list of list of strings or 3d char array
petslist = malloc(sizeof(char **)); // allocate pointer to contain a double array
petslist[0] = malloc(sizeof(char *)); // allocate a pointer to contain the name of the owner
if (petslist[0] == NULL) {
printf("malloc err\n");
exit(1);
}
petslist[0][0] = malloc(strlen(name) + 1); // allocate memory to contain the owner
if (petslist[0][0] == NULL) {
printf("malloc err\n");
exit(1);
}
strcpy(petslist[0][0], name); // copy owner into the first index
for (i = 0, j = 1; i < PETAMOUNT; i++) { // go through all pets
if (strcmp(pets[i].owner, name) == 0) { // if the owner of the current pet is the same as the inputted owner
petslist[0] = realloc(petslist[0], (j + 1) * sizeof(char *)); // allocate pointer for the next pet
petslist[0][j] = malloc(strlen(pets[i].petName) + 1); // allocate memory to contain the chars of the pet
if (petslist[0][j] == NULL) {
printf("malloc err\n");
exit(1);
}
strcpy(petslist[0][j], pets[i].petName); // copy the petname into the array
j++;
}
}
puts("petslist:"); // print it all out
for (i = 0; i < j; i++) {
printf("|%s|\n", petslist[0][i]);
}
exit(0);
}
currently I always write to the [0][0] but if you realloc you can make room for more after that
This is a little weird. Maybe:
if(!*(ptr)[i])
should be
if(!(*ptr)[i])
?
I am new to C but I am currently working on a C program which I am having an issue with relating to structures and memory allocation.
What I have in my code is a permanent while loop which will break out of when a certain condition is met. Within this while loop it checks the database and has a while loop to loop round the MySQL rows.
When the program first loads, it creates 3 structures, one of the structures also containing a linked list and initialised to be a size of 100.
As my program loops through the MySQL rows it checks whether 100 records have been added, and if so, then does a realloc to increase the size by another 100 rows. When I have looped through all of the MySQL rows it then returns to main while loop (the never ending loop) and NULL's the structures and then initialises them again to be 100 rows and starts all over again.
The problem I am having, is once it does the first complete loop through all of the mysql rows and returns to the main never ending loop and the structures are reinitialised back to be a size of 100, on the 17th row my program segfaults. When I inspect everything in GDB the structure at index 17 comes up saying that the certain elements within the structure are out of bounds.
Below is the definition for the structure I am having the issue with.
typedef struct CallLogSearchDataStruct
{
char * date;
char * time;
char * bParty;
char * aParty;
float duration;
char * cleardownCause;
struct CallLogSearchOutboundStruct * outboundLegs;
} callLogSearchDataStruct;
Below is how the structure initially get set up when the program first runs
callLogSearchData = calloc(INITIAL_CALL_STRUCT_SIZE,sizeof(callLogSearchDataStruct));
callLogSearch = calloc(INITIAL_CALL_STRUCT_SIZE,sizeof(callLogSearchResultStruct));
switches = calloc(INITIAL_CALL_STRUCT_SIZE, sizeof(switchIDStructure));
INITIAL_CALL_STRUCT_SIZE is equal to 100.
Below is the code for how the reallocateStructures function is called. This reallocates the structures by adding another 100 in size to the original size.
if (reallocateStructures(&callLogSearch, &callLogSearchData, &switches, ×StructHasBeenReallocated, currentStructIndexValue, dataRow) == 0)
{
//Structures have been reallocated so reset the index
currentStructIndexValue = 0;
}
Below is the actual code for the reallocating of the structures
int reallocateStructures(callLogSearchResultStruct **callLogSearch, callLogSearchDataStruct ** callLogSearchData,
switchIDStructure ** switches, int *timesStructHasBeenReallocated, int currentStructIndexValue,
int dataRow)
{
int INITIAL_CALL_STRUCT_SIZE = 100;
int currentSize = 0;
int newSize = 0;
int initFromIndex = 0;
callLogSearchResultStruct * callLogSearchTemp;
callLogSearchDataStruct * callLogSearchDataTemp;
switchIDStructure * switchesTemp;
printf("Current Struct Index Value: %i\n", currentStructIndexValue);
if (currentStructIndexValue >= INITIAL_CALL_STRUCT_SIZE) {
printf("REALLOCATING STRUCTURES");
currentSize = currentStructIndexValue * *timesStructHasBeenReallocated;
newSize = currentSize + INITIAL_CALL_STRUCT_SIZE;
*timesStructHasBeenReallocated = *timesStructHasBeenReallocated + 1;
callLogSearchTemp= (callLogSearchResultStruct*)realloc(*callLogSearch, (newSize * sizeof(callLogSearchResultStruct)));
callLogSearchDataTemp = (callLogSearchDataStruct*)realloc(*callLogSearchData, (newSize * sizeof(callLogSearchDataStruct)));
switchesTemp = (switchIDStructure*)realloc(*switches, (newSize * sizeof(switchIDStructure)));
/**callLogSearchData = realloc(*callLogSearchData, newSize * sizeof (callLogSearchDataStruct));
*callLogSearch = realloc(*callLogSearch, newSize * sizeof (callLogSearchResultStruct));
*switches = realloc(*switches, newSize * sizeof (switchIDStructure));
*/
for (initFromIndex = currentSize; initFromIndex < newSize; initFromIndex++) {
callLogSearchDataTemp[initFromIndex].aParty = NULL;
callLogSearchDataTemp[initFromIndex].bParty = NULL;
callLogSearchDataTemp[initFromIndex].cleardownCause = NULL;
callLogSearchDataTemp[initFromIndex].date = NULL;
callLogSearchDataTemp[initFromIndex].duration = 0;
callLogSearchDataTemp[initFromIndex].outboundLegs = NULL;
callLogSearchDataTemp[initFromIndex].time = NULL;
callLogSearchTemp[initFromIndex].date = NULL;
callLogSearchTemp[initFromIndex].dRowIndex = dataRow;
switchesTemp[initFromIndex].switchID = NULL;
if (initFromIndex == newSize - 1)
{
printf("debugging here\n");
}
}
*callLogSearch = callLogSearchTemp;
*callLogSearchData = callLogSearchDataTemp;
*switches = switchesTemp;
return 0;
}
else
{
return 1;
}
}
Below is the code where after it has looped through all of the MySQL rows the structures are then reset and re-initialised to be a size of 100.
//Check if function has looped round already and if so reset all structures
if (dataRow > -1)
{
numberOfTimesEverythingHasReset++;
callLogSearchData = NULL;
callLogSearch = NULL;
switches = NULL;
callLogSearchData = realloc(callLogSearchData, INITIAL_CALL_STRUCT_SIZE*sizeof(callLogSearchDataStruct));
callLogSearch = realloc(callLogSearch, INITIAL_CALL_STRUCT_SIZE*sizeof(callLogSearchResultStruct));
switches = realloc(switches, INITIAL_CALL_STRUCT_SIZE*sizeof(switchIDStructure));
//initialiseNewStructure(&callLogSearch, &callLogSearchData, &switches);
//callLogSearchData = (callLogSearchDataStruct*)realloc(callLogSearchData, INITIAL_CALL_STRUCT_SIZE*sizeof(callLogSearchDataStruct));
//callLogSearch = (callLogSearchResultStruct*)realloc(callLogSearch, INITIAL_CALL_STRUCT_SIZE*sizeof(callLogSearchResultStruct));
//switches = (switchIDStructure*)realloc(switches, INITIAL_CALL_STRUCT_SIZE*sizeof(switchIDStructure));
//Initialise all elements within structures
for (initFromIndex = INITIAL_CALL_STRUCT_SIZE; initFromIndex < INITIAL_CALL_STRUCT_SIZE; initFromIndex++)
{
callLogSearchData[initFromIndex].aParty = NULL;
callLogSearchData[initFromIndex].bParty = NULL;
callLogSearchData[initFromIndex].cleardownCause = NULL;
callLogSearchData[initFromIndex].date = NULL;
callLogSearchData[initFromIndex].duration = 0;
callLogSearchData[initFromIndex].outboundLegs = NULL;
callLogSearchData[initFromIndex].time = NULL;
callLogSearch[initFromIndex].date = NULL;
callLogSearch[initFromIndex].dRowIndex = dataRow;
}
timesStructHasBeenReallocated = 1;
currentSize = 0;
}
currentStructIndexValue = 0;
I'm not sure why when it enters the if statement the first time to reset the structures back to a size of 100, after the 17 MySQL row the memory is then out of bounds so I segfault.
Thanks for any help you can provide.
UPDATE
I have fixed the issue with the for loop as per #Rohan answer but I am now getting a slightly different issue.
I am now getting a different area when trying to add data to the structures but I am not sure if the structures are anything to do with it as it seems to be the MySQL array having an out of bounds issue now causing a seg fault.
The line where it is failing is
callLogSearchData[dataRow].bParty = strdup(rowReport[bPartyColIndex]);
When I inspect rowReport in GDB it seems to have garbage inside it and at the 7th index (bPartyColIndex is at index 9) I start to see the out bounds error.
rowReport is assigned at each time the loop for each mysql row as shown in the code below
sqlLen = asprintf(&sql, "SELECT Tmp.SwitchID, Tmp.CorrelationID, SeizeUTC as Date, "
"SeizeUTC as Time, Direction, ACMToAns/100 as ACMToAns, Duration/100 as Duration, "
"CleardownCause, AParty, Tmp.BParty FROM TMP_Log AS Tmp ORDER BY SeizeUTC, "
"Tmp.SwitchID, Tmp.CorrelationID, Direction, SeizeCSec LIMIT %i, %i",
index, count);
SL_DebugAll(DBG_INFO, sql);
if ((mysql_real_query(HandleDB, sql, sqlLen))) return 1;
resultReport = mysql_store_result(HandleDB);
if (mysql_num_rows(resultReport) == 0 || index > atoi(limit))
{
SL_DebugAll(DBG_INFO, "Data retrieval for call log complete");
break;
}
else
{
numRows = mysql_num_rows(resultReport);
swID = -1;
corrID = -1;
dataRow = -1;
if (numRows > 0)
{
maxTargets = 1;
}
audioRow = mysql_fetch_row(audioResult);
sspSwitchID = atoi(audioRow[switchIDColIndex]);
sspCorrID = atoi(audioRow[correlationIDColIndex]);
inbound_counter = 0;
while (rowReport = mysql_fetch_row(resultReport))
In last code section of yours you have
if (dataRow > -1)
{
numberOfTimesEverythingHasReset++;
callLogSearchData = NULL;
callLogSearch = NULL;
switches = NULL;
callLogSearchData = realloc(callLogSearchData,
INITIAL_CALL_STRUCT_SIZE*sizeof(callLogSearchDataStruct));
callLogSearch = realloc(callLogSearch,
INITIAL_CALL_STRUCT_SIZE*sizeof(callLogSearchResultStruct));
switches = realloc(switches,
INITIAL_CALL_STRUCT_SIZE*sizeof(switchIDStructure));
//Initialise all elements within structures
for (initFromIndex = INITIAL_CALL_STRUCT_SIZE;
initFromIndex < INITIAL_CALL_STRUCT_SIZE;
initFromIndex++)
{
....
You are setting callLogSearchData to NULL so reallocating is not relevant.
And you are allocating for INITIAL_CALL_STRUCT_SIZE number of elements, but your for loop counter starts from INITIAL_CALL_STRUCT_SIZE so effectively for loop does not execute.
I've managed to find out what the issue, its a pretty basic mistake unfortunately.
Because of where the code was crashing, I thought it had to be something related to the way I was re-initialised the structure, maybe because of the way I was using pointers.
However, this was not the case instead, instead, somewhere else in the code I was adding some data to one of the structures using an index called fileRow. Each time a value was added to the structure I incremented fileRow however I forgot to reset back to 0 when I realloced the structures, so when I realloced the structures to be of size 100, I was inserting into the structure of where fileRow was which was set to 1300 so each time I looped round I smashed up the memory until it segfaulted.
Thanks for your help and suggestions.
I'm trying to create the following array:
"Fruits", 25, {
{"Apple", 2},
{"Grapes", 13},
{"Melon", 10}
}
"Meats", 40, {
{"Beef", 9},
{"Chicken", 27},
{"Pork", 4}
}
...
Feels like there's a more elegant way of doing what I got so far. Any feedback/samples on how to create this structure more efficient given the input struct would be appreciated.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct Product {
char *name;
int qty;
} Prods;
typedef struct Category {
char *name;
int qty;
int prods_count;
Prods *prod;
} Cats;
typedef struct Inventory {
Cats *cat;
int cats_count;
} Inv;
struct tmp_input {
char name[12];
int qty;
char cat[12];
};
// return index if found
int in_array(Inv *inv, char *k) {
int i;
if (inv->cats_count == 0)
return -1;
for (i = 0; i < inv->cats_count; i++) {
if (strcmp (k, inv->cat[i].name) == 0) return i;
}
return -1;
}
int main () {
int i, j, exists = 0;
// temp struct.
struct tmp_input items[] = {
{"Apple", 2, "Fruit"}, {"Coke", 10, "Drink"}, {"Pork", 4, "Meat"},
{"Beef", 9, "Meat"}, {"Chicken", 27, "Meat"}, {"Melon", 10, "Fruit"},
{"Tea", 3, "Drink"}, {"Coffee", 20, "Drink"}, {"Grapes", 13, "Fruit"}
};
size_t len = sizeof (items) / sizeof (struct tmp_input);
Inv *inven = malloc(sizeof(Inv));
inven->cats_count = 0;
inven->cat = calloc(1, sizeof(Cats));
for (i = 0; i < len; i++) {
exists = in_array(inven, items[i].cat);
// category does not exist
if (exists == -1) {
inven->cat = realloc(inven->cat, sizeof(Cats) * (inven->cats_count + 1));
inven->cat[inven->cats_count].name = strdup(items[i].cat);
inven->cat[inven->cats_count].qty += items[i].qty;
inven->cat[inven->cats_count].prods_count = 1;
inven->cat[inven->cats_count].prod = calloc (1, sizeof (Prods));
inven->cat[inven->cats_count].prod->name = strdup (items[i].name);
inven->cat[inven->cats_count].prod->qty = items[i].qty;
inven->cats_count++;
}
// category found
else {
inven->cat[exists].qty += items[i].qty;
int size = inven->cat[exists].prods_count + 1;
inven->cat[exists].prod = realloc(inven->cat[exists].prod, sizeof(Prods) * (size));
inven->cat[exists].prod[size - 1].name = strdup (items[i].name);
inven->cat[exists].prod[size - 1].qty= items[i].qty;
inven->cat[exists].prods_count++;
}
}
for (i = 0; i < inven->cats_count; i++) {
printf("%3d %s\n", inven->cat[i].qty, inven->cat[i].name);
for (j = 0; j < inven->cat[i].prods_count; j++) {
printf("%3d %s\n", inven->cat[i].prod[j].qty, inven->cat[i].prod[j].name);
}
}
return 0;
}
You aren't allocating any memory for the Prod array.
Something like
...
if (exists == -1) {
inven->cat = realloc(inven->cat, sizeof(Cats) * (inven->cats_count + 1));
inven->cat[inven->cats_count].name = items[i].cat;
inven->cat[inven->cats_count].qty += items[i].qty;
// Allocate memory for 1 product
inven->cat[inven->cats_count].prods_count = 1;
inven->cat[inven->cats_count].prod = malloc (sizeof (Prods));
// Now allocate space and copy the name
inven->cat[inven->cats_count].prod->name = strdup (items[i].name + 1);
inven->cats_count++;
}
...
I will leave it to you to handle the case where there are more than 1 product in a category, where you'll need to reallocate the memory again.
Another error is that you need to allocate and copy the category name
inven->cat[inven->cats_count].name = items[i].cat;
should be replaced by
inven->cat[inven->cats_count].name = strdup (items[i].cat);
This is because the items array does not exist outside of this function, so if you just do
inven->cat[inven->cats_count].name = items[i].cat;
then after you leave this function, invent->cat[inven->cats_count].name will point to garbage memory.
A final suggestion would be to split each structure into a function that handles creation of it, just to clean up the code.
--- edit to add comments on Abstract Data Types
Arrays are useful if you have data that you know you will be accessing via indices. If you don't know the index of the item you want (as in this case), an array is less useful.
Unlike other comments, I don't think using a Linked List really gives you anything useful. Linked Lists are useful when you need to walk sequentially through all the items, without really caring where they are in the list. It seems to me that the most common use case for a system like you are creating is searching: Do we have any Fruit in stock? Add 10 cases of Coke to the inventory... those sorts of things.
Also, you only want a single entry for each category/product. You don't want 3 Fruit categories in the data. Both arrays and linked lists don't really have any restrictions on adding the same structure multiple times. This means that every time you'll need to check the whole list to see if you need to add the new structure.
For that reason, I'd certainly make both the Categories and products arrays into hashtables (or called a dictionary in some languages) that map name -> structure. This will speed up your search as you don't have to search the entire dataset every time and will prevent you from adding the same structure multiple times.
Wikipedia article on Hashtables: http://en.wikipedia.org/wiki/Hashtable
Here's an example of how to set up the structures dynamically (simpler than a linked list, but not as flexible).
typedef struct Product {
char *name;
int qty;
} Prods;
typedef struct Category {
char *name;
int qty;
int prods_count;
Prods *prod; // dynamic array of Products
} Cats;
The structures as they were.
struct Category categ[10];
An arbitrary number of categories, for now take categ[0] for 'Fruits'.
Next dynamically create an array of 10 product structures:
Prods *prod_array = malloc(sizeof(Prods) * 10); // i.e. prod_array[0] to [9]
Now just store the array in the category structure:
categ[0].prod = prod_array;
categ[0].prods_count = 10;
If you need to access the product name, it's just: categ[i].prod[j].name
Now, if you need another 10 products, you can just use realloc to increase the size of the array, and update its count.
Put all these things in functions, and the code isn't too complex.