C 在不返回分配的指针的情况下,这段代码是如何工作的?

C 在不返回分配的指针的情况下,这段代码是如何工作的?,c,return,binary-tree,return-value,C,Return,Binary Tree,Return Value,我正在使用这个特殊的代码创建一个二叉树,代码本身也在工作,但我似乎缺乏理解代码的关键概念 节点结构 typedef struct node{ int data; struct node *left,*right; }* Node; create函数 Node create(){ Node root = (Node) malloc(sizeof(struct node)); int x; scanf("%d",&x); if(x==-1) //cr

我正在使用这个特殊的代码创建一个二叉树,代码本身也在工作,但我似乎缺乏理解代码的关键概念

节点结构

typedef struct node{
  int data;
  struct node *left,*right;
}* Node;
create函数

Node create(){
    Node root = (Node) malloc(sizeof(struct node));
    int x;
    scanf("%d",&x);
    if(x==-1) //create a null node
      return NULL;
    root->data=x;
    root->left = create();
    root->right = create();
}
函数的调用是

Node root=create();
在create函数中,没有指向正在传递的根节点的指针。
在该函数中,将创建一个新节点,并分配该节点的内存和值。但是代码中没有指向正在传递或返回的新创建节点的指针

那么这个代码是如何工作的呢


PS:返回类型为Node。我认为这是相关的,但我不知道为什么会这样。它也可能是空的,因为没有返回值。

在返回指向动态分配内存的指针之前,create()函数没有任何用处。

祝贺您,您发现了内存泄漏。函数正在堆上分配资源,但它不会将控制权交给您或释放它。

这里的问题是您忽略了
create
末尾的return语句。C编译器在这方面的许可度令人惊讶,并且通常编译得很好(尽管有警告。您确实启用了警告,不是吗!?)

函数很可能返回寄存器或堆栈帧中的任意值,幸运的是这是
root
的地址,因此一切正常(请参阅)


如果添加
返回根目录在函数结束时,除了未定义的行为之外,它应该继续工作。

好的,首先,这个函数中有一个明确而直接的错误

Node create(){
    Node root = (Node) malloc(sizeof(struct node));
    int x;
    scanf("%d",&x);
    if(x==-1) //create a null node
      return NULL;
    root->data=x;
    root->left = create();
    root->right = create();
}
编译器应该向您指出的:

test.c: In function ‘create’:
test.c:18:1: warning: control reaches end of non-void function [-Wreturn-type]
(如果您的编译器毫无怨言地使用了这段代码,请打开警告。默认情况下,GCC尤其过于宽松;如果您使用的是GCC,则应始终为其提供
-Wall
选项,并且可能还会提供更多的
-Wsomething
选项。)

错误只是缺少最后一行:

return root;
现在,你真正的问题似乎是“我知道有一个bug,但为什么它能工作?”在我开始之前,我需要你承认你明白,如果它能工作,它只会在计算机上和你现在正在使用的编译器上意外地工作<代码>创建
有我们称之为“未定义的行为”,这意味着它可以做任何事情,包括看起来工作正常,但也会出现灾难性的失败

有一个合理的解释解释为什么它可以工作:在许多CPU上,用于返回指针的寄存器也是一个方便的临时寄存器,因此在编译语句时

root->right = create();
编译器可以将
root
放在返回值寄存器中,以便执行内存访问
root->right=…
。由于在该点之后没有操作,生成的机器代码“从末尾掉下来”,没有在该寄存器中正式输入返回值,也没有从该寄存器中删除局部变量
root
。因此,它似乎起了作用


顺便说一句,此函数中还有很多错误:

Node create(){
这有一个几乎无害的语义错误和一个风格错误;应该写下来

Node create(void)
{
在C语言中,由于历史原因,必须编写
(void)
,而不是
()
,以定义不带参数的函数。从技术上讲,这是定义一个接受未指定数量参数的函数,这意味着如果使用参数调用
create
,编译器不会抱怨

在C语言中,与您可能熟悉的其他一些语言不同,函数定义的开头大括号应该始终放在它自己的行上

    Node root = (Node) malloc(sizeof(struct node));
在C语言中(与C++不同),不要强制转换malloc的返回值。这是不必要的,如果您不这样做,编译器将在您忘记包含
stdlib.h
时提醒您,因此您的指针将被截断为
int
的宽度

    int x;
    scanf("%d",&x);
。此外,您没有检查输入错误

    if(x==-1) //create a null node
      return NULL;
当x等于-1时,这会泄漏刚刚分配的节点。此检查应在调用
malloc
之前进行

此外,缩进不一致:第一级使用4-空格缩进,第二级使用2-空格缩进。使用什么缩进样式并不重要,但选择一种样式并在整个程序中坚持它非常重要


(而且不要使用制表符,因为缩进制表符的程序对每个人来说都不一样。8-空格缩进可以,但实际上应该是8个空格。)

此函数已被破坏。它没有返回它应该返回的值。这段代码有效吗?创建callIt无效后,如何使用和测试root的值。它是未定义的。它可能会意外工作。请给我们一个这个实现的链接…呃-除了其他人指出的实际问题之外,节点创建和输入应该是单独的操作。另外,您正在创建一个无序的树,它。。。似乎不是很有用。旁注:永远不要使用指针。这只会给读者带来麻烦,并使好的编码风格成问题。@EugeneSh。这就是我评论的原因,它应该返回指向动态分配内存的指针。啊,好的,我已经用其他方式解释了答案。无论如何,这段代码正在调用UB。但是我可以在create()函数之外访问它。这样可以吗?不可以。如果您真的可以像这样访问节点结构,那么您正在处理未定义的行为。幸运的是,结果是all@savram:更像是不走运,因为这段代码欺骗OP相信它工作正常,直到它决定不正常为止。我确实启用了警告,但在编译期间没有收到任何警告