C语言类型转换

C语言类型转换

    本文第一部分,“寻常算术转换”是《C专家编程》1.10的阅读笔记;第二部分“强制类型转换”是个人经验。

一、寻常算术转换

   许多运算符都会引发编译器 静默地转换操作数类型,以类似的方式产生结果类型,这个模式成为“寻常算术转换”。

        1、K&R标准(第6.6节:算术转换)

  •  任何类型为char或为short的操作数都被转化为int,任何类型为float的操作数都被转换为double;
  • 如果其中一个操作数的类型是double,那么另一个操作数和计算结果类型都是double;
  • 如果其中一个操作数的类型是long,那么另一个操作数和计算结果类型都是long;                      
  • 如果其中一个操作数的类型是unsigned,那么另一个操作数和计算结果类型都是unsigned;   

        2、ANSI C标准

    ANSI C标准重新编写了有关内容,填补了其中的漏洞

  • 字符和整型(整型升级):在需要使用int或者unsigned int的表达式中,char,short int,int型位段,包括有符号或无符号类型,以及枚举类型,会发生整型升级。如果int可以完整表示原类型的所有值,那就转换为int,否则转换为unsigned int。
  • 如果其中一个操作数的类型是long double,那么另一个操作数也转换为long double;如果其中一个操作数的类型是double,那么另一个操作数也转换为double;如果其中一个操作数的类型是float,那么另一个操作数也转换为long float;其他情况将按照下面的规则进行整型升级,
  • 如果其中一个操作数的类型是unsigned long int,那么另一个操作数也转换为unsigned long int;如果其中一个操作数的类型是long int,那么另一个操作数也转换为long int,另一个操作数是unsigned int,在long int 能够完整表示unsigned int的所有值时(比如long 是32位,int是16位),unsigned int 转换为long int,在long int 不能表示unsigned int的所有值时(比如long 和int都是32位),两个操作数都转换为unsigned long int;如果其中一个操作数的类型是unsigned int,那么另一个操作数也转换为long double;
  • 如果其中一个操作数的类型是long int,那么另一个操作数也转换为long int;如果其中一个操作数的类型是unsigned int,那么另一个操作数也转换为unsigned int;
如果不属于以上情况,那么两个操作都为int;
总而言之(便于记忆,但不精确),ANSI C 标准大致意思为:当执行算术运算时,操作数的类型如果不同,就会发生转换。数据类型一般朝着浮点精度更高,长度更长的方向转换,整型数如果转换为signed不会丢失信息,就转换为signed,否则转换为unsigned。

    3、两者比较分析

    K&R C采用无符号保留(unsigned preserving)原则,就是当一个无符号类型与int或更小的整型混合时,结果类型就是无符号类型。这是个简单的规则,与硬件无关。下列所示的例子,将会是一个负数丢失符号位。
    ANSI C 采用值保留(value preserving)原则,就是当整型操作数混合使用时,结果类型有可能是符号数,也有可能是无符号数,取决于操作数类型的相对大小。

#include 

int main()
{
    if(-1 < (unsigned char) 1)
        printf("-1 is less than (unsigned char)1:ANSI semantics");
    else
        printf("-1 NOT less than (unsigned char)1:K&R semantics");
    return 0;
}

 程序中的表达式在两种编译器下的编译结果不同。-1的位模式是一样的,但是ANSI C将其解释为负数,而K&R C编译器将其解释为无符号数,表示为一个很大的正数。此外,若将上述条件改为“(-1 < (unsigned int) 1)”,在ANSI C编译器会把-1 解释为一个很大的正数。其转化规则如上所述。

4、分析启发

    尽量不要在代码中使用无符号类型,以免增加不必要的复杂性。尤其是不要仅仅因为无符号数不存在负值(如:年龄)而用来表示数量。尽量使用像int那样的有符号类型,这样在涉及涉及升级混合类型的复杂细节时,不必担心边界情况(如-1被翻译为非常大的正数)。
    只有在使用位段和二进制掩码时,才可以使用无符号数。应该在表达式中使用强制类型转换,使操作数均为有符号数或者无符号数,这样就不必有编译器来选择结果的类型。

二、强制类型转换
    

当C/C++进行强制类型转换时,会有两种方式:

    1. 保持内存中的内容不变,仅修改对这段内存的解释方式, 如int和char的互转

    2. 保持值不变(近似),但修改内存中的内容,如int和double的互转

可以自动转换的类型一定能够强制转换,但是,需要强制转换的类型不一定能够自动转换。如下代码所示:

#include 

int main()
{
    struct A
    {
        char a1;
        int a2;
    }a;

    struct B
    {
        int b1;
        char b2;
    }b;

    struct A *aa = &a;
    struct B *bb = &b;

    aa->a1 = 1;
    aa->a2 = 2;

    bb->b1 = 3;
    bb->b2 = 4;

    struct A * aCast = ( struct A *)(bb);//将B结构体的数据显性强制转化为A结构体数据
    printf("%d",aCast->a1);

    //若使用隐形强制类型转换,则会出错
    //printf("%d",( struct A *)(bb)->a2); //error: 'struct B' has no member named 'a2'

    return 0;
}
    此外,  强制类型转换仅适用于基类型( "标量类型", 如char,short,int,long,float,double ,或者指针),而不允许用于struct,union等类型。




你可能感兴趣的:(C,C++)