【C】隐式类型转换

隐式类型转换是C语言在编译阶段自动进行的类型转换,无需程序员显式指定。它主要用于以下场景:

  • 表达式中操作数类型不同(如int + float)
  • 赋值时左右类型不匹配(如int = float)
  • 函数调用时参数类型不匹配(如传递char给int形参)

隐式转换的核心目标是保证运算的合理性和精度,但它可能带来意料之外的结果,需谨慎处理。

补充:浮点数和整型数之间无论是隐式转换还是强转,都是数值语义的转换,1.0转成int就是1,而不是内存值。只有用联合体或指针,才能取浮点数特殊的存储格式。

  • 整型提升时规则(低于int的,在计算时不管需不需要,都先统一到int,即使两边都是char):

有符号类型:高位填充符号位(负数补1,正数补0)(也适用于右移规则);

无符号类型:高位补0;

复制代码
unsigned char uc = 0xFF; //11111111
int i = uc;              //提升为int -> 00000000 00000000 00000000 11111111

***左移规则:低位补0;***单字节左移会触发整型提升,左移不大于24无需考虑强转uint64_t,大于24则一定要先强转再左移

右移规则:无符号数:高位补0;有符号数:高位补符号位;

  • 两个操作数类型不同时,按下方从低到高转换(低优先级直接变成高优先级):

char/short -> int -> unsigned int -> long -> unsigned long -> long long -> unsigned long long -> float -> double

(unsigned int 比 int范围大,每个int包括负数都可以找到对应点,且计算逻辑没问题,信息不丢失。如果反过来,unsigned int的大值转成int变成了负数,虽然在int范围内合法,但是和原始数学意义完全割裂,导致信息丢失)

复制代码
char a = -1;
unsigned long aa = 1;
unsigned long long b = 1;
unsigned long long c = a * b;
unsigned long long d = a * aa * b;

printf("chenmin %lld %lld\n", c, d);
//结果是:chenmin -1 4294967295
//c是由a变int再变unsigned long long
//d是由a变int再变unsigned long再变unsigned long long,中间符号位丢失
  • 操作数精度相同,则计算结果类型不变,且中间过程无需转换。但是有需要的话我们可以借助整型提升时规则来理解过程和结果,比如INT32U的5-255实际计算过程都是INT32U(无需浪费资源),我们可以先假设提升了,得到-250,再转成一个很大的INT32U,之后再进行强转或者赋值操作。

INT64U = 0x1D000000 * 0x1D000000; //结果是0,两个32位的数先相乘溢出再赋值

INT64U = (INT64U)(0x1D000000 * 0x1D000000); //结果是0,两个32位的数先相乘溢出再赋值

常见陷阱:

1、符号位扩展混淆意图

复制代码
char c = -1;
unsigned int u = c; //c提升为int(0xFFFFFFFF) -> 转换为unsigned int为4294967295

unsigned int u = (unsigned char)c; //截断为0xFF

2、溢出截断,可借助0作为补码计算,比如-700赋值给char,-700+256+256+256 == 68

复制代码
int a = 50000;
short b = a; //若short为16位,b值为-15536(溢出)

3、类型提升导致正数小于负数

复制代码
unsigned int u = 10;
int s = -5;
if (u < s)
{
    //s被转换为unsigned int -> 4294967291
}

4、不同情况的整型提升

复制代码
unsigned int a1 = 9;
unsigned int b1 = 1000;

if (a1 - b1 > 1) //操作数精度相同,则计算结果类型不变,且中间过程无需转换
{
    printf("1 Hello world! %d\n", a1 - b1);
}

unsigned short a2 = 9;
unsigned short b2 = 1000;

if (a2 - b2 < 1) //低于int的,在计算时不管需不需要,都先统一到int
{
    printf("2 Hello world! %d\n", a2 - b2);
}

unsigned int a6 = 9;
unsigned int b6 = 1000;

if (a6 - b6 < -99) //常数的类型由变量类型决定
{
    printf("6 Hello world! %d\n", a6 - b6);
}

5、精度丢失

复制代码
float f = 0.1f;
double d = 0.1;
if (f != d)
{
    //因float精度不足,实际存储值与double不同
}

//建议:避免直接比较浮点数,使用误差范围
#define EPSILON 1e-6
if (fabs(f - d) < EPSILON)
{
    //近似相等
}
相关推荐
泛凡(Linyongui)2 小时前
PY32F002B实践之三--宠物腹背理疗仪项目功能代码分析说明
c语言·keil·32位单片机·腹背理疗仪项目实践·普苒py32
W23035765732 小时前
【C++ 高性能日志系统实战】第三篇:异步日志系统的实现与优化
网络·数据结构·算法·日志
y = xⁿ2 小时前
【LeetCode】哈希表
算法·leetcode·散列表
大江东去浪淘尽千古风流人物2 小时前
【Basalt】nfr_mapper 中的“小 SfM/BA 后端”
c++·人工智能·计算机视觉·oracle·augmented reality
Magic--2 小时前
C++ STL中vector与list的核心区别
c++·windows·list
初願致夕霞2 小时前
Linux_线程
linux·运维·服务器·c++
2401_892070982 小时前
【Linux C++ 后端实战】异步日志系统 AsyncLogging 完整设计与源码解析
linux·c++·高并发·异步日志
梓䈑2 小时前
gtest实战入门:从安装到TEST宏的单元测试指南
c++·单元测试