C语言:整型提升

一, 整型提升

C语⾔中整型算术运算总是⾄少以缺省(默认)整型类型的精度来进⾏的。
为了获得这个精度,表达式中的字符和短整型操作数在使⽤之前被转换为普通整型,这种转换称为整型提升
整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执⾏,CPU内整型运算器(ALU)的操作数的字节⻓度⼀般就是int的字节⻓度,同时也是CPU的通⽤寄存器的⻓度。
因此,即使两个char类型的相加,在CPU执⾏时实际上也要先转换为CPU内整型操作数的标准⻓
度。
通⽤CPU(general-purpose CPU)是难以直接实现两个8⽐特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种⻓度可能⼩于int⻓度的整型值,都必须先转换为 int或unsigned int,然后才能送⼊CPU去执⾏运算

整型提升的规则

  • 有符号类型 :如果原类型是有符号类型(如 signed charsigned short),则将其值符号扩展为 int 类型。也就是说,如果原类型的最高位(符号位)是 1,则在扩展后的 int 类型中,高位全部补 1;如果最高位是 0,则高位全部补 0。
  • 无符号类型 :如果原类型是无符号类型(如 unsigned charunsigned short),则将其值零扩展为 int 类型,即高位全部补 0。

整型提升的原因

  • 提高计算效率 :大多数计算机的 CPU 对于 int 类型的运算处理速度更快,将小整数类型提升为 int 类型可以利用 CPU 对 int 运算的优化,提高计算效率。
  • 统一运算操作数类型 :在表达式求值时,要求运算符的操作数类型一致。通过整型提升,可以将不同的小整数类型统一转换为 int 类型,便于进行运算。

整型提升的示例

示例 1:有符号类型的整型提升
#include <stdio.h>

int main() {                                  //          10000001(原码)
                                              //          11111111(反码)
    signed char a = -1;  // 有符号字符型,8 位,二进制表示为 11111111(补码,只取八位比特)
    int result = a + 2;  // 进行加法运算,a 会被提升为 int 类型

    // 有符号字符型 -1 提升为 int 类型后,二进制表示为 11111111 11111111 11111111 11111111
    //2的二进制表示00000000 00000000 00000000 00000010(补码)
    //提升后:-1+2=00000000 00000000 00000000 00000001(补码)  
    printf("结果: %d\n", result);
    return 0;
}

在这个例子中,signed char 类型的变量 a 的值为 -1,其 8 位二进制表示是 11111111。在进行 a + 2 运算时,a 会被提升为 int 类型,通过符号扩展,其 32 位二进制表示变为 11111111 11111111 11111111 11111111。然后与 2(二进制 00000000 00000000 00000000 00000010)相加,得到结果 1

示例 2:无符号类型的整型提升
#include <stdio.h>

int main() {
    unsigned char b = 255;  // 无符号字符型,8 位,二进制表示为 11111111(补码)
    int result = b + 1;     // 进行加法运算,b 会被提升为 int 类型

// 无符号字符型 255 提升为 int 类型后,二进制表示为 00000000 00000000 00000000 11111111(补码)
    // 与 1 相加后结果为 256,二进制表示为 00000000 00000000 00000001 00000000(补码)
    printf("结果: %d\n", result);
    return 0;
}

这里 unsigned char 类型的变量 b 的值为 255,其 8 位二进制表示是 11111111。在进行 b + 1 运算时,b 会被提升为 int 类型,通过零扩展,其 32 位二进制表示变为 00000000 00000000 00000000 11111111。然后与 1(二进制 00000000 00000000 00000000 00000001)相加,得到结果 256

注意事项

  • 对表达式结果的影响 :整型提升可能会影响表达式的计算结果,特别是在涉及有符号和无符号类型混合运算时。例如:

    #include <stdio.h>
    
    int main() {
        signed char a = -1;
        unsigned char b = 255;
        int result = a + b;
    
        // a 提升为 int 类型后是负数,b 提升为 int 类型后是正数
        // 相加结果为 254
        printf("结果: %d\n", result);
        return 0;
    }
    

    在这个例子中,signed char 类型的 a 提升为 int 类型后是负数,unsigned char 类型的 b 提升为 int 类型后是正数,它们相加的结果是 254

  • 位运算中的整型提升 :在进行位运算(如按位与 &、按位或 | 等)时,也会进行整型提升。例如:

    #include <stdio.h>
    
    int main()
     {
        unsigned char x = 0xFF;
        unsigned char y = 0x01;
        unsigned char result = x & y;
    
        // x 和 y 先提升为 int 类型进行位与运算,结果再截断为 unsigned char 类型
        printf("结果: %d\n", result);
        return 0;
    }
    

    算术转换

  • 如果某个操作符的各个操作数属于不同的类型,那么除⾮其中⼀个操作数的转换为另⼀个操作数的类型,否则操作就⽆法进⾏。下⾯的层次体系称为寻常算术转换。

算术转换的规则

算术转换遵循以下一般规则,这些规则按照优先级从高到低的顺序执行:

  • 长双精度扩展 :如果其中一个操作数是 long double 类型,那么另一个操作数会被转换为 long double 类型。
  • 双精度扩展 :如果其中一个操作数是 double 类型,而另一个不是 long double 类型,那么另一个操作数会被转换为 double 类型。
  • 单精度扩展 :如果其中一个操作数是 float 类型,而另一个不是 doublelong double 类型,那么另一个操作数会被转换为 float 类型。
  • 整型提升 :如果操作数不属于上述浮点类型,那么会先对操作数进行整型提升(将比 int 小的整数类型转换为 intunsigned int)。
  • 整数类型转换 :经过整型提升后,如果两个操作数的类型仍然不同,会将较低等级的整数类型转换为较高等级的整数类型。整数类型的等级从低到高大致为:charshortintunsigned intlongunsigned longlong longunsigned long long

算术转换的原因:

  • 统一操作数类型:C 语言的二元运算符通常要求两个操作数的类型相同,算术转换可以将不同类型的操作数转换为相同类型,使得运算符能够正确执行。
  • 保证计算精度:在进行混合类型的算术运算时,将操作数转换为较高精度的类型可以避免数据丢失,保证计算结果的准确性。

算术转换的示例

示例 1:浮点类型的算术转换
#include <stdio.h>

int main() 
{
    float f = 3.5f;
    double d = 2.0;
    // f 会被转换为 double 类型进行计算
    double result = f + d;
    printf("结果: %lf\n", result);
    return 0;
}

在这个例子中,由于 ddouble 类型,ffloat 类型,根据规则,f 会被转换为 double 类型,然后进行加法运算。

示例 2:整数类型的算术转换
#include <stdio.h>

int main()
 {
    char c = 'A';  // ASCII 码值为 65
    int i = 10;
    // c 会先进行整型提升为 int 类型,然后进行加法运算
    int result = c + i;
    printf("结果: %d\n", result);
    return 0;
}

这里 cchar 类型,iint 类型,c 会先进行整型提升为 int 类型,然后与 i 进行加法运算。

示例 3:混合类型的算术转换

#include <stdio.h>

int main() {
    short s = 5;
    float f = 2.5f;
    // s 会先进行整型提升为 int 类型,然后再转换为 float 类型进行计算
    float result = s + f;
    printf("结果: %f\n", result);
    return 0;
}

在这个例子中,sshort 类型,ffloat 类型。s 先进行整型提升为 int 类型,然后根据规则,int 类型的 s 会被转换为 float 类型,再与 f 进行加法运算。

注意事项

  • 数据丢失问题 :在进行算术转换时,如果将较高精度的类型转换为较低精度的类型,可能会导致数据丢失。例如:

    #include <stdio.h>
    
    int main() 
    {
        double d = 123456789.123;
        int i = (int)d;  // 强制将 double 类型转换为 int 类型,可能会丢失小数部分
        printf("结果: %d\n", i);
        return 0;
    }
    

    在这个例子中,将 double 类型的 d 强制转换为 int 类型,小数部分会被截断,可能会导致数据丢失。

  • 无符号类型的影响 :当涉及无符号类型和有符号类型的运算时,需要特别注意结果的符号性。例如:

    #include <stdio.h>
    
    int main()
     {
        unsigned int u = 4294967295;  // 无符号整型的最大值
        int i = -1;
        // 由于算术转换,i 会被转换为无符号类型,结果可能不符合预期
        unsigned int result = u + i;
        printf("结果: %u\n", result);
        return 0;
    }
    

    在这个例子中,i 是有符号 int 类型,u 是无符号 int 类型,在进行加法运算时,i 会被转换为无符号类型,导致结果可能不符合预期。

    理解算术转换规则对于编写正确的 C 语言代码至关重要,特别是在处理混合类型的算术运算时。

相关推荐
加油,旭杏44 分钟前
【go语言】函数
开发语言·后端·golang
摘星崖1 小时前
1.3编译流程与调试基础
c语言
南玖yy1 小时前
C语言:结构体
c语言·开发语言
代码对我眨眼睛1 小时前
重回C语言之老兵重装上阵(十三)C 预处理器
linux·c语言
〖是♂我〗3 小时前
自定义数据集 使用scikit-learn中svm的包实现svm分类
开发语言·python
iqay3 小时前
【C语言】填空题/程序填空题1
c语言·开发语言·数据结构·c++·算法·c#
程序猿编码3 小时前
自定义命令执行器:C++中命令封装的深度探索(C/C++实现)
linux·c语言·c++·网络安全·shell·命令行
锐策3 小时前
『 C 』 `##` 在 C 语言宏定义中的作用解析
c语言
lsx2024063 小时前
ECharts 样式设置
开发语言