Reputation: 35
I have a struct which holds 3 char pointers (arbitrary length strings) and I created a dynamic array with malloc since It can have an arbitrary amount of entries. This is my struct data type:
typedef struct student{
char* name;
char* phoneNumber;
char* department;
}STUDENT;
I used a function to realloc to increase the size of the struct array whenever a new entry is needed and another function to print the whole array.
The problem that i encounter is that: I am able to print the new entry inside the addNumber function but it is not always the case when I tried printing outside the addNumber function. Sometimes it works, most of the time I got a segmentation fault: 11 and one time while adding a new entry I got a 'malloc: *** error for object 0x7fc426c058f0: pointer being realloc'd was not allocated'
Here is inside of my main:
int nEntry = 0;
STUDENT* directory = malloc(nEntry * sizeof *directory);
if(directory == NULL){
puts("Unable to allocate memory");
exit(-1);
}
while(1){
int choice = 0;
scanf("%d", &choice);
while(getchar() != '\n');
switch(choice){
case 1:
printDirectory(directory, nEntry);
break;
case 2:
addNumber(directory, &nEntry);
break;
default:
printf("Unknown option!\n");
break;
}
}
return 0;
Here is my addNumber function:
void addNumber(STUDENT* Array, int* nArray){
*nArray += 1;
int x = *nArray - 1;
STUDENT* tempDirectory = realloc(Array, *nArray * sizeof *Array);
if(tempDirectory == NULL){
puts("Unable to allocate memory");
exit(-1);
}
else{
Array = tempDirectory;
Array[x].name = (char*)malloc(sizeof(char*));
Array[x].phoneNumber = (char*)malloc(sizeof(char*));
Array[x].department = (char*)malloc(sizeof(char*));
printf("Name: ");
scanf("%[^\n]", Array[x].name);
while(getchar() != '\n');
printf("Number: ");
scanf("%[^\n]", Array[x].phoneNumber);
while(getchar() != '\n');
printf("Department: ");
scanf("%[^\n]", Array[x].department);
while(getchar() != '\n');
for(int i = 0; i < *nArray; i++){
printf("%s\t%s\t(%s)\n", (Array + i)->name, (Array + i)->phoneNumber, (Array + i)->department);
}
}
}
and Here is my print function:
void printDirectory(STUDENT* Array, int nArray){
int i;
for(i = 0; i < nArray; i++){
printf("%s\t%s\t(%s)\n", (Array + i)->name, (Array + i)->phoneNumber, (Array + i)->department);
}
}
The print function works fine if I hardcode the entry into main, the problem seems to be that whats created in the addNumber function isn't properly passed back? But then for all the arguments i am passing by reference, I am confused why I'm getting an undefined behaviour.
Upvotes: 0
Views: 119
Reputation: 3596
As pointed out in comments, your malloc
s are the wrong size, so there are other problems with your code, but to address your specific concern about realloc:
To store the return value of realloc
so it can be accessed in your main function, you're going to need to pass the address of the pointer.
So in main:
addNumber(&directory, &nEntry); // Note the ampersand
And in addNumber:
void addNumber(STUDENT** Array, int* nArray){ // Note the pointer to a pointer
...
STUDENT* tempDirectory = realloc(*Array, *nArray * sizeof *Array); // Note Array is dereferenced
...
*Array = tempDirectory;
...
If you picture directory
in main as a variable stored at location 0x1 that points to memory location 0x2, in your current code you are sending 0x2 to the function. The function can read the data there, but it has no reference to 0x1 to update the directory
variable in main.
The general rule for passing by reference is: If you want to update the reference in a function so that the caller can read it, you need to pass it with one more asterix than the variable has in the caller. So if you have an int
in main, you would want to update an int*
in the function. If you have an int***
in main, you would want to update an int****
.
Upvotes: 2
Reputation: 311156
For starters this code snippet
int nEntry = 0;
STUDENT* directory = malloc(nEntry * sizeof *directory);
if(directory == NULL){
puts("Unable to allocate memory");
exit(-1);
}
does not make a sense. The behavior of a call of malloc with the argument equal to 0 is implementation defined. That is such a call can return either NULL or some valid pointer depending on the used system.
It will be enough to write
int nEntry = 0;
STUDENT* directory = NULL;
The function addNumber
deals with a copy of the value of the passed argument. So changing the copy within the function does not influence on the original argument.
You have to pass the original argument by reference. That is through pointer to pointer.
void addNumber( STUDENT **Array, int *nArray );
^^^^^^^
Also the similar parameter names like Array
and nArray
make function code unreadable.
The function could be declared the following way
int addNumber( STUDENT **directory, int *n )
{
STUDENT *tempDirectory = realloc( *directory, ( *n + 1 ) * sizeof **directory );
if ( !tempDirectory ) return 0;
++*n;
*directory = tempDirectory;
// and so on
return 1;
}
and the caller could check the return value of the function whether it ends with success or failure and issue a corresponding message if required.
These memory allocations
Array[x].name = (char*)malloc(sizeof(char*));
Array[x].phoneNumber = (char*)malloc(sizeof(char*));
Array[x].department = (char*)malloc(sizeof(char*));
also do not make a sense. You need to allocate character arrays where you are going to store inputted strings in calls like this
scanf("%[^\n]", Array[x].name);
You could declare an auxiliary character array like for example
char s[100];
and read strings in this arrays. For example
printf("Name: ");
fgets( s, sizeof( s ), stdin );
s[ strcspn( s, "\n" ) ] = '\0';
Then you could allocate a memory for the data member name
knowing the length of the read string like
( *Array )[x].name = malloc( strlen( s ) + 1 );
and copy the string
strcpy( ( *Array )[x].name, s );
Upvotes: 2