我有一个棘手的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 - 为超长职位道歉。我发誓我努力保持它的可控尺寸...
答案 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