Deref指向数组中结构中的字符串的C指针?

时间:2016-10-07 18:38:38

标签: c pointers struct dereference

我有一个棘手的C语法问题。我正在构建一个链表的数组,其中列表中的每个节点都由一个结构表示。每个结构都包含一个字符串,这在以后很重要:

// "linkedList.h"

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

我的代码构建了一个指向这些“listnodes”的指针表,并将所有指针设置为NULL,原因超出了本文的范围:

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;
}

到目前为止,这么好。之后,我的程序将数据填充到表中,如果/在必要时添加到右链表。这样做的代码有效,但我会在这里提供一个高度简化的版本,以保持我的帖子尽可能简短:

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;
}

这一切都很棒。现在我的真正问题是......假设我想进入表格并取消引用其中一个字符串?

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;
}

编译器不喜欢我的语法:

$ 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);
                                                ^
$

所以我知道这对于一个简单的问题来说是一个长期缠绕的前提,但有人可以帮助我在这里使用正确的语法吗?我尝试了很多语法变化,没有运气。

更令人费解的是,当我稍微修改代码以编译它时,然后在GDB中查看,我可以看到**(table+i)是我的结构,但是**(table+i)->data是不可访问的。这是调试(修改)程序时的GDB输出;表示“新字符串A”的节点首先插入表中的索引0处。 :

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)

我真的很困惑。一旦C指针经过多个解除引用层,我就会开始睁大眼睛。任何人都知道这里的正确语法是什么?

万分感谢 -Pete

PS - 为超长职位道歉。我发誓我努力保持它的可控尺寸...

3 个答案:

答案 0 :(得分:3)

鉴于声明

listnode **table;

然后以下表达式具有指定的类型

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

因此,您可以使用以下表达式之一来访问data成员,从最少到大多数眼睛都是:

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

分组代表是必要的 - .->成员选择运算符的优先级高于一元*,因此*table[i].data将被解析为*(table[i].data) ,这不是你想要的。

答案 1 :(得分:2)

C a->b运算符的计算结果为(*a).b。因此,您的行实际上会评估为***(table+i).data,这显然是不正确的。

也可以帮助将括号分组,以便明确该行是否评估为(***(table+i)).data***((table+i).data)。根据您收到的错误消息,可能是后者。

然后你可以使用数组语法来清理它。

全部放在一起,您可以将行简化为table[i]->data

那么为什么你的调试会话显示不是这样?因为**(table + i)是实际的 struct 本身。要使用->,您需要一个指向结构的指针。

另外,小小的提示。你铸造了malloc()调用。通常,程序员出于几个原因避免这种情况。有几个帖子,但最好的帖子之一可以找到here

答案 2 :(得分:1)

好的,所以->最初被称为主要操作符,现在在C11中被指定为后缀表达式。它比一元运算符更紧密地绑定。 ->.类似,因为它具有最高优先级。

案例1:使用间接指针,可以执行以下操作:

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

案例2:在间接指针的疯狂边缘上,你显然可以继续这个......

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

但是一旦你把那些parens扔进那里,你就可以点击下面的结果表达式。使用->会在->运算符中放置一些deref,在*中放置一些deref;可能存在使用其中一个的争论。

(**p).field      // case 1
p[0][0].field    // still case 1
(***p).field     // case 2
p[0][0][0].field // still 2