[栖岸计划]智英带你学C语言(第...

物理
[栖岸计划]智英带你学C语言(第三章)

用户头像
暂缓开通站点增加啦! 更新于2026-6-9 10:48:09

返回目录:[栖岸计划]智英带你学C语言(新版回归)

第三章 变量

3.1 整型变量

C语言中的整型变量有很多种,区别主要在于能否表示负数(即是否有正负号),以及表示整数的大小范围。根据是否有符号,我们可以分为signed和unsigned。通常情况下,signed可以省略。

我们知道,计算机以二进制的方式存储数据。所以,使用 $n$ 比特的存储空间就可以表示出 $2^n$ 种不同的数。下面给出常用的几种整型变量,它们通常需要的存储空间如下:

char:1字节,即8比特;

short:2字节,即16比特;

int:4字节,即32比特;(注:一些古老的机器上,int只占2字节,不过现在基本见不到了)

long:存在不同标准,一般为4/8字节,即32/64比特;

long long:8字节,即64比特。

对于无符号整数,由其二进制表示推算表示的整数方法如下:

将最低位记为第0位,从低往高,如果第 $i$ 位的值是1,那么表示的数就应该要加上 $2^i$ 。

例如,我们考虑

unsigned char a = 0b10011011;

这里0b表示后面的内容是二进制数。从低往高,第0, 1, 3, 4, 7位的值为1,故a表示的整数就是

 $2^0+2^1+2^3+2^4+2^7=155$ 。

我们验证一下:

image.png

(图中的等号表示给变量赋值)

对于有符号整数,情况要更为复杂,下面详细展开:

我们规定,有符号整数的最高位为符号位,值为0表示为正数,值为1表示为负数。如果变量为 $n$ 位,那么绝对值不超过 $2^{n-1}-1$ 的整数,均可以用除了最高位以外的部分进行编码,方法同上。

例如,因为7是正数,所以其符号位为0,而 $7=2^0+2^1+2^2$ ,其对应的编码就是00000111;-7是负数,其符号位为1,其对应的编码就是10000111。这种编码被称为原码。

实际的运算过程中,原码会带来很大的不便。例如,7+(-7)=0,这点在数学上是显然的;但是我们比较这三个数的原码:

7:00000111

-7:10000111

0:00000000

我们发现,直接类比竖式计算,对7和-7的原码相加,得到的结果并不是0。为了避免类似问题的出现,引入反码和补码:

对于正数,反码和补码都等于原码;

对于负数,反码是将原码除符号位的部分按位取反,补码等于反码加1。

例如,前面提到,-7的原码是10000111,将除了最高位的每一位按位取反,得到其反码为11111000;再将其加上1,就得到其补码为11111001。我们与7相加:

7:00000111

-7:11111001

类比竖式,相加之后每一位都是0。(最高位当然不会再往上进位,否则就不是“最高位”了。)可以说明,对于不超出存储范围的计算,结果都是正确的。这点对计算机非常有利。所以,计算机内一般都使用补码存储有符号整数。

对比上面,如果考虑

char a = 0b10011011;

那么我们由补码反推原码,并得到对应的整数。将上述过程倒过来即可。

将数减去1,得到10011010;除了最高位,每位按位取反,得到11100101;再将除了最高位,其他值为1的位表示的数加在一起:

 $2^0+2^2+2^5+2^6=101$ 。

所以a存储的数就是-101。

我们验证一下:

image.png

没有问题。

使用上面的方法,有一个小问题,即:符号位是1,但其余所有位都是0的编码不表示任何一个数。因此我们规定:对于这种情况,如果是 $n$ 位整数,表示的数就是 $-2^{n-1}$ 。

例如,我们考虑

char a = 0b10000000;

char是8位整数,所以a存储的值就是 $-2^7=-128$ 。验证一下:

image.png

因此,我们通过这种方式,用n位整数表示了 $-2^{n-1}$ 到 $2^{n-1}-1$ 之间的所有整数。

在头文件limits.h中,存有无符号整数能存储的最大值,以及有符号整数能存储的最大和最小值。通过如图的程序,可以查看这些整型变量的存储范围:

image.png

输出结果如下:

CHAR_MAX: 127

CHAR_MIN: -128

SHRT_MAX: 32767

SHRT_MIN: -32768

INT_MAX: 2147483647

INT_MIN: -2147483648

LONG_MAX: 2147483647

LONG_MIN: -2147483648

LLONG_MAX: 9223372036854775807

LLONG_MIN: -9223372036854775808

UCHAR_MAX: 255

USHRT_MAX: 65535

UINT_MAX: 4294967295

ULONG_MAX: 4294967295

ULLONG_MAX: 18446744073709551615

(LONG_MAX, LONG_MIN, ULONG_MAX可能受标准影响)

一般来讲,我们要控制数据以及它们运算得到的值不能超出上面说的范围。例如,对两个int类型变量作加法时,就必须尽量让和不大于INT_MAX,不小于INT_MIN。那如果突破了这个界限会发生什么呢?我们来看一下:

image.png

当我们把INT_MAX加上1之后,得到的是一个很小的负数,刚好是INT_MIN。类似地,把LLONG_MAX加上1之后,得到的是LLONG_MIN。这仅仅是加法,如果是乘法,我们可能会得到一些看似没有意义的数值:

image.png

当然,我们知道,INT_MAX乘以它自己绝对不可能等于1。

这种现象出现的原因是什么呢?因为存储数据范围的限制,当数学${\displaystyle}$运算的结果超出范围时,程序运算得到的输出与之并不相等。超出存储范围的更高位,在计算结果中完全“消失”了。我们将这种现象称为“溢出”。绝大多数情况下,这一现象都对程序编写是不利的,可能会导致许多超出预期的行为。因此,在编程时,一定要选取合适的变量类型存储数据。

(当然,有时我们也会利用溢出这一特性来简化很多问题。例如,当我们不关心某个巨大数字的具体值,只关心它模 $2^{32}$ 的余数的话,我们就可以使用unsigned int来存储。这么做可行的原因是很好理解的,大家可以自己分析)

接下来的内容是关于整型变量的运算。我们知道,我们可以对整数进行加法、减法、乘法。计算结果与顺序与数学上的计算相同。对于这些计算,需要注意以下几点:

①溢出。开始已经提到过了,不再赘述。

②隐式变量类型转换。为了让不同类型的整型变量也能进行运算,在运算过程中,变量的类型会发生转换。对于仅限于整型变量之内的运算,遵循以下规则:

1)将存储范围小于int的类型(如short,unsigned short,char,unsigned char)提升为int;

对于这一点,我们看一个例子:

image.png

分析一下,为什么结构完全一样,但只有第一句话被打印?

先解释下面为什么不被打印。因为我们将c赋值为INT_MAX,将它加上1之后,发生了溢出,导致返回值小于0。因此第二句话没有被打印。

问题来了:照理来讲,同样的结构,同样的运算,a+b也应该发生溢出,为什么第一句话被打印了?

答案:因为计算a+b时,实际进行的步骤如下:

先将a提升为int型变量,即将式子中的a替换为int类型的0x0000007f,而非char类型的0x7f;

再将b提升为int型变量,即将式中的b替换为int类型的0x00000001;

最终相加,得到的结果是0x00000080,即128。这当然是个正数,因此第一句话被打印。

在参与运算的所有数都是整数时,这一类型转换的优先级最高。

2)如果提升后两变量类型不同,将较低级的类型转为较高级的类型。具体准则如下:

如果均为有符号整型,那么long long>long>int;

如果均为无符号整型,那么unsigned long long>unsigned long>unsigned;

如果有有符号整型,有无符号整型,若有符号整型覆盖了无符号整型的所有数,则有符号整型更高级,否则无符号整型更高级。

这套规则会有如下结果(需要考虑long和unsigned long是32位还是64位):

若long和unsigned long为32位:unsigned long long>long long>unsigned long>unsigned>long>int;

若为64位:unsigned long long>unsigned long>long long>long>unsigned>int。

我们举一个例子:

image.png

image.png

这个程序在Windows系统(long为32位)下面会有输出,但在Linux系统(long为64位)下不会。

因为long为32位的话,long无法覆盖unsigned的所有取值范围(不小于 $2^{31}$ ,不大于 $2^{32}-1$ 的数无法被long覆盖)。由前面的规则,long被提升为unsigned,转换为一个很大的整数,一般是 $2^{32}-20$ 。这个数加上10之后,不会发生溢出,从而和为正数,打印语句执行。

但long为64位的话,long可以覆盖unsigned的全部取值范围,由前面的规则,unsigned被提升为long,结果只是普通的-20+10=-10,当然小于0,什么也不发生。

收起
11
9
共1条回复
时间正序
用户头像
今朝
26天前
这么好的帖子为什么没有评论?