0
点赞
收藏
分享

微信扫一扫

冒牌程序员-毛哥 C语言入门教程(第二章:数据类型 第八节 强制转换)

第二章:数据类型

第八节 强制转换

所谓强制转换,就是对一个数据类型明确的数据进行重新说明,例如有一个int类型的数据a,我们在某个运算或者其他数据处理的时候,希望将这个int类型的数据当作float类型的浮点数据来处理,此时,我们就需要对这个a进行重新说明,叫做强制转换。如下例:

int	a;
float f;
a=100;
printf("我们把a看作浮点数进行显示:%f\n",(float)a);

或者

f=100.1+(float)a;

从上面的代码可以看出,所谓的强制转换或者重新说明数据类型,其实现的格式就是在数据的前添加新的数据类型,然后用括号括起来。经过这么一次临时说明,编译器在当前的这个语句内,就会认为这个数据或者变量的数据类型就是新的数据类型。例如在上述printf语句中,变量本来是int类型,但是在a前面添加了(float)后,编译器就会在printf语句内,认为a是一个浮点数(但是在其他没有对a进行强制转换的地方,a依然是int数据类型)。

这么总结一下,一个变量的数据类型,其默认数据类型是这个变量定义的时候给定的数据类型,但在使用的时候,可以临时把这个变量看成其他数据类型,其方法就是强制转换,这个转换是临时的,只在转换时有效,其他没有强制转换的地方,变量保持其默认数据类型。

变量如此,常量呢?编译器一般会根据常量的形式,确定常量的数据类型,例如"This is a string",只可能是一个字符串。但10可能是整数,也可能是浮点数,一般来说编译器能够根据上线文和常量的格式,来确定常量的数据类型。如果不能确定,建议程序员对常量数据类型进行说明,就是在常量前面,用括号添加对应的数据类型。例如:

float f = (float)10;

指针类型的强制转换规则:

转换规则比较简单,指针类型的变量被强制转换后,指针变量对应的内存地址的值不会改变,但指针指向的内存地址中,保存的二进制串代表的事物或者含义会被改变。如下举例:

/*******冒牌程序员-毛哥 c-pointer.c ******/

#include<stdio.h>
int main()
{
    float a = 100;//这里编译器可以根据上下文,判断出100就是一个浮点数。
    unsigned int *p;
    float *fp;
    fp=&a; //这里不需要强制转换,因为fp是float*类型,&也是float*类型。

    p = (unsigned int *)&a;
    /*
    &a是一个指针,其类型是float*,而p的类型是(unsigned int*)类型。
    其实这里对于编译器来说,也不难推断出需要将&a转换成unsigned int*类型。
    (其实也不用转换,直接将&a的值,赋值给p就可以了)。但是,如果没有这个转换
    说明,编译器会报错或者报警告(大家可以测试下)。编译器为啥要多此一举呢?

    这是编译器为你好,因为这种强制一般来说非常危险(还记得我说的,C语言的这种
    转换是被诟病的地方吗?)。搞不好程序就会出问题,所以编译器会提醒你,你真的
    要这么做吗?如果你确认要这么做,请明确告诉编译器。
    */

    printf("unsigned int = %#X\n",*p);
    /*
    本来&a这个地址内保存的是一个float类型的浮点数,但在上面一句printf语句中,
    这个内存地址内的二进制串被解释成了无符号整数unsigned int。前面我们在研究
    数据类型的时候,就使用了这个强制转换。
    */

    return 0;
}

总结:指针类型强制转换,指针对应的地址值不会改变,改变的是对该地址内二进制串的含义,或者说解释方法。

非指针类型的强制转换

我们还是用程序说话

/***********冒牌程序员-毛哥 coercion.c */
#include<stdio.h>

int main()
{
    /*无符号整数之间的转换*/
    unsigned char a;
    unsigned long b;
    a=100;
    b=a;    //这里不进行强制转换也没有警告
    printf("b = %#lx a=%#hhx\n",b,a);
    /*
    可以看出值没变,我们用gdb看一下,a变量内存地址和b内存地址比较一下

    (gdb) x/1xb &a
    0x7fffffffe037: 0x64
    (gdb) x/1xg &b
    0x7fffffffe038: 0x0000000000000064

    可以看出,转换的规则,就是高位用0补齐,其他不变。这就是小字节宽度的无符号整数
    到大宽度的无符号整数的转换规则(所谓字节宽度指的是这个类型所占的字节数)。
    */

    b=0x12345678;
    a=b;    //这里同样没有警告

    printf("b = %#lx a=%#hhx\n",b,a);
    /*
    同样,用gdb看一下两个变量的内存地址中的二进制串:

    (gdb) x/1xb &a
    0x7fffffffe037: 0x78
    (gdb) x/1xg &b
    0x7fffffffe038: 0x0000000012345678

    可以看出,大字节宽度的无符号整数往小字节宽度转换的时候,就是把高位扔掉,保留低位数据。
    */

    /*
    有符号数之间的转换
    */

    char a1;
    long b1;

    a1=100;
    b1=a1;

    /*
    程序暂停到下一句,看看,a的二进制串和b的二进制串

    (gdb) x/1tb &a1
    0x7fffffffe02f: 01100100
    (gdb) x/1tg &b1
    0x7fffffffe038: 0000000000000000000000000000000000000000000000000000000001100100

    可以看出,a1这个有符号数的符号位是0,根据观察结果,小字节类型到大字节类型转换,高位用0补齐。

    */

    a1=-100;
    b1=a1;
    /*
    程序暂停到下一句,看看,a的二进制串和b的二进制串

    (gdb) x/1tb &a1
    0x7fffffffe02f: 10011100
    (gdb) x/1tg &b1
    0x7fffffffe038: 1111111111111111111111111111111111111111111111111111111110011100

    可以看出,a1这个有符号数的符号位是1,根据观察结果,小字节类型到大字节类型转换,高位用1补齐。

    总结,有符号数,小字节往大字节转换,用符号位填充多出来的高位。
    */

    b1=-1000000035L;
    a1=b1;
    /*
    程序暂停到下一句,看看,a的二进制串和b的二进制串

    (gdb) x/1tb &a1
    0x7fffffffe02f: 11011101
    (gdb) x/1tg &b1
    0x7fffffffe038: 1111111111111111111111111111111111000100011001010011010111011101

    高位扔掉,保留地位
    */

    b1=1000000178L;
    a1=b1;
    /*
    程序暂停到下一句,看看,a的二进制串和b的二进制串

    (gdb) x/1tb &a1
    0x7fffffffe02f: 10110010
    (gdb) x/1tg &b1
    0x7fffffffe038: 0000000000000000000000000000000000111011100110101100101010110010

    高位扔掉,保留地位

    结论,有符号整数大字节宽度往小字节宽度转换,就是把高位直接扔掉,保留低位。
    */

    float f;
    double df;

    df=2e128;
    f=df;
    printf("f=%f,df=%lf\n",f,df);

    /*
    可以看出,浮点数大字节到小字节转换比较简单,超出范围的,用对应的无限inf来表示,不超出范围,
    就按近似值来转换。

    float到double转换我就不试验了,按照值不变转换就可以了。不信的可以试验一下。
    */

    a=255;
    a1=a;
    printf("a1 = %hhd\n",a1);

    /*
    有符号整数和无符号整数转换:
    同字节数的类型转换,其实就是对二进制串进行重新解释。
    */

    a1=-1;
    b=a1;
    printf("b = %lu\n",b);//符号位扩展完毕后,再进行二进制串的重新解释。

    a = 255;
    b1=a;
    printf("b1 = %ld\n",b1);   //按照值进行转换

    b1 = -2232323232323;
    a=b1;
    /*
    查看变量内存地址内的二进制串:

    (gdb) x/1tb &a
    0x7fffffffe022: 10111101
    (gdb) x/1tg &b1
    0x7fffffffe030: 1111111111111111111111011111100000111111001010101010110110111101

    看一看出来,就是把高位抛弃,留低位。
    */

    return 0;

}

从上面的程序可以看出,这个转换很乱,基本能符合人类直觉,但有时候也有例外,那么最保险的办法就是按照上面程序的办法,如果要使用两个数据类型之间的强制转关规则,试一下,有数了再进行转换。好脑子不如烂爪子,试一下最好。

总结:

本章介绍了C语言中基本数据类型,和这些数据类型的本质。下面一章我们将介绍如何操作这些数据类型,也就是这些数据类型的操作符,或者说运算符。

举报

相关推荐

0 条评论