今天因为漏写了一个return语句,g++又没开warning,结果就悲剧了,调用的时候出现了奇怪的现象,于是就测试了一把到底没写return的时候返回什么东西。
例程:
#include <iostream>
#include <vector>
using namespace std;
bool func()
{
int i=10;
i++;
}
int main()
{
bool a = func();
bool b=true;
bool c=100;
cout<<!(a)<<endl;
cout<<!(b)<<endl;
cout<<!(c)<<endl;
cout<<(a)<<endl;
cout<<(b)<<endl;
cout<<(c)<<endl;
cout<<!10<<endl;
cout<<func()<<endl;
if(func()==true)
cout<<"true"<<endl;
if(func()==false)
cout<<"false"<<endl;
if(!func()==true)
cout<<"true"<<endl;
if(!func()==false)
cout<<"false"<<endl;
}
程序输出:
5
0
0
4
1
1
0
64
true
false
true
false
多次运行时上面的除0,1外的数字是随机的。
而且从if语句的判断当中我们可以看到当我们那这个没有返回语句的函数直接来判断时,他已经没有了bool变量的特性了。
再跟true,false的比较中他们都是成立的。这个时候但我们不小心使用了if else结构的时候,就永远只会执行if而不会到else的语句中了。
所以当bool返回值的函数,在没写返回语句的时候,他并不会默认地返回一个true和false,而是一个无定义的行为,会导致后面的判断出错。
那到底是怎么出现这样不合逻辑的现象的呢,让我们继续实验来一窥其中的奥秘。
要想知道其中的过程,我们首先用g++ -S来编译这份代码的汇编代码来看看。
整体汇编代码过长就不全部贴上来了,我们只来关注我们的if语句(第一个if语句和第二个)的汇编。
.L7: //第一个if语句
call _Z4funcv //首先调用func函数
xorl $1, %eax //将返回结果%eax与1作异或操作
testb %al, %al //判断al寄存器是否为0,这里的test操作就是作一次and,并设置标志位ZF;关于al 与eax的关系见下面说明。
je .L8 //根据标志位ZF作跳转。
movl $.LC0, 4(%esp)
movl $_ZSt4cout, (%esp)
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp)
movl %eax, (%esp)
call _ZNSolsEPFRSoS_E
.L8: //第二个if语句,基本和1一样,但是由于和0作异或,因为作不作都一样,因而没有对应语句。
call _Z4funcv
testb %al, %al
je .L9
movl $.LC0, 4(%esp)
movl $_ZSt4cout, (%esp)
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp)
movl %eax, (%esp)
call _ZNSolsEPFRSoS_E
从上面的分析中我们可以看到,但程序没有写返回语句时,我们去那返回值时,这个值就是原来返回值寄存器(eax)里面已经存在的值,这是不确定的。比如我们这里返回了一个不为0,1的数字时,接下来我们进行bool判断时,无论与1异或,还是与0异或,都不会使改寄存器变0,因而对应的test语句也就总是成立了。
我们使用指针去修改bool变量的值也能达到上面的同样的效果。而我们直接用数字去比较的时候却没有这个效果,是因为程序在比较之前会做一次隐性的类型转换。
附 exa, ax, ah, al 寄存器的介绍:
先请看图,图看懂了就基本解决这个了疑问了。
00000000 00000000 00000000 00000000
|===============EAX===============|--32个0,4个字节,2个字,1个双字
|======AX=======|--16个0,2个字节,1个字
|==AH===|-----------8个0,1个字节
|===AL==|---8个0,1个字节
虽说EAX是32位的寄器,但其实只是在原有的8086CPU的寄存器AX上增加了一倍的数据位数而已。故而EAX与AX根本不可能独立,二者是整体与部分的关系。
对EAX直接赋值,若更改了低16位自然会改变了AX值,而AX又可以影响EAX整体。而AH,AL寄存器和AX之间的关系也是如此。