取消引用数组中结构中字符串的 C 指针?
Deref a C pointer to a string within a struct within an array?
我有一个棘手的 C 语法问题。我正在构建一个链表数组,其中列表中的每个节点都由一个结构表示。每个struct保存一个字符串,后面很重要:
// "linkedList.h"
typedef struct llnode listnode;
struct llnode {
char* data; // string
// ...and other data
};
我的代码构建了一个 table 指向这些 "listnodes" 的指针,并将所有这些指针设置为 NULL,原因超出了 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;
}
到目前为止,还不错。后来,我的程序把数据塞进了table,添加到右链表if/when是必要的。执行此操作的代码有效,但为了使我的 post 尽可能简短,我将在此处提供一个高度简化的版本:
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;
}
这一切都很棒。现在是我真正的问题......假设我想进入 table 并取消引用其中一个字符串?
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 输出;表示 "New String A" 的节点首先插入到 table 中的索引 0 处。 :
31 printf("table[%d] == %d\n", i, **(table+i));
(gdb) p *(table+i)
= (listnode *) 0x6000397b0
(gdb) p **(table+i)
= {data = 0x6000397d0 "New String A"}
(gdb) p **(table+i)->data
Cannot access memory at address 0x4e
(gdb)
我对此真的很困惑。一旦一个 C 指针经历了不止一层的取消引用,我就开始怀疑了。有人知道这里的正确语法是什么吗?
感谢一百万,
-皮特
PS - 为超长内容道歉 post。我发誓我努力将它保持在可管理的大小...
C a->b
运算符的计算结果为 (*a).b
。所以你的行实际上评估为 ***(table+i).data
,这显然是不正确的。
它也可能有助于用括号分组,因此很清楚该行的计算结果是 (***(table+i)).data
还是 ***((table+i).data)
。根据您收到的错误消息,可能是后者。
然后你可以用数组语法来清理更多。
全部放在一起,可以将行简化为table[i]->data
。
那么为什么您的调试会话有其他指示?因为 **(table+i) 是实际的 struct 本身。要使用 ->
,您需要一个指向该结构的指针。
另外,小费。你投了你的 malloc() 电话。通常,程序员出于多种原因避免这种情况。有几篇关于此的帖子,但可以找到最好的帖子之一 here。
鉴于声明
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)
,这不是你想要的。
好的,所以 ->
最初被称为 主运算符 ,现在在 C11 中被指定为 后缀表达式 .它比一元运算符绑定得更紧密。 ->
与 .
相似,因为它具有最高优先级。
情况 1: 要使用间接指针,可以执行以下操作:
(*p)->field // or...
p[0]->field
案例 2: 在间接指针的疯狂边缘,你显然可以继续...
(**p)->field // or ...
p[0][0]->field
但是一旦你把括号放在那里,你就可以像下面那样在结果表达式中加点。使用 ->
将一些 deref 放在 ->
运算符中,一些放在 *
中;可能存在使用其中一个的争论。
(**p).field // case 1
p[0][0].field // still case 1
(***p).field // case 2
p[0][0][0].field // still 2
我有一个棘手的 C 语法问题。我正在构建一个链表数组,其中列表中的每个节点都由一个结构表示。每个struct保存一个字符串,后面很重要:
// "linkedList.h"
typedef struct llnode listnode;
struct llnode {
char* data; // string
// ...and other data
};
我的代码构建了一个 table 指向这些 "listnodes" 的指针,并将所有这些指针设置为 NULL,原因超出了 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;
}
到目前为止,还不错。后来,我的程序把数据塞进了table,添加到右链表if/when是必要的。执行此操作的代码有效,但为了使我的 post 尽可能简短,我将在此处提供一个高度简化的版本:
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;
}
这一切都很棒。现在是我真正的问题......假设我想进入 table 并取消引用其中一个字符串?
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 输出;表示 "New String A" 的节点首先插入到 table 中的索引 0 处。 :
31 printf("table[%d] == %d\n", i, **(table+i));
(gdb) p *(table+i)
= (listnode *) 0x6000397b0
(gdb) p **(table+i)
= {data = 0x6000397d0 "New String A"}
(gdb) p **(table+i)->data
Cannot access memory at address 0x4e
(gdb)
我对此真的很困惑。一旦一个 C 指针经历了不止一层的取消引用,我就开始怀疑了。有人知道这里的正确语法是什么吗?
感谢一百万, -皮特
PS - 为超长内容道歉 post。我发誓我努力将它保持在可管理的大小...
C a->b
运算符的计算结果为 (*a).b
。所以你的行实际上评估为 ***(table+i).data
,这显然是不正确的。
它也可能有助于用括号分组,因此很清楚该行的计算结果是 (***(table+i)).data
还是 ***((table+i).data)
。根据您收到的错误消息,可能是后者。
然后你可以用数组语法来清理更多。
全部放在一起,可以将行简化为table[i]->data
。
那么为什么您的调试会话有其他指示?因为 **(table+i) 是实际的 struct 本身。要使用 ->
,您需要一个指向该结构的指针。
另外,小费。你投了你的 malloc() 电话。通常,程序员出于多种原因避免这种情况。有几篇关于此的帖子,但可以找到最好的帖子之一 here。
鉴于声明
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)
,这不是你想要的。
好的,所以 ->
最初被称为 主运算符 ,现在在 C11 中被指定为 后缀表达式 .它比一元运算符绑定得更紧密。 ->
与 .
相似,因为它具有最高优先级。
情况 1: 要使用间接指针,可以执行以下操作:
(*p)->field // or...
p[0]->field
案例 2: 在间接指针的疯狂边缘,你显然可以继续...
(**p)->field // or ...
p[0][0]->field
但是一旦你把括号放在那里,你就可以像下面那样在结果表达式中加点。使用 ->
将一些 deref 放在 ->
运算符中,一些放在 *
中;可能存在使用其中一个的争论。
(**p).field // case 1
p[0][0].field // still case 1
(***p).field // case 2
p[0][0][0].field // still 2