将C指针指向数组中结构体中的字符串



我有一个棘手的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

相关内容

  • 没有找到相关文章

最新更新