我对C ++中的函数有一个小问题
#include <iostream>
int multiply(int x, int y)
{
int product = x * y;
}
int main()
{
std::cout << multiply(4,90) << std::endl;
return 0;
}
此代码生成输出360.好吧这可能是一个错误。我改变了价值观。 输出也相应改变了。我同意代码不应该给出编译错误。但是如果没有return语句,函数如何返回值。
编译器:GNU GCC 5.1 编译语句:g ++ file.cpp
没有使用编译标记。
现在这只是为了测试。问题在于此代码:
#include <iostream>
#include <stdlib.h>
struct tree{
int data;
struct tree *left;
struct tree *right;
};
typedef struct tree *node;
node getNode(){
node n = (node)malloc(sizeof(struct tree));
n->left = NULL;
n->right = NULL;
n->data = 0;
}
int main(){
node root = getNode();
root->data = 4;
std::cout << root->data << std::endl;
return 0;
}
此代码编译。好的同意了。产生一个警告,getNode()不返回任何内容。没关系,我必须得到运行时错误。但是代码成功生成了以下输出。
4
如何?如何将“root”分配给由另一个函数创建但未由其返回的节点?
答案 0 :(得分:5)
但是如果没有return语句,函数如何返回值?
_Z8multiplyii:
.LFB1486:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
mov rbp, rsp
.cfi_def_cfa_register 6
mov DWORD PTR [rbp-20], edi
mov DWORD PTR [rbp-24], esi
mov eax, DWORD PTR [rbp-20] <--- look here
imul eax, DWORD PTR [rbp-24] <--- and here
mov DWORD PTR [rbp-4], eax
nop
pop rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
这是你的函数在没有GCC 6优化的情况下编译的内容。它只是使用eax
来存储结果,以及它的工作原理(EAX注册表被视为结果此调用约定下的值)。 -O2
版本返回0,因为它完全有权这样做,因为未定义的行为(以一种非常有趣的方式):
_Z8multiplyii:
.LFB1511:
.cfi_startproc
rep ret
.cfi_endproc
格式良好的版本略有不同,使您更容易理解代码的工作原理:
...
mov eax, DWORD PTR [rbp-20]
imul eax, DWORD PTR [rbp-24]
mov DWORD PTR [rbp-4], eax <--- compare this
mov eax, DWORD PTR [rbp-4] <--- and this
...
我还没有触及过更为复杂(&#34;实际&#34;)的代码,但它的内容几乎相同,只是它的call malloc
被存储了进入rax
,然后字段制定者将其留在那里。
sub rsp, 16
mov edi, 24
call malloc <--- result stored in rax
mov QWORD PTR [rbp-8], rax <--- struct field setting here
mov rax, QWORD PTR [rbp-8]
mov QWORD PTR [rax+8], 0
mov rax, QWORD PTR [rbp-8]
mov QWORD PTR [rax+16], 0
mov rax, QWORD PTR [rbp-8] <--- rax contains proper pointer at the end
mov DWORD PTR [rax], 0
nop
leave
.cfi_def_cfa 7, 8
ret <--- which is then left for return
.cfi_endproc
答案 1 :(得分:2)
C ++的核心是一种非常低级的语言。函数处理的值放入堆栈和寄存器,并保留在那里直到被覆盖。相同的堆栈空间和寄存器一直被重复使用,如果您的程序包含未定义的行为,则很可能下一次计算最终使用相同的数据。
现在在这种情况下,编译器生成用于执行计算的代码,并将结果留在那里。你不会返回它们,但是它们恰好位于堆栈中的相同内存位置,或者位于相同的寄存器中,如果你确实返回了某些内容,它们就会被使用。并且调用代码不知道函数没有返回任何东西,它只信任函数签名,并获取返回值(根据使用的调用约定,如果你想google that)。
但是如果你打开优化,编译器可能会完全删除你的计算,然后你会得到垃圾结果。或者整个程序可能会崩溃。或者其他什么,这就是&#34; undefined&#34;装置
此外,如果您为您的功能添加了更多代码,或者以任何方式更改了这些代码,那么结果可能会存储在不同的位置,并且您将获得其他内容作为&#34;返回值&#34;。或者整个程序可能会崩溃。或者其他什么,这就是&#34; undefined&#34;装置
这就是你想
的原因-Wall -Wextra
适用于 gcc 和 clang ,例如)。当然,没有警告并不能保证没有任何令人讨厌的未定义行为,但它会帮助您避免最常见的情况。