Sharvari Marathe
Sharvari Marathe

Reputation: 79

Passing arrays in c

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

Answers (5)

JackCColeman
JackCColeman

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

verbose
verbose

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

John Bode
John Bode

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

sujin
sujin

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

ror3d
ror3d

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

Related Questions