6

C语言赋值-1,为何输出一个很大的数,比如255?

 3 years ago
source link: https://blog.popkx.com/c%E8%AF%AD%E8%A8%80%E8%B5%8B%E5%80%BC-1-%E4%B8%BA%E4%BD%95%E8%BE%93%E5%87%BA%E4%B8%80%E4%B8%AA%E5%BE%88%E5%A4%A7%E7%9A%84%E6%95%B0-%E6%AF%94%E5%A6%82255/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

C语言赋值-1,为何输出一个很大的数,比如255?

发表于 2019-07-28 20:07:47   |   已被 访问: 1,158 次   |   分类于:   C语言   |   暂无评论

前两天,我在我的圈子里发了一个小问题,相关的C语言代码如下,这段程序会输出什么呢?

#include <stdio.h>

int main()
{
    signed char   a = (unsigned char)-1;
    unsigned char b = a;
    int           c = a;
    int           d = b;
    printf("%d %d %d %d\n", a, b, c, d);

    return 0;
}
7e25f8a39ba191f9642eb1e73a935469.png

在分析这个问题之前,先说些题外话。有程序员认为研究这样的代码没有意义,无异于孔乙己的“茴”字有几种写法。

这个问题其实并不是我空想出来的。

最近,我的一个同事被他的C语言程序 bug 困扰了好几天,始终无法找到问题究竟出在哪里,于是找我,我看到他的代码居然混用无符号变量和有符号变量,于是就提醒他注意这个方面,后来发现果然是这个原因。他的问题涉及到比较复杂的项目,完整的复述一遍不太现实,于是我把他的问题精简一下,就构成了上述C语言代码段。

事实上,很多公司招聘时,都有一些面试题或者笔试题看起来很怪异,很不符合标准的开发规范,于是有些程序员就认为做这样的面试题是完全没有意义的,甚至觉得做这些题目是一种侮辱。

其实换个角度想想,这些题目很能考察一个人的基本功,它们很可能来自公司内部的某个项目的某次重大 bug。C语言是一门极其重视基本功的编程语言,这些题目很能查漏补缺。

现在来考虑上面这段C语言代码,我们编译并执行它,得到了下面的输出:

# gcc t.c
# ./a.out 
-1 255 -1 255

C语言程序的输出出乎了一些朋友的预料,-1 容易理解,255 是怎么回事呢?

首先要明白的是,在计算机中,整数通常采取补码的形式存储。负数的补码等于其反码+1,负数的反码符号位不变,数值为按位取反。对于 signed char 型变量,大部分C语言编译器都是由 8 个 bit 组成的,最高一个 bit 通常表示符号位。

所以对于 -1,其原码原本是 0b10000001,但是计算机内部存储该数值时,是以补码形式存储的。-1 的补码等于反码+1,也即 0b11111110 +1 = 0b11111111 = 0xff。

到这里就清楚了,变量 b 在内存里的 8 个 bit 都是 1,它是一个 unsigned char 型的变量,最高 bit 也表示数值,也即 b 等于 255。

现在再来分析变量 c 和变量 d 的值,它俩都是有符号型的 int 型。按理说,a 和 b 在内存中的布局是一样的,都是 8 个 bit 的 1,为什么传递给 c 和 d 就不一样了呢?

其实C语言在处理 c = a; 和 d = b; 这两句赋值语句时,有一个过程没有显式的表现出来,即“整形提升”。以 c=a; 为例,因为 c 和 a 的数据类型不同,所以C语言在处理赋值时,为了不丢失精度,会将 a 中的数值也强制转换为 int 型。

a 中的数值是 -1,提升为 int 型后依然是 -1,而不是 0x000000ff(255,这里假设 int 类型占用 4 字节内存空间)。至于变量 d 的值,就更简单了,就是简单的赋值而已。

本节讨论的问题虽然很简单,但是仍然有很多人做错,这其中也包含我工作多年的同事。C语言是一门极其重视基本功的编程语言,事实上,本节涉及的知识点非常基础,无非就是原码补码,以及整型提升的相关知识。

阅读更多:   C语言


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK