C语言中结构体和动态内存分配问题

3

令人惊讶的是,即使是最简单的程序在C语言中也可能会引起很多麻烦。

#include <stdio.h> 
#include <stdlib.h> 

typedef struct node {
    int value;
    struct node *leftChild;
    struct node *rightChild;
} node;

typedef struct tree {
    int numNodes;
    struct node** nodes;
} tree;

tree *initTree() {
    tree* tree = (tree*) malloc(sizeof(tree));
    node *node = (node*) malloc(sizeof(node));
    tree->nodes[0] = node;
    return tree;
}

int main() {
    return 0;
}

编译器显示:
main.c: In function 'initTree':
main.c:17: error: expected expression before ')' token 
main.c:18: error: expected expression before ')' token

你能帮忙吗?

这段内容与IT技术无关。

4
错误的原因(名称冲突)并不是由C语言引起的。在许多编程语言中都可能会出现此类错误。(但是,是的,错误信息往往难以理解。) - user395760
8个回答

14

您正在使用两个名为treenode的变量,但您还将结构体typedeftreenode

请更改您的变量名称:

#include <stdio.h>
#include <stdlib.h>

typedef struct node {
    int value;
    struct node *leftChild;
    struct node *rightChild;
} node;

typedef struct tree {
    int numNodes;
    struct node** nodes;
} tree;

tree *initTree() {
   /* in C code (not C++), don't have to cast malloc's return pointer, it's implicitly converted from void* */
   tree* atree = malloc(sizeof(tree)); /* different names for variables */
   node* anode = malloc(sizeof(node));
   atree->nodes[0] = anode;
   return atree;
}

int main() {
    return 0;
}

1
那我是不是永远都不应该尝试将从malloc返回的void转换类型? - Gal
2
@sombe - 你不需要在C语言中做这个操作,从void *到指针类型的转换是隐式的。 - wkl
@sombe,是的。如果您这样做,可能会隐藏一些对int的隐式转换,如果未包含malloc的原型。 - Jens Gustedt

5

treenode是类型名称,以后不应该将它们用作变量名。

tree *initTree() {
    tree *myTree = (tree*) malloc(sizeof(tree));
    node *myNode = (node*) malloc(sizeof(node));
    myTree->nodes[0] = myNode;
    return myTree;
}

3

(tree*)(node*)更改为(struct tree*)(struct node*)。不能只写tree,因为那也是一个变量。


1
不需要(typedef struct tree {} tree)。 - user395760
我现在尝试了一下,似乎可以编译。但是,它在没有 struct 的情况下工作(但只有当变量具有与类型名称不冲突的名称时)。 - user395760
@delnan:恰好就是我的观点…因此如果他不改变其余的代码,他需要说“struct”。 - user541686
如果他不改变代码的其余部分,那么是的。但大多数人(看看投票)似乎认为重命名变量才是真正的解决方案(它还允许在类型名称之前省略有争议的“struct”)。 - user395760
1
但是,如果有更好的解决方案,为什么不提出更好的解决方案(并可能提及较劣的解决方案)呢? - user395760
显示剩余2条评论

2

将initTree的主体更改为以下内容:

tree* myTree = (tree *)malloc(sizeof(tree));
node *myNode = (node *)malloc(sizeof(node));
myTree->nodes[0] = myNode;
return myTree;

1

不要使用typedef的名称作为变量名,而且在C语言中没有必要对malloc();进行强制类型转换。

#include <stdio.h> 
#include <stdlib.h> 

typedef struct node {
    int value;
    struct node *leftChild;
    struct node *rightChild;
} node;

typedef struct tree {
    int numNodes;
    struct node** nodes;
} tree;

tree *initTree() {
    tree->nodes[0] = malloc(sizeof(node));
    return malloc(sizeof(tree));
}

int main() {
    return 0;
}

0
除了原来的问题,即使在正确的形式下,这段代码也不会工作,因为tree::nodes(抱歉,这是C++符号)作为指向指针的指针,在一个tree被malloced之后不会指向任何有用的东西。所以tree->nodes[0]在普通指针的情况下基本上与*(tree->nodes)相同,不能被解引用。这是一棵非常奇怪的树的头部,但你至少应该分配一个单独的node*来初始化那个指向指针的指针:
tree *initTree() {
   /* in C code (not C++), don't have to cast malloc's return pointer, it's implicitly converted from void* */
   tree* atree = malloc(sizeof(struct tree)); /* different names for variables */

   /* ... */

   /* allocate space for numNodes node*, yet numNodes needs to be set to something beforehand */
   atree->nodes = malloc(sizeof(struct node*) * atree->numNodes);

   node* anode = malloc(sizeof(struct node));
   atree->nodes[0] = anode;
   return atree;
}

0

我同意Mehrdad的解释非常到位。

在C代码中,你定义一个与结构体名称相同的变量,例如"node node;"是很常见的。也许这不是一个好的风格;但在像Linux内核等代码中很普遍。

原始代码中的真正问题是编译器不知道如何解释"(tree*) malloc"中的"tree"。根据编译错误,显然它被解释为一个变量。


0

有趣的是,如果你只是将分配写成以下形式,它就可以干净地编译通过:

tree *tree = malloc(sizeof *tree);

通常认为使用"sizeof 变量"而不是"sizeof(类型)"更好,而在这种情况下,风格约定解决了语法错误。个人认为,这个例子很好地说明了为什么类型转换通常是一个坏主意,因为如果写成以下形式,代码会更加清晰:

struct tree *tree = malloc(sizeof *tree);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接