bool型返回值函数,没写return语句的时候返回啥?

今天因为漏写了一个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;
}

程序输出:


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之间的关系也是如此。

  • 9
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Swifter-Swift 开发者必备 Tips (第四版),王巍版本 柯里化 (Currying) Swift 里可以将方法进行柯里化 (Currying),这是也就是把接受多个参数的方法进行一些变形,使其更加灵活的方法。函数式的编程思想贯穿于 Swift 中,而函数的柯里化正是这门语言函数式特点的重要表现。 举个例子,下面的函数简单地将输入的数字加 1: func addOne(num: Int) -> Int { return num + 1 } 这个函数所表达的内容非常有限,如果我们之后还需要一个将输入数字加 2,或者加 3 的函数,可能不得不类似地去定义返回为 num + 2 或者 num + 3 的版本。有没有更通用的方法呢?我们其实可以定义一个通用的函数,它将接受需要与输入数字相加的数,并返回一个函数返回函数将接受输入数字本身,然后进行操作: func addTo(_ adder: Int) -> (Int) -> Int { return { num in return num + adder } } 有了 addTo,我们现在就能轻易写出像是 addOne 或者 addTwo 这样的函数了: let addTwo = addTo(2) // addTwo: Int -> Int let result = addTwo(6) // result = 8 再举一个例子,我们可以创建一个比较大小的函数: func greaterThan(_ comparer: Int) -> (Int) -> Bool { return { $0 > comparer } } let greaterThan10 = greaterThan(10); greaterThan10(13) // => true greaterThan10(9) // => false 柯里化是一种量产相似方法的好办法,可以通过柯里化一个方法模板来避免写出很多重复代码,也方便了今后维护。 举一个实际应用时候的例子,在 Selector 一节中,我们提到了在 Swift 中 Selector 只能使用字符串在生成。这面临一个很严重的问题,就是难以重构,并且无法在编译期间进行检查,其实这是十分危险的行为。但是 target-action 又是 Cocoa 中如此重要的一种设计模式,无论如何我们都想安全地使用的话,应该怎么办呢?一种可能的解决方式就是利用方法的柯里化。Ole Begemann 在这篇帖子里提到了一种很好封装,这为我们如何借助柯里化,安全地改造和利用 target-action 提供了不少思路。 protocol TargetAction { func performAction() } struct TargetActionWrapper: TargetAction { weak var target: T? let action: (T) -> () -> () func performAction() -> () { if let t = target { action(t)() } } } enum ControlEvent { case TouchUpInside case ValueChanged // ... } class Control { var actions = [ControlEvent: TargetAction]() func setTarget(target: T, action: @escaping (T) -> () -> (), controlEvent: ControlEvent) { actions[controlEvent] = TargetActionWrapper( target: target, action: action) } func removeTargetForControlEvent(controlEvent: ControlEvent) { actions[controlEvent] = nil } func performActionForControlEvent(controlEvent: ControlEvent) { actions[controlEvent]?.performAction() } } 模式匹配 在之前的正则表达式中,我们实现了 =~ 操作符来完成简单的正则匹配。虽然在 Swift 中没有内置的正则表达式支持,但是一个和正则匹配有些相似的特性其实是内置于 Swift 中的,那就是模式匹配。 当然,从概念上来说正则匹配只是模式匹配的一个子集,但是在 Swift 里现在的模式匹配还很初级,也很简单,只能支持最简单的相等匹配和范围匹配。在 Swift 中,使用 ~= 来表示模式匹配的操作符。如果我们看看 API 的话,可以看到这个操作符有下面几种版本: func ~=(a: T, b: T) -> Bool func ~=(lhs: _OptionalNilComparisonType, rhs: T?) -> Bool func ~=(pattern: I, value: I.Bound) -> Bool 从上至下在操作符左右两边分别接收可以判等的类,可以与 nil 比较的类,以及一个范围输入和某个特定值,返回值很明了,都是是否匹配成功的 Bool 值。你是否有想起些什么呢..没错,就是 Swift 中非常强大的 switch,我们来看看 switch 的几种常见用法吧: 可以判等的类的判断 let password = "akfuv(3" switch password { case "akfuv(3": print("密码通过") default: print("验证失败") } 对 Optional 的判断 let num: Int? = nil switch num { case nil: print("没值") default: print("\(num!)") } 对范围的判断 let x = 0.5 switch x { case -1.0...1.0: print("区间内") default: print("区间外") } 这并不是巧合。没错,Swift 的 switch 就是使用了 ~= 操作符进行模式匹配,case 指定的模式作为左参数输入,而等待匹配的被 switch 的元素作为操作符的右侧参数。只不过这个调用是由 Swift 隐式地完成的。于是我们可以发挥想象的地方就很多了,比如在 switch 中做 case 判断的时候,我们完全可以使用我们自定义的模式匹配方法来进行判断,有时候这会让代码变得非常简洁,具有条理。我们只需要按照需求重载 ~= 操作符就行了,接下来我们通过一个使用正则表达式做匹配的例子加以说明。 首先我们要做的是重载 ~= 操作符,让它接受一个 NSRegularExpression 作为模式,去匹配输入的 String: func ~=(pattern: NSRegularExpression, input: String) -> Bool { return pattern.numberOfMatches(in: input, options: [], range: NSRange(location: 0, length: input.characters.count)) > 0 } 然后为了简便起见,我们再添加一个将字符串转换为 NSRegularExpression 的操作符 (当然也可以使用 StringLiteralConvertible,但是它不是这个 tip 的主题,在此就先不使用它了): prefix operator ~/ prefix func ~/(pattern: String) -> NSRegularExpression { return NSRegular[removed]pattern: pattern, options: nil, error: nil) } 现在,我们在 case 语句里使用正则表达式的话,就可以去匹配被 switch 的字符串了:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值