Why is the pointer passed into the function still NULL?

Discussion in 'C Programming' started by Malcolm Nooning, Apr 5, 2011.

  1. Hello,
    My goal here is to be able to understand why the code below results in
    the printing of "maintable is NULL.". I saw a way to do what I wanted
    using typedef string ..., but doing that does not help me to
    understand what is going wrong here. Wy doesn't the variable
    maintable change?
    Thanks


    #include <stdio.h>

    void get_table (char**);

    int main (void) {
    int i, j;
    char *maintable = NULL;
    get_table((char**) maintable);

    if (maintable == NULL) {
    puts("maintable is NULL.");
    } else {
    puts("maintable is not NULL.");
    for (i=0; i<2;i++) {
    printf("%s\n", maintable);
    }
    }
    }

    void get_table (char ** table) {
    char* functable[] = {"Okay", "Help"};
    table = functable;
    }

    /* Prints "maintable is NULL." */
     
    Malcolm Nooning, Apr 5, 2011
    #1
    1. Advertising

  2. Same result if I (more properly) change * to ** in the line below.
    char **maintable = NULL;
     
    Malcolm Nooning, Apr 5, 2011
    #2
    1. Advertising

  3. Thank you very much, especially for your thoroughness. Nothing helps
    in understanding like a really good explanation.
     
    Malcolm Nooning, Apr 5, 2011
    #3
  4. Malcolm Nooning

    John Bode Guest

    On Apr 5, 11:37 am, Malcolm Nooning <> wrote:
    > Hello,
    > My goal here is to be able to understand why the code below results in
    > the printing of "maintable is NULL.".  I saw a way to do what I wanted
    > using typedef string ..., but doing that does not help me to
    > understand what is going wrong here.  Wy doesn't the variable
    > maintable change?
    > Thanks
    >


    You have several problems. First of all, remember that in C all
    function parameters are passed by value; the formal parameter is a
    different object in memory from the actual parameter, so if the
    function writes to the formal parameter, the actual parameter isn't
    affected. If you want the function to be able to modify it's inputs,
    you must pass a *pointer* to the thing you want to modify. For
    example,

    void foo (int x)
    {
    x = 5;
    }

    void bar(void)
    {
    int a = 3;
    foo(a);
    printf("a = %d\n", a);
    }

    The expression x in foo and the expression a in bar refer to
    completely different objects; writing to x doesn't affect a, so the
    output of bar is "a = 3". If we want foo to modify the contents of a,
    we must pass a pointer to a, like so:

    void foo (int *x)
    {
    *x = 5;
    }

    void bar(void)
    {
    int a;
    foo(&a); // the expression &a evaluates to the *location* of
    a.
    printf("a = %d\n");
    }

    Now the expression *x in foo and the expression a in bar both refer to
    the same object, so writing to *x affects a.

    Note that I used the address-of operator & to get the address of a and
    pass that to foo; I didn't just cast a to int *.

    This same rule applies for any type parameter; if you want a function
    to modify a pointer (that is, change where the pointer points to), you
    must pass a pointer to that pointer:

    void foo (int **x)
    {
    *x = &some_other_integer; // where some_other_integer has a
    // lifetime outside of foo
    }

    void bar (void)
    {
    int *a = &some_integer;
    foo(&a);
    printf("*a = %d\n", *a);
    }

    Again, the expression *x in foo and the expression a in bar refer to
    the same object, it's just that this time that object is a pointer to
    an integer, rather than the integer itself. Writing to *x updates the
    *pointer* value, not the integer value.

    Your next problem is that your off by one level of indirection in your
    types. The expression functable in get_table has type char *[2],
    which will "decay" to char **, not char *. Thus, you want maintable
    in main to have type char **, and the parameter table will need to
    have type char ***.

    Your final problem is that functable has auto extent; once the
    get_table function exits, functable will no longer exist (the memory
    that it occupied is released for something else to use, and the
    pointer to it will no longer be valid). You can deal with this
    several different ways. One is to declare functable as static; this
    allocates the memory for it in such a way that it's not released after
    the function exits:

    static char *functable[] = {"Okay", "Help"};

    Another option is to define the array at file scope, which has the
    same effect as declaring it as static in the function, although the
    functable identifier is visible to the entire program now:

    char *functable[] = {"Okay", "Help"};
    ...
    int main(void)
    ...
    void get_table(char ***table)
    {
    *table = functable;
    }

    Or, you could declare the memory dynamically, which remains allocated
    until you deallocate it explicitly:

    void get_table(char ***table)
    {
    *table = malloc(sizeof **table * 2);
    if (*table)
    {
    *table[0] = malloc(strlen("Okay") + 1);
    if (*table[0])
    strcpy(*table[0], "Okay");
    *table[1] = malloc(strlen("Help") + 1);
    if (*table[1])
    strcpy(*table[1], "Help");
    }
    }

    The downside of this method is that you'll have to free up that memory
    at the end of the program:

    free(maintable[0]);
    free(maintable[1]);
    free(maintable);
     
    John Bode, Apr 5, 2011
    #4
  5. I am very close. The code pasted further below prints out
    maintable is not NULL.
    A01 A02 A03 A04
    Segmentation fault (core dumped)
    I know the problem has to do with pointer indirection, but I cannot
    reason (or guess) it out. Where is the bug?

    ------- Paste of updated code snippet

    #include <stdio.h>

    void print_table (char ***);
    void get_table (char ***);

    int main (void) {
    char ***maintable;
    get_table(maintable);

    if (maintable == NULL) {
    puts("maintable is NULL.");
    } else {
    puts("maintable is not NULL.");
    print_table(maintable);
    }
    }

    void get_table (char ***table) {
    static char *functable[2][4] =
    {
    "A01", "A02", "A03", "A04",
    "B01", "B02", "B03", "B04"
    };
    *table = functable;
    }

    void print_table (char ***ptable) {
    int i, j, k;
    for (i=0; i < 2; i++) {
    for (j=0; j < 4; j++) {
    for (k=0; k < 3; k++) {
    printf("%c", ptable[j][k]);
    }
    printf(" ");
    }
    printf("\n");
    }
    };
     
    Malcolm Nooning, Apr 18, 2011
    #5
  6. Malcolm Nooning <> writes:

    > I am very close. The code pasted further below prints out
    > maintable is not NULL.
    > A01 A02 A03 A04
    > Segmentation fault (core dumped)
    > I know the problem has to do with pointer indirection, but I cannot
    > reason (or guess) it out. Where is the bug?


    There is not really one bug more a general misunderstanding about how a
    function can alter a variable in the calling function and another
    misunderstanding about the distinction between arrays and pointers.
    Specifically about pointers to pointers and 2D arrays. I think you'd
    find Q6.18 of the C FAQ useful:

    http://c-faq.com/aryptr/pass2dary.html

    > ------- Paste of updated code snippet
    >
    > #include <stdio.h>
    >
    > void print_table (char ***);
    > void get_table (char ***);
    >
    > int main (void) {
    > char ***maintable;
    > get_table(maintable);


    A C function can not alter in any way the value of it argument.
    get_table can't change maintable no matter how many *s the declaration
    has.

    Step back a bit and think about a function that takes an int:

    void f(int);

    and a call to it like this:

    int x = 42;
    f(42);

    we know for certain that f can't change x -- C passes by value: the
    value of x is passed to f and appears in f as the initial value of a new
    object, the declared parameter of the function.

    It won't matter if add a star:

    void f(int *);
    /* ... */
    int *x;
    f(x);

    or if we add several:

    void f(int ****);
    /* ... */
    int ****x;
    f(x);

    the function can't change x. For a function to change something in the
    calling function we must pass a pointer to that thing. This means that
    the declaration of the function's parameter will have one more * than
    the declaration of object we want changed. This mismatch is corrected
    but passing &x rather than x:

    void f(int *ip) { *ip = 1; }
    /* ... */
    int x = 42;
    f(&x);

    Now x will be 1.

    > if (maintable == NULL) {
    > puts("maintable is NULL.");
    > } else {
    > puts("maintable is not NULL.");
    > print_table(maintable);


    The fact that you get this is simply an accident.

    > }
    > }
    >
    > void get_table (char ***table) {
    > static char *functable[2][4] =
    > {
    > "A01", "A02", "A03", "A04",
    > "B01", "B02", "B03", "B04"
    > };
    > *table = functable;


    If the compiler is not complaining about this line, you must alter the
    way you call it. The assignment is between type that can't be assigned
    without a diagnostic (basically an error message). The expression

    functable

    is of type char *(*)[4]. That's quite a complex type (it's a pointer to
    an array or 4 pointers to char) but the point is that you can't assign
    such an object to a char ** object (that's the type of *table).

    Arrays complicate the picture to the extent that I am not exactly sure
    how best to fix things. If you really need to have the function alter an
    object in the caller so that it refers to a 2D array of strings then
    you'll need a seriously complex type![1]

    If you said what it is you need to do, maybe someone could come up with
    a simpler design.

    > }
    >
    > void print_table (char ***ptable) {
    > int i, j, k;
    > for (i=0; i < 2; i++) {
    > for (j=0; j < 4; j++) {
    > for (k=0; k < 3; k++) {
    > printf("%c", ptable[j][k]);
    > }
    > printf(" ");
    > }
    > printf("\n");
    > }
    > };


    That ; is a syntax error.

    [1] Here's one way:

    #include <stdio.h>

    void print_table (char *(*)[4]);
    void get_table (char *(**)[4]);

    int main (void) {
    char *(*maintable)[4];
    get_table(&maintable);

    if (maintable == NULL) {
    puts("maintable is NULL.");
    } else {
    puts("maintable is not NULL.");
    print_table(maintable);
    }
    }

    void get_table (char *(**table)[4]) {
    static char *functable[2][4] =
    {
    "A01", "A02", "A03", "A04",
    "B01", "B02", "B03", "B04"
    };
    *table = functable;
    }

    void print_table (char *(*ptable)[4]) {
    int i, j, k;
    for (i=0; i < 2; i++) {
    for (j=0; j < 4; j++) {
    for (k=0; k < 3; k++) {
    printf("%c", ptable[j][k]);
    }
    printf(" ");
    }
    printf("\n");
    }
    }

    but I doubt very much that this is the best way to do what you want.

    --
    Ben.
     
    Ben Bacarisse, Apr 18, 2011
    #6
  7. Hiding data within functions is done a lot in higher level languages.
    The data is only accessible by invoking the function that hides it.
    My original idea was to be able to figure out how to do the same thing
    in C. As I stated earlier, I found a way to do it using typedef
    string ..., but that did not provide any understanding of why I could
    not do it using just chars, arrays of chars, and pointers. Your code
    does indeed work. Along with your explanation, that is what I
    wanted. Thank you very much.

    Besides my two C books, I read a number of pointer and array tutorials
    to fully understand these concepts. Only one book covered up to
    arrays of arrays of chars, but it did not discuss pointers to arrays
    of arrays of chars, or passing the information in such structures to
    and from functions. Your code is an excellent example.


    For completeness, can you explain why the parenthesis are as they are
    with
    void print_table (char *(*)[4]);
    and
    void get_table (char *(**)[4]);

    I know that
    char (*)[4]
    is a pointer to an array of four chars, while
    char *[4]
    is an array of four pointers to type char.

    You have already gone further than the books and tutorials, so again I
    thank you for your efforts. I hope the explanation does not get
    quantum!

    By the way, I will be passing all of this information to book and
    tutorial authors, in case they ever update their writings.

    Thanks again.
     
    Malcolm Nooning, Apr 19, 2011
    #7
  8. Malcolm Nooning <> writes:
    <snip>
    > For completeness, can you explain why the parenthesis are as they are
    > with
    > void print_table (char *(*)[4]);
    > and
    > void get_table (char *(**)[4]);
    >
    > I know that
    > char (*)[4]
    > is a pointer to an array of four chars, while
    > char *[4]
    > is an array of four pointers to type char.


    C types are read from the "inside" out (respecting parentheses) going
    right if you can and left otherwise. The "inside" is where the name
    would be if the type were being used to declare a variable. Brackets
    are used to alter this order -- that's why (*)[...] is a pointer to a
    array and *[...] is an array or pointers.

    Another way of looking at this is to think of the various components of
    a declarator like operators in an expression. In fact, this is why C
    types look like they do -- the declarator syntax was chosen to mirror
    the syntax of expressions. In this view, function brackets and array
    brackets bind more tightly than the * used for pointer declarations with
    parentheses being used to alter this binding as they are in expressions.

    Given that you understand how ()s alter the meaning of char (*)[4] and
    char *[4], I find your opening question hard to answer. The parentheses are as
    they are so that the types are correct! Maybe you mean "why are these
    the types that work" or maybe "what are these types"?

    The answer to the second of these questions is that types are "a pointer
    to an array of 4 pointers to char" and "a pointer to a pointer to an
    array of 4 pointer to char". As you'd expect from the simple int
    example I gave, the type needed so that get_table can modify the value
    of the variable in the caller has one more "pointer to ..." in front of
    it. So the first question becomes: "why are maintable in main and the
    parameter of print_table of type "pointer to an array of 4 pointers to
    char"?

    The data are held in a 2D array, and arrays in C are passed as pointers
    to their first element. Ignoring the element type for the moment (it's
    char * but let's just say T for now) an object declared:

    T x[2][4];

    will be passed as a pointer to the first element -- a pointer to the
    first of the two 4-element sub-arrays of x:

    print_table(T (*)[4]);

    T is in fact char * which is why the real type you need is

    print_table(char *(*)[4]);

    That's also the type you need for the variable that refers to the actual
    data you have. That's why maintable is declared

    char *(*maintable)[4];

    I hope that helps.

    <snip>
    --
    Ben.
     
    Ben Bacarisse, Apr 19, 2011
    #8
  9. It helps. Thanks again.
     
    Malcolm Nooning, Apr 21, 2011
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. dee
    Replies:
    9
    Views:
    515
    Joseph Byrns
    Apr 15, 2005
  2. _ivan
    Replies:
    4
    Views:
    361
    Hafiz Abid Qadeer
    Sep 6, 2003
  3. Mr. SweatyFinger
    Replies:
    2
    Views:
    2,008
    Smokey Grindel
    Dec 2, 2006
  4. aneuryzma
    Replies:
    3
    Views:
    720
    Jim Langston
    Jun 16, 2008
  5. Christopher
    Replies:
    4
    Views:
    445
    Ruben Safir
    Jul 9, 2011
Loading...

Share This Page