Pete Pete - 1 month ago 7
C Question

Deref a C pointer to a string within a struct within an array?

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...

Answer

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.

Comments