1

I have a thorny C syntax question. I'm building an array of linked lists, where each node in a list is represented by a struct. Each struct holds a string, which is important later:

// "linkedList.h"

typedef struct llnode listnode;
struct llnode {
    char* data;     // string
    // ...and other data
};

My code builds a table of pointers to these "listnodes" and sets all those pointers to NULL, for reasons beyond the scope of this post:

void initTable(listnode** table){
    // Initialize all pointers in the table to NULL
    for (int i = 0; i < TABLESIZE; i++)
        table[i] = NULL;
}

int main(){    
    // Create table of linked lists
    listnode** table = (listnode**) malloc(TABLESIZE * sizeof(listnode*));
    initTable(table);

    return 1;
}

So far, so good. Later, my program stuffs data into the table, adding on to the right linked list if/when necessary. The code to do that works, but I'll offer up a HIGHLY simplified version here for the sake of keeping my post as brief as possible:

void insert(listnode** table, int index, char* newData){
    if(*(table+index)==NULL){
        // Create the new Node
        listnode *newNode = NULL;
        newNode = (listnode*)malloc(sizeof(listnode));  // allocate for the struct
        newNode->data = (char*)malloc(sizeof(char)*15); // allocate for the string within the struct
        strcpy(newNode->data, newData);                 // copy newData into newNode->data

        // Insert node into table (Super simple version)
        *(table+index) = newNode;
    }
}

int main(){
    listnode** table = (listnode**) malloc(TABLESIZE * sizeof(listnode*));
    initTable(table);

    insert(table, 0, "New String A");
    insert(table, 5, "New String B");
    insert(table, 7, "New String C");

    return 1;
}

All this works great. Now for my real question... Suppose I want to reach into the table and dereference one of those strings?

void printTable(listnode** table){
    for(int i=0; i<TABLESIZE; i++){
        if(*(table+i)==NULL)
            printf("table[%d]  ==  NULL\n", i);
        else
            printf("table[%d]  ==  %s\n", i, **(table+i)->data);  // << PROBLEM IS HERE!
    }
}

int main(){
    // create & initialize the table, as above
    // insert data into the table, as above
    printTable(table);

    return 1;
}

The compiler doesn't like my syntax:

$ gcc -Wall linkedList.c
linkedListc: In function ‘printTable’:
linkedList.c:31:48: error: request for member ‘data’ in something not a structure or union
    printf("table[%d]  ==  %s\n", i, **(table+i)->data);
                                                ^
$

So I know this is kinda a long-winding premise for a simple question, but can someone help me with the proper syntax here? I've tried a number of syntactical variations, with no luck.

More puzzling, when I modify the code a little to compile it, then look at this in GDB, I can see that **(table+i) is my struct, yet **(table+i)->data is not accessible. Here's the GDB output when I debug the (modified) program; the node representing "New String A" is inserted first at index 0 in the table. :

31           printf("table[%d]  ==  %d\n", i, **(table+i));
(gdb) p *(table+i)
$1 = (listnode *) 0x6000397b0
(gdb) p **(table+i)
$2 = {data = 0x6000397d0 "New String A"}
(gdb) p **(table+i)->data
Cannot access memory at address 0x4e
(gdb)

I'm really confused about this. Once a C pointer goes through more than one layer of dereferencing, I start getting cross-eyed. Anyone know what the proper syntax here might be?

Thanks a million, -Pete

PS - Apologies for the superlong post. I swear I struggled to keep it a manageable size...

Pete
  • 1,511
  • 2
  • 26
  • 49
  • First, instead of setting them null, why not calloc? Second, it's not your syntax, that object is improperly aligned -- try a cast. – cat Oct 07 '16 at 18:41
  • I don't think your examples are enough to reproduce this to write an answer, consider providing a [mcve] – cat Oct 07 '16 at 18:42

3 Answers3

3

Given the declaration

listnode **table;

then the following expressions have the specified type

   Expression             Type
   ----------             ----
        table             listnode **
    table + i             listnode **
       *table             listnode *
 *(table + i)             listnode *
     table[i]             listnode *
**(table + i)             listnode
    *table[i]             listnode   

So you would use one of the following expressions to access the data member, from least to most eye-stabby:

table[i]->data      // use this, please
(*table[i]).data
(*(table + i))->data
(**(table + i)).data

The grouping parens are necessary - the . and -> member selection operators have higher precedence than unary *, so *table[i].data would be parsed as *(table[i].data), which is not what you want.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • Thanks, this is superhelpful! I'm adding all of this into my C notes. I've gotta say, C syntax is really, really difficult - how long does it take to learn all this stuff? Years? Whew...! – Pete Oct 10 '16 at 17:29
  • 1
    @Pete: C expression syntax isn't *difficult*, just involved (and in some cases, non-intuitive). It only *looks* difficult because nobody takes the time to explain it properly. If I taught a C class (which will never happen, thank *your* lucky stars), I'd spend at least half the semester going over expressions and types. – John Bode Oct 10 '16 at 17:32
  • Well, God bless you for taking the time to explain it to me. I'm finding that 90% of my C questions boil down to the specifics of C syntax - its a comfort to know that perhaps it isn't as complex and thorny as I initially thought. Thanks again! – Pete Oct 10 '16 at 17:34
2

The C a->b operator evaluates to (*a).b. So your line actually evaluates to something like ***(table+i).data, which is obviously incorrect.

It also may help to group with parentheses so it's clear whether the line evaluates to (***(table+i)).data or ***((table+i).data). Based on the error message you get, it's probably the latter.

And then you can use the array syntax to clean it up more.

All put together, you can simplify the line to table[i]->data.

So why did your debugging session indicate otherwise? Because **(table+i) is the actual struct itself. To use ->, you need a pointer to the struct.

Also, a little tip. You casted your malloc() calls. Generally, programmers avoid this for several reasons. There are several posts on this, but one of the best ones can be found here.

Community
  • 1
  • 1
anonymoose
  • 819
  • 2
  • 11
  • 25
  • Thanks, this helps a lot. I thought I tried table[i]-> data, but obviously I didn't. Its in my code now and the whole thing works. Much appreciated! – Pete Oct 10 '16 at 17:33
1

Ok, so -> is something that was originally called a primary operator and now is specified in C11 as a postfix expression. It binds more closely than a unary operator. The -> is like . in that it has the highest priority of all.

Case 1: to use an indirect pointer, one could do the following:

(*p)->field // or...
p[0]->field

Case 2: out on the lunatic fringe of indirect pointers, you can obviously continue this with...

(**p)->field // or ...
p[0][0]->field

But once you throw the parens in there, you could just dot into the resulting expression like below. Using -> puts some of the deref in the -> operator and some of it in the *; there might be an argument for using one or the other.

(**p).field      // case 1
p[0][0].field    // still case 1
(***p).field     // case 2
p[0][0][0].field // still 2
DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
  • Thanks, this is clear and thought-provoking. I have a long way to go before I truly understand C syntax. Thanks! – Pete Oct 10 '16 at 17:30