出现的问题
在做编程题的的时候,遇到了一个很奇怪的错误,出问题的代码如下:
1 #include <cstdio> 2 using namespace std; 3 4 int main() { 5 int c; 6 bool b; 7 // printf("%p %p", &c, &b); // c的地址是:66fe1c,b的地址是:66fe1b 8 scanf("%d %d", &c, &b); 9 printf("c = %d b = %d", c, b); 10 11 return 0; 12 }
这是运行的结果:
是不是觉得很奇怪?明明给变量c和变量b输入的是10和1,可是在输出的结果中,c的值为0。这是为什么呢?其中原因涉及到内存。大概的解释是,我通过%d占位符来为bool型变量b输入值,而系统为bool型的变量分配1个字节大小的内存,又因为通过%d输入的值是占4个字节大小的,所以由于变量b的内存不够,输入的值会占用到变量c的内存并且覆盖原来存储的值。下面,我将通过变量所对应的内存中发生的变化进行详细的解释。
详细解释
通过打印输出变量c,b的地址,我们来绘制出相应的内存条:
注意,由于我们在定义变量c,b时没有进行初始化,系统会为变量随机分配值,所以内存条应该是有相应的十六进制的值的,但图中为了方便,表示空白省略其中相应的值。
为了能够更好地表示出其中的错误,我们会输入比较大的数字。
这里我们还是先为变量c输入10,之后十进制10会转变为十六进制00 00 00 0A,倒着存储到变量c的内存中:
在为变量b输入值之前,我们需要知道,在C++中,bool型变量的字节大小为1个字节,也就是sizeof(bool) == 1,但我们在scanf函数中通过%d占位符来为变量b读取值,又知道通过占位符%d的值是占4个字节的大小的,而b变量的内存大小为1个字节,很明显,是存储不了4个字节大小的值的,所以只能通过占用变量c的内存来存储(准确来说是占用了变量c的前3个字节的内存),导致原本存储在变量c内存中的值被覆盖。
现在,我们为b变量输入180079837,再按照上述的说法,来看看内存发生了什么变化:
首先十进制180079837会转变为十六进制0A BB CC DD,倒着存储到变量b的内存中,又因为变量b的内存只有1个字节的大小不够存储,所以会在接着的3个字节大小内存来存储,这就会导致属于变量c的内存被占用,我们输入的新值覆盖了原本的值。
没错,这就是问题所在。现在我们来打印输出看看他们的值:
嗯...又是些很奇怪的值,这是怎么读出来的呢?
首先来看变量b,虽然我们为变量b输入的值是4个字节的大小,但由于变量b的所占的字节大小为1,所以系统自会只读66fe1b这个地址存放的值,十六进制DD对应着十进制的211,所以实际上b变量所存储的值是211。
然后是变量c,因为变量c是int型变量,占用字节大小为4,所以系统会从66fe1c这个地址开始往后读4个字节大小的内存,这4个字节的内存就存储着变量c的值,我们读的顺序应该是00 0A BB CC,对应着十进制703436,所以变量就存储着703436,并打印输出之。
通过,上面的解析,我们现在应该明白了一开始为什么我们输入 10 1 会出现 c = 0, b = 1 的情况了,就是下图的情况。
所以有什么解决的方法呢?首先,bool型没有专门对应的占位符。所以如果想通过scanf函数来为bool变量进行输入,可以把该变量定义为int型并通过占位符%d进行输入,又或者是通过枚举来定义。而如果是C++的话,就更简单了,用cin来对bool型变量进行输入。
常用占位符
%d:输入/输出十进制整数。
%o:输入/输出八进制整数。
%x:输入/输出十六进制整数。
%u:输入/输出无符号型整数。
%hd:输入/输出十进制短整数。
%ld:输入/输出十进制长整数。
%lld:输入/输出十进制长长整数。
%ull:输入/输出十进制无符号长长整数。
(如果要输入/输出八进制或十六进制的数,只需要把d改为o或x)
%f:输入/输出单精度浮点数。
%lf:输入输出双精度浮点数。
%e:输入/输出科学计数。
%c:输入/输出一个字符。
%s:输入/输出字符串。
%p:输入/输出地址。
参考资料
https://tieba.baidu.com/p/7250200456
《C语言程序设计现代方法-第二版》