用c语言求两个整型变量的和,问渠网-C语言深处-第二节 整型变量

一、字节

C语言的整形变量有如下几个类型:

char        字符变量         1byte

short       短整型变量      2byte

int           整型变量         4byte(在16位架构下的int型变量占2个byte)

long        长整型变量      4byte

long long 超长整型变量   8byte

先来看char类型的变量,char在内存中占用1个byte,也就是说它拥有8个bit位,最多可以表示256个数字,但在C语言中有符号数的最高位为符号位,0代表正数,1代表负数,所以可以使用的bit位数实际上只有7位,加上正负符号char类型变量的数字表示范围为-128 ~ 127,这也就是为什么ASCII码只有128个(-128 ~ -1保留,为其它编码使用,例如汉字等)。

注意,本教程中明确写出来的内存地址,例如0x100、0x200、0x2000等等,这些地址是为了便于读者理解而假设的地址,程序实际运行时i的地址并不会是0x100、0x200、0x2000。本教程中其它例子也是一样。

short类型变量在内存中占用2个byte,也就是16个bit位,但是在不同操作系统编译器下所编译出来的程序这两个byte的顺序是不一样的,如下图:

Ha1cotFOnIQAAAABJRU5ErkJggg==

假设在内存地址0x200处定义了一个short型变量,地址0x200处byte的值为0x11,地址0x201处byte的值为0x22,如果在大尾机中这个short变量的值为0x1122,而在小尾机中这个short变量的值为0x2211。大尾机是将数据的高字节存放在低地址,而小尾机则是将数据的高字节放在高地址。同样的道理,int类型、long类型和long long类型也都与short是一样的。例如我们定义一个int型变量并为其初始化:

int i = 0x11223344;

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

假设变量i的内存地址为0x200则在小尾机中的数据存储方式为:

8CAZgIBiAgWAABoIBGP4FxRWe8JkvZBMAAAAASUVORK5CYII=

在大尾机中的数据存储方式为:

JBoPq5XSthcjZHdTpkwxX+TwzXKWRONhiW17MUJ2N2TIEPehtPeqT58+MmnSJPe5b5azJBoPS2zbixG6O6VfO3PmjHvqdOnSpZo7It8sR0k0HpbYthcjdHdKf8mqjzgd8c1yxNtHAAPhAAyEAzAQDsBAOAAD4QAMhKNKzm+FQOsIxy+0HaI9wgEYCAdgIByAgXAABsIBGP4FcCWe9OaEM9EAAAAASUVORK5CYII=

二、赋值

下面我们来看讨论两个变量赋值的问题:

如果将一个小范围的变量赋值给一个大范围变量会产生什么样的结果?

如果将一个大范围的变量赋值给一个小范围变量会产生什么样的结果?

编写一段代码,编译并运行它,看看结果是什么:

#include 

int main(int argc, char **args)

{

char i = 0x11;

short j = 0x2233;

j = i;

printf("0x%x\n", j);

return 0;

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

运行结果为:

0x11

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

也就是说一个小范围变量赋值给一个大范围变量时,将小范围变量的有效内存数据复制到大范围变量的低字节区域,并将高字节区域清零,具体的过程如下:

lckJUUmobEAAAAAElFTkSuQmCC

再来看第二个问题,大范围的变量赋值给一个小范围变量:

#include 

int main(int argc, char **args)

{

char i = 0x11;

short j = 0x2233;

i = j;

printf("0x%x\n", i);

return 0;

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

运行结果为:

0x33

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

把一个short类型的变量赋值给一个char类型的变量结果为什么是0x33呢?这是因为当一个赋值操作的目标变量范围小于源变量范围时,编译器只会把低字节的数据复制到小范围变量中,而将无法容纳的字节(高字节)自动抛弃。可以看到short j的低字节为0x33,高字节为0x22,赋值时只将低字节的0x33复制到变量char i中,而将高字节的0x22抛弃。

下面再来看一个非常有趣的赋值问题:

#include 

int main(int argc, char **args)

{

char i = 34;

short j = 129;

i = j;

printf("%d\n", i);

return 0;

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

运行结果为:

-127

结果出乎我们的意料,但却是正确的运行结果。来看一下这个赋值运算的原理:

IgY9+3jfjYRnxsIz62ER+bcukzW8wiBQAAAAD8QJECAAAAwBSKFAAAAACmUKQAAAAAMIUiBQAAAIApFCkAAAAATKFIAQAAAGAKRUoKSpUq5XcX4IH42EZ8bCM+thEf24iPbcQnGChSkpRLX6YDAAAAZBNFCgAAAABTKFIAAAAAmEKRAgAAAMAUihQAAAAAplCkAAAAADCFIgUAAACAKRQpAAAAAEz5f7P8R9RxH4CcAAAAAElFTkSuQmCC

可以看到i被j赋值之后值变为了129,它的2进制表示为1000 0001,因为i是一个有符号数,所以它的最高位为符号位,所以1000 0001的值正好为-127。

三、补码

下面就来了解一下负数的表示方式,在计算机中数通常采用补码的形式来表示,正数和0的补码为其本身,负数的补码为其绝对值取反再加1。

10进制数

绝对值

原码(不包括符号位)

反码(不包括符号位)

补码(不包括符号位)

实际补码(包括符号位)

-3

3

000 0011

111 1100

111 1101

1111 1101

-23

23

001 0111

110 1000

110 1001

1110 1001

-127

127

111 1111

000 0000

000 0001

1000 0001

上面例子中char i的值为-127,其补码为1000 0001正好是short j的129的原码。所以char i = 129;的结果是-127。

负数在计算机中通常以补码的形式表示,那为什么要采用补码来表示负数呢?我们来看一下9的补码为0000 1001。在计算机中为了能让CPU对所有的数,无论是正数还是负数都能够快速的进行正确的加减运算,例如要让CPU能够快速的识别并运行9 + (-9) = 0这样的运算法则。也就是说让0000 1001加上一个数(-9)能让结果快速的变为0,这个过程分为两步:

1.让9加上其本身的反码,即得到一个全1的结果:

0000 1001

+ 1111 0110

1111 1111

2.在全1的结果基础上再加1,即出现多米诺骨牌效应,所有位均为0,第9个bit位的1溢出,所以结果为0:

1111 1111

+ 0000 0001

0000 0000

把上面两步的运算合并成一步运算,即一个正数加上其反码再加1结果为0,为了能让CPU能够快速的计算正负数的加减法,所以负数通常采用“补码”的形式来表示。

再来看看两个负的加法:

(-9) + (-7) = -16

-9的补码为1111 0111,-7的补码为1111 1001,两个负数的加法结果为:

1111 0111

+ 1111 1001​

1111 0000

1111 0000取其补码(符号位不变)的结果为1001 0000 = -16

四、位移运算

无符号整数的位移运算非常简单,每向左位移1位相当于将其乘2,每向右位移1位相当于将其除2。例如,将无符号整数7向左位移3次再向右位移4次:

#include 

int main(int argc, char **args)

{

unsigned int i = 7;

i <<= 1;

printf("%u\n", i);

i <<= 1;

printf("%u\n", i);

i <<= 1;

printf("%u\n", i);

i >>= 1;

printf("%u\n", i);

i >>= 1;

printf("%u\n", i);

i >>= 1;

printf("%u\n", i);

i >>= 1;

printf("%u\n", i);

return 0;

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

运行结果为:

14

28

56

28

14

7

3

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

需要注意的是,当向右位移时如果这个数是奇数,它的最低位将会被抛弃,也就是说7向右位移1位时0000 0111变为0000 0011。上面所说的移位运算只限于无符号整数,对于有符号型整数的运算法则不适用。因为当位移运算时可能会影响到其符号位,使其结果并不一定是乘2或除2。

再来看一个关于有符号数位运算的例子:

#include 

int main(int argc, char **args)

{

char i = 11;

i <<= 4;

printf("%d\n", i);

i = -7;

i <<= 5;

printf("%d\n", i);

i = -7;

i >>= 1;

printf("%d\n", i);

return 0;

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

运行结果为:

-80

32

-4

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

结果很奇怪,但这是正确的运算结果,来分析一下这3个位移运算:

11的补码为0000 1011,向左移动4位结果为1011 0000(左移时总是将最低位补0),它的最高位为1所以它表示的是一个负数,将这个数取反再加1,得到它的数值为-80。

-7的补码为1111 1001,向左移动5位结果为0010 0000。它的最高位为0所以它表示的是一个正数,值为32。

-7的补码为1111 1001,向右移动1位1111 1100(右移时如果最高位为1,右移结束后再将最高位用1补齐),它的最高位为1所以它表示的是一个负数,将这个数取反再加1,得到它的数值为-4。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值