5

我有这个结构定义:

typedef struct node_bst
{
  int data;
  struct node_bst *lchild;
  struct node_bst *rchild;
  struct node_bst *parent;
} node_bst;

我尝试使用以下方法创建指向结构的指针:

node_bst *root;

并像这样为其分配内存:

root= malloc(sizeof(node_bst));

现在,为了初始化其中的数据项,我尝试了这个语句(从结构变量的通常初始化中得到提示):

*root= {0, NULL, NULL, NULL};

但是编译器抛出了一个错误

错误:“{”标记之前的预期表达式

我查了一下,发现我需要像这样对它进行类型转换:

*root= (node_bst) {0, NULL, NULL, NULL};

现在它工作正常,但我的问题是,为什么我需要这样做?

我希望编译器已经知道 root 是指向 node_bst 类型结构变量的指针。那么为什么需要对右值进行类型转换呢?

另一个奇怪的事情:

int *a= malloc(sizeof(int));
*a= 4;

这工作得很好。

4

2 回答 2

10

没有铸造。

在这份声明中

*root = (node_bst) {0, NULL, NULL, NULL};

使用了所谓的复合文字(node_bst) {0, NULL, NULL, NULL},它对应于该类型的对象,node_bst并将该对象分配给该对象*root

来自 C 标准(6.5.2.5 复合文字)

3 由带括号的类型名称后跟用大括号括起来的初始化器列表组成的后缀表达式是复合文字。它提供了一个未命名的对象,其值由初始化列表给出。

另一种方法是分配动态分配对象的每个数据成员。例如

root->data   = 0;
root->lchil  = NULL;
root->rchil  = NULL;
root->parent = NULL;

至于这个说法

*root= {0, NULL, NULL, NULL};

那么从C的角度来看它是无效的。您只能在声明中使用大括号初始化。

如果您将程序编译为 C++ 程序,则该语句将有效。

于 2017-04-17T19:04:07.267 回答
4

这里使用的语法不是类型转换,而是复合文字。这些在C 标准的第 6.5.2.5 节中定义:

3由带括号的类型名称后跟用大括号括起来的初始值设定项列表组成的后缀表达式是 复合文字。它提供了一个未命名的对象,其值由初始化列表给出。

当分配给struct一个整体时,复合文字是必要的。

在这种情况下,您不需要此语法:

int *a= malloc(sizeof(int));
*a= 4;

因为*a有类型int并且4是一个简单的整数常量,可以直接赋值给*a.

另请注意,涉及指针的事实是无关紧要的。在这种情况下,您需要执行相同的操作:

node_bst root;
root= (node_bst){0, NULL, NULL, NULL};

这不同于:

node_bst root = {0, NULL, NULL, NULL};

前一种情况是赋值,而后一种情况是初始化。初始化只能在定义变量时进行(甚至在函数之外),而赋值可以随时进行。

初始化的语法(参见标准的第 6.7.9 节)只允许使用大括号括起来的值列表,而赋值则需要复合文字。

此外,如评论中所述,您仍然可以在初始化中使用复合文字,并且复合文字的生命周期为您可以获取其地址的当前范围。

这是一个有趣的例子:

char *str = (char[]){ "My string" };
str[3] = 'S';

在这里,复合文字正在被修改,这是允许的。但如果你这样做:

char *str = "My string";
str[3] = 'S';

相反,您将尝试修改字符串文字,并且很可能会出现分段错误。

于 2017-04-17T19:16:49.510 回答