• bool值一定为0或者1吗


    一、bool值的范围
    根据C++语言中的说明,一个bool值可能的值只有两个,要么是0,要么是1。但是对于一个未初始化的bool值变量,它的取值范围却要丰富多彩的多,它的取值范围应该是可以为任意的一个单字节整数。测试的程序非常简单,大家随便打印一下一个未初始化的bool变量的值就应该可以看到各种各样的初始化值。
    二、测试代码
    [tsecer@Harry boolval]$ cat boolval.cpp 
    int level = 0;

    #include <stdio.h>

    int recursive(bool b_var)
    {
        if (level++ > 1024*20 )
            return 0 ;
        bool b_localvar;这个变量是木有初始化的
        printf("%d ",b_var);
        recursive(b_localvar);

    }
    int main()
    {
        recursive(1);
    }
    [tsecer@Harry boolval]$ g++ boolval.cpp -o 
    a.out            boolval.cpp      boolval.cpp.exe  
    [tsecer@Harry boolval]$ g++ boolval.cpp -o boolval.cpp.exe 
    [tsecer@Harry boolval]$ ./boolval.cpp.exe | more
    1    8    8    8    8    8    8    8    8    8    8    8    88    8    8    8    8    8    8    8    8    8    8    8    88    8    8    8    8    8    8    8    8    8    8    8    88    8    8    8    8    8    8    8    8    8    8    8    88    8    8    8    8    8    8    8    8    8    8    8    88    8    8    8    8    8    8    8    8    8    8    8    88    8    8    8    8    8    8    8    8    8    8    8    88    8    8    8    8    8    8    8    8    8    8    8    8
    这个命令一直输出,输出到最后这个输出结果一直是这个非常吉利的8这个数字。按照正常来说,操作系统给用户第一次映射的物理页面是执行过清零动作的,所以所以也就是说输出的所有结果应该是另一组为0的。我以为这里是乱码,但是在打印了5个物理页面之后,打印的内容依然还是这个8,这就有些诡异的。
    三、这里为什么不是0也不是1
    [tsecer@Harry boolval]$ gdb boolval.cpp.exe 
    GNU gdb (GDB) Fedora (7.0-3.fc12)
    Copyright (C) 2009 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
    and "show warranty" for details.
    This GDB was configured as "i686-redhat-linux-gnu".
    For bug reporting instructions, please see:
    <http://www.gnu.org/software/gdb/bugs/>...
    Reading symbols from /home/tsecer/CodeTest/perltest/boolval/boolval.cpp.exe...(no debugging symbols found)...done.
    (gdb) disas recursive
    Dump of assembler code for function _Z9recursiveb:
    0x08048474 <_Z9recursiveb+0>:    push   %ebp
    0x08048475 <_Z9recursiveb+1>:    mov    %esp,%ebp
    0x08048477 <_Z9recursiveb+3>:    sub    $0x38,%esp
    0x0804847a <_Z9recursiveb+6>:    mov    0x8(%ebp),%eax
    0x0804847d <_Z9recursiveb+9>:    mov    %al,-0x1c(%ebp) 传递给recursive的bool值放入一个byte结构中
    0x08048480 <_Z9recursiveb+12>:    mov    0x80497e4,%eax
    0x08048485 <_Z9recursiveb+17>:    cmp    $0x5000,%eax
    0x0804848a <_Z9recursiveb+22>:    setg   %dl
    0x0804848d <_Z9recursiveb+25>:    add    $0x1,%eax
    0x08048490 <_Z9recursiveb+28>:    mov    %eax,0x80497e4
    0x08048495 <_Z9recursiveb+33>:    test   %dl,%dl
    0x08048497 <_Z9recursiveb+35>:    je     0x80484a4 <_Z9recursiveb+48>
    0x08048499 <_Z9recursiveb+37>:    mov    $0x0,%eax
    0x0804849e <_Z9recursiveb+42>:    mov    %eax,%edx
    0x080484a0 <_Z9recursiveb+44>:    mov    %edx,%eax
    0x080484a2 <_Z9recursiveb+46>:    jmp    0x80484c4 <_Z9recursiveb+80>
    0x080484a4 <_Z9recursiveb+48>:    movzbl -0x1c(%ebp),%eax
    0x080484a8 <_Z9recursiveb+52>:    mov    %eax,0x4(%esp)
    0x080484ac <_Z9recursiveb+56>:    movl   $0x80485b4,(%esp)
    0x080484b3 <_Z9recursiveb+63>:    call   0x80483a0 <printf@plt>
    0x080484b8 <_Z9recursiveb+68>:    movzbl -0x9(%ebp),%eax
    0x080484bc <_Z9recursiveb+72>:    mov    %eax,(%esp)
    ---Type <return> to continue, or q <return> to quit---
    0x080484bf <_Z9recursiveb+75>:    call   0x8048474 <_Z9recursiveb>
    0x080484c4 <_Z9recursiveb+80>:    leave  
    0x080484c5 <_Z9recursiveb+81>:    ret    
    End of assembler dump.
    (gdb) 
    从汇编代码中可以看到,其中bool变量是存在一个字节为单位来存储的,汇编代码首先把一个字节放入-0x1c(%ebp)中的,高3字节单位全部清零。所以参数中的字节内容的取值范围是0到255字节的范围都是有可能的。
    四、为什么一直是8而不是0
    我当场拿出纸笔比划了一下,情况可能是这样的:
    秘密在于递归执行recursive函数之前,函数还调用了printf函数,该函数将会提前污染堆栈空间中的内容,而整个8是一个0x80地址的最高的一个字节,大家知道,在通常的桌面系统中,用户态的堆栈空间是从2G开始的,也就是0x80开始的堆栈地址。为了便于分析,我这里使用了静态链接的版本分析:
    0x0804828c <_Z9recursiveb+48>:    movzbl -0x1c(%ebp),%eax
    0x08048290 <_Z9recursiveb+52>:    mov    %eax,0x4(%esp)
    0x08048294 <_Z9recursiveb+56>:    movl   $0x80b006c,(%esp)
    0x0804829b <_Z9recursiveb+63>:    call   0x8054ad0 <printf> 此处call返回值占用一个int
    0x080482a0 <_Z9recursiveb+68>:    movzbl -0x9(%ebp),%eax 这个是发送给子函数的bool值的参数。我们现在分析一下之前调用的printf是如何确定的修改这个值的
    0x080482a4 <_Z9recursiveb+72>:    mov    %eax,(%esp)
    ---Type <return> to continue, or q <return> to quit---
    0x080482a7 <_Z9recursiveb+75>:    call   0x804825c <_Z9recursiveb>
    0x080482ac <_Z9recursiveb+80>:    leave  
    0x080482ad <_Z9recursiveb+81>:    ret    
    printf的代码
    (gdb) x/30i 0x8054ad0
    0x8054ad0 <printf>:    push   %ebp push占用一个int。
    0x8054ad1 <printf+1>:    mov    %esp,%ebp
    0x8054ad3 <printf+3>:    sub    $0xc,%esp最后一个esp指向的int就是将要使用的bool值
    0x8054ad6 <printf+6>:    lea    0xc(%ebp),%eax
    0x8054ad9 <printf+9>:    mov    %eax,0x8(%esp)
    0x8054add <printf+13>:    mov    0x8(%ebp),%eax
    0x8054ae0 <printf+16>:    mov    %eax,0x4(%esp)
    0x8054ae4 <printf+20>:    mov    0x80d3d80,%eax
    0x8054ae9 <printf+25>:    mov    %eax,(%esp)
    0x8054aec <printf+28>:    call   0x8063960 <vfprintf> call在堆栈中占用一个int。我们看到的这个地址就是这里call调用的时候被CPU直接压入堆栈中的vfprintf函数的地址,而它的最高字节为0x08,所以显示的8就是此处的8
    0x8054af1 <printf+33>:    leave  
    0x8054af2 <printf+34>:    ret    
    最后的描述可能比较模糊,因为清楚的描述可能需要画图,那样会非常直观,只是我觉得没有这个必要,有兴趣的同学自己验证一下这个过程。

  • 相关阅读:
    JAVASCRIPT高程笔记-------JSON与AJAX
    JAVASCRIPT高程笔记-------第十章 DOM对象
    JAVASCRIPT高程笔记-------第八章 浏览器BOM对象
    JAVASCRIPT高程笔记-------第 七章 函数表达式
    JAVASCRIPT高程笔记-------第六章 面向对象的程序设计
    JAVASCRIPT高程笔记-------第五章 引用类型
    javascript高程笔记-------第四章 变量、作用域和内存问题
    redis 从0 到 1 键值相关命令 服务器相关命令
    SnpHub搭建(三) | 手动处理数据后的配置文件填写
    SnpHub搭建 | 数据处理中可能出现的问题
  • 原文地址:https://www.cnblogs.com/tsecer/p/10487397.html
Copyright © 2020-2023  润新知