Reputation: 79
I have a question regarding passing arrays in c to a function.
When I run this program it gives me a segmentation fault
int main()
{
char **ptr= NULL;
test(ptr);
printf("%s", ptr[0]);
return 0;
}
void test(char **ptr)
{
ptr = (char **) malloc(sizeof(char *));
ptr[0] = (char *) malloc(sizeof(char)*5);
strcpy(ptr[0], "abc");
return;
}
but this worked just fine
int main()
{
char **ptr= NULL;
test(&ptr);
printf("%s", ptr[0]);
return 0;
}
void test(char ***ptr)
{
*ptr = (char **) malloc(sizeof(char *));
*ptr[0] = (char *) malloc(sizeof(char)*5);
strcpy(*ptr[0], "abc");
return;
}
Could someone explain why?
Upvotes: 0
Views: 353
Reputation: 3807
To expand on the answer from @rcrmn.
It is a good program design to have data get passed "up" the calling structure. In this case main()
needs to free the storage it received from test.
Notice that this code avoids all of the double pointers in the original question and still meets the requirements. This is the essence of good programming, write clear, simple, code that gets the job done.
#include <stdio.h>
char *test();
int main()
{
char *ptr = test();
printf("%s", ptr);
free(ptr);
return 0;
}
char *test()
{
char *ptr = (char *) malloc(sizeof(char) * 5);
strncpy(ptr, "abc", 3);
return ptr;
}
Upvotes: 0
Reputation: 7917
In C, when you pass an array to a function, the array "decays" to a pointer. The function receives a pointer to the first element of the array. It does not receive the array itself. Any changes made to the pointer assignment in the called function are not reflected in the calling function.
In your first example, test()
receives the array of strings, and inside the function, it changes what that pointer points to. So the local copy of ptr
, which was NULL
, gets allocated memory inside the function, as does ptr[0]
. However, those changes are local to test()
. They are not reflected in the calling function. When test()
finishes executing and returns, the value of ptr
in the calling function is still NULL
. And you have a memory leak because there is no way to access the memory that was allocated in test()
.
In order for the changes to be reflected in the calling function, you have to pass a pointer to the array of strings: hence the &ptr
in the call and the three-level pointer in the definition of test()
. Another, simpler approach would be:
int main()
{
char **ptr= NULL;
ptr = test();
printf("%s", ptr[0]);
return 0;
}
char** test(void)
{
char **ptr = (char **) malloc(sizeof(char *));
ptr[0] = (char *) malloc(sizeof(char) * 5);
strcpy(ptr[0], "abc");
return ptr;
}
One clarification: I said "Any changes made to the pointer assignment in the called function are not reflected in the calling function." This is not the same as saying "Any changes to array elements are not reflected in the calling function." Consider this:
int main (void) {
int array [] = { 0, 1, 2, 3, 4 };
test1 (array);
printf ("%d\n", *array);
test2 (array);
printf ("%d\n", *array);
return 0;
}
void test1 (int* array) {
int new_array[] = { 3, 4, 5, 6, 7 };
array = new_array;
return;
}
void test2 (int* array) {
array [0] = 5; // or *array = 5
array [1] = 6;
array [2] = 7;
array [3] = 8;
array [4] = 9;
return;
}
Output:
0
5
Here in test1()
the change is to where the array pointer itself is pointing, which won't be reflected in the caller. So in the calling function, *test
is still 0. In test2()
, the changes are to the array elements, which are reflected in the caller. Hence the difference in output: *test
is now 5. All this uses static rather than dynamic memory allocation, but the principle is the same.
Upvotes: 2
Reputation: 123468
You're passing the parameter ptr
by value; the formal parameter ptr
in test
is a different object from the actual parameter ptr
in main
, so changing the value of test::ptr
is not reflected in main::ptr
.
Thus, you need to pass a pointer to ptr
into test
, and test
needs to dereference that pointer to write to the correct object.
For any type T
, if you want to modify the value of the function argument, you need to do the following:
void foo( T *param )
{
*param = ...;
}
void bar( )
{
T obj;
foo( &obj );
}
In this particular instance, T
is char **
.
Upvotes: 3
Reputation: 2853
Consider below code for understandling
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void test(char **ptr);
int main()
{
char *ptr= NULL;
test(&ptr);
printf("%s\n", ptr);
return 0;
}
void test(char **ptr)
{
*ptr = malloc(sizeof(char *));
strcpy(*ptr, "abc");
return;
}
Whatever doing in function definition has to be reflect in main function. So if you want to make changes in ptr
variable you have to pass the address of the ptr
variable.
ptr
is single pointer(char ptr
) variable, we need to pass address of the pointer(&ptr
) variable.
In function definition we are receiving address of pointer variable, so argument has to be the type of double pointer.
This concept is similar to call by reference.
Similar things happening in your code also.
You need to pass address of double pointer to make changes in pointer variable.
Address of double pointer has to taken by triple pointer in function definition.
Upvotes: 0
Reputation: 521
It's because you expect your function not to return a value, but to modify a variable you have already created in another scope.
You expect, from your function, to pass ptr
and end up with the following structure:
ptr -> b -> []
where b
is a pointer to the array []
.
In the first example you are not modifying the outer ptr inside the function, but a copy of it. In that case, to get the desired structure you'd need your function to return a char** so that you could return the allocated pointer. And you wouldn't need to pass the original ptr.
In the second example, on the other hand, you are passing a pointer to the outer ptr, so the function will be modifying the same memory space that the outer ptr uses.
I hope this can help a little.
Upvotes: 1