我有一个棘手的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] == NULLn", i);
else
printf("table[%d] == %sn", 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] == %sn", i, **(table+i)->data);
^
$
我知道对于一个简单的问题来说这是一个冗长的前提,但是有人能在这里帮助我正确的语法吗?我尝试了许多语法变化,但都没有成功。
更令人费解的是,当我稍微修改代码来编译它时,然后在GDB中查看这个,我可以看到**(table+i)
是我的结构体,但**(table+i)->data
不可访问。下面是调试(修改后的)程序时的GDB输出;表示"新字符串A"的节点首先插入到表的索引0处。:
31 printf("table[%d] == %dn", 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指针经历了不止一层的解引用,我就开始对眼了。有人知道这里的正确语法是什么吗?
非常感谢。皮特
PS -为这篇超长的文章道歉。
给定声明
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)
,这不是您想要的。
C a->b
运算符的计算结果为(*a).b
。因此,您的行实际上求值为***(table+i).data
,这显然是不正确的。
用括号分组也有帮助,这样行计算结果是(***(table+i)).data
还是***((table+i).data)
就很清楚了。根据您得到的错误消息,可能是后者。
然后你可以使用数组语法来清理它。
所有这些放在一起,您可以将这行简化为table[i]->data
。
那么为什么你的调试会话显示的不是这样呢?因为**(table+i)是实际的结构体本身。要使用->
,需要一个指向该结构体的指针。
还有一个小提示。您强制转换了malloc()调用。通常,程序员会出于几个原因避免这种情况。关于这个问题有很多帖子,但最好的一个可以在这里找到。
好,所以->
最初被称为主操作符,现在在C11中指定为后缀表达式。它比一元操作符绑定更紧密。->
类似于.
,因为它具有最高的优先级。
情形1:使用间接指针,可以这样做:
(*p)->field // or...
p[0]->field
情况2: out在间接指针的极端情况下,你显然可以继续使用…
(**p)->field // or ...
p[0][0]->field
但是,一旦您在那里添加了父元素,您就可以像下面这样在结果表达式中添加点。使用->
将部分解算符放入->
操作符,部分放入*
操作符;使用其中一个或另一个可能存在争议。
(**p).field // case 1
p[0][0].field // still case 1
(***p).field // case 2
p[0][0][0].field // still 2