码迷,mamicode.com
首页 > 编程语言 > 详细

C语言笔记之数据类型(二)

时间:2015-05-01 17:30:10      阅读:192      评论:0      收藏:0      [点我收藏+]

标签:

接下来是不同类型之间的强制转换。当把一个高容量的类型强制转换为低容量的类型时,会发生截断:丢弃二进制的高位,只保留低位(二进制的左边为高位,右边为低位);而把低容量类型强制转换成高容量类型时,会发生扩展:在二进制的高位左边继续填充数字。扩展分为两类:零扩展和符号扩展。下面看代码示例。

1、截断

#include <stdio.h>

int main(void)
{
    short a = 257;
    char b = (char)a;

    printf("a = %u, b = %d\n", a, b); 
    printf("a = %#x, b = %#x\n", a, b); 

    return 0;
}
结果:

a = 257, b = 1
a = 0x101, b = 0x1
0x0101被分成两部分,高位的01和低位的01,char类型只能容纳一个字节,于是取低位的一个字节后,结果是0x01。
再看一个:

#include <stdio.h>

int main(void)
{
    short a = -24368;
    char b = (char)a;

    printf("a = %d, b = %d\n", a, b); 
    printf("a = %#x, b = %#x\n", a, b); 
    printf("a: %d, b: %d\n", sizeof a, sizeof b);

    return 0;
}
结果:

a = -24368, b = -48
a = 0xffffa0d0, b = 0xffffffd0
a: 2, b: 1
虽然截断后的结果是一致的,但是这里的printf似乎是按四个字节来打印负数的十六进制。

2、扩展

#include <stdio.h>

int main(void)
{
    unsigned short a = 63222u;
    unsigned int b = (unsigned int)a;

    printf("a = %d, b = %d\n", a, b); 
    printf("a = %#x, b = %#x\n", a, b); 
    printf("a: %d, b: %d\n", sizeof a, sizeof b);

    return 0;
}
结果:

a = 63222, b = 63222
a = 0xf6f6, b = 0xf6f6
a: 2, b: 4
显然,虽然打印出来的a和b的二进制表示相同,但是用sizeof运算符计算长度时,发现b占用了4个字节,所以b的实际十六进制为:0x0000f6f6。这表明,无符号数的扩展采用0来填充高位,这就是所谓的零扩展。

另外,我发现printf函数打印十六进制时,自动忽略高位的0。

有符号数呢?

#include <stdio.h>

int main(void)
{
    char a = -68;
    short b = (short)a;

    printf("a = %d, b = %d\n", a, b); 
    printf("a = %#x, b = %#x\n", a, b); 
    printf("a: %d, b: %d\n", sizeof a, sizeof b);

    return 0;
}
结果:

a = -68, b = -68
a = 0xffffffbc, b = 0xffffffbc
a: 1, b: 2
呃,又被吓到了,我不禁怀疑printf函数打印十六进制时存在bug:sizeof显示a的确只占一个字节,所以十六进制只打印0xbc就够了,为毛还要加上前面一堆f?同理,b的话,0xffbc就够了。。
不过至少,我们知道低容量的char向高容量的short扩展时,如果其二进制最高位是1,那么扩展后的高位用1来填充,这就是所谓的符号扩展。

我再验证一下有符号的正数:

#include <stdio.h>

int main(void)
{
    char a = 68;
    int b = (int)a;

    printf("a = %d, b = %d\n", a, b); 
    printf("a = %#x, b = %#x\n", a, b); 
    printf("a: %d, b: %d\n", sizeof a, sizeof b);

    return 0;
}
结果:

a = 68, b = 68
a = 0x44, b = 0x44
a: 1, b: 4
恩,虽然也是用0来填充高位,但此时0应被视为正号,仍是符号扩展。

总结上面printf函数的这个小“bug”让我不禁这样猜想:是不是在做类型转换时,无论是截断还是扩展,先统一变成四个字节,若不足四个字节,则采用相应的扩展。

若打印十进制,则从右到左读取相应的类型长度,然后停止读取并将读取的数据打印成十进制;但是打印十六进制时,在读取了足够的类型长度后,若读到0则停止读入,若读到1则继续读入,直到读够4个字节。


那么最后,符号转换和类型转换一起上会怎样?

#include <stdio.h>

int main(void)
{
    short a = -1;
    int b = (int)(unsigned short)a;
    unsigned int c = (unsigned)(int)a;

    printf("a = %d, a = %#x\n", a, a);
    printf("b = %d, b = %#x\n", b, b);
    printf("c = %u, c = %#x\n", c, c);
    //printf("a: %d, b: %d\n", sizeof a, sizeof b);

    return 0;
}
结果:

a = -1, a = 0xffffffff
b = 65535, b = 0xffff
c = 4294967295, c = 0xffffffff
对于b,是先把a由signed short转换成unsigned short,再转换成int;而c的话,先做类型转换,再由signed转换成unsigned。我们看到结果是不同的,理由在上面说过了。另外,unsigned等价于unsigned int


现在我们来研究一下有符号数和无符号数运算的问题。
C语言规定,如果参与运算的两个数一个是有符号的另一个是无符号的,那么有符号数会被隐式的强制转换成无符号数,并假设两个数都是非负数。

这条规则对于算数运算来说,不会导致什么区别,也就是没什么负面作用。然而对于逻辑运算可能会产生意外。

如:-1 < 0u

表面上看没什么错,但这个运算的结果是0,即false。。因为-1被转换成无符号数后就是4294967295u,它当然不可能小于0u。。。

在比如:2147483647u > -2147483647 - 1

结果也是错的,因为-2147483648转换成无符号数后是2147483648。









C语言笔记之数据类型(二)

标签:

原文地址:http://blog.csdn.net/u012668018/article/details/45373525

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!