变量赋值时右值必须可求值且能安全隐式转换为左值类型,仅限算术与枚举类型;结构体、指针等需显式转换;转换由左值决定,可能丢失精度或截断。

变量赋值时,右值(rvalue)必须能被转换为左值(lvalue)所声明的类型,这个转换不是任意的,而是受明确约束的。核心原则是:赋值操作中,右值的类型会向左值类型靠拢,系统自动执行隐式转换,但仅在安全、可预测的前提下发生。
右值必须是可求值的表达式
右值不能是未定义行为的来源,比如空指针解引用、越界数组访问、未初始化变量的直接使用等。这些表达式本身不产生确定值,也就无法作为合法右值参与赋值。例如:
-
int x = *p;—— 若p为NULL或未指向有效内存,该右值非法 -
int y = arr[10];—— 若arr只有5个元素,此右值行为未定义 -
int z = a + b;—— 只要a、b已定义且类型兼容,就是合法右值
类型转换只发生在算术与枚举类型之间
隐式转换不是万能的。C语言只对数值类类型(整型、浮点型、枚举)提供自动转换支持;结构体、联合体、函数指针、void*等类型之间不允许隐式赋值,必须显式转换或逐字段处理。
- 允许:
double d = 3; int i = d;(d转为int,截断小数) - 允许:
char c = 65; int n = c;(ASCII码提升为int) - 禁止:
struct S s1 = {1}; struct S s2 = s1;—— 这是合法的赋值,但靠的是逐字节拷贝,不是类型转换 - 禁止:
int *p = malloc(4); char *q = p;—— 必须写成char *q = (char *)p;
转换方向由左值类型决定,且可能丢失信息
右值总是被“适配”到左值类型,而不是反过来。这意味着精度下降、符号变化或截断都可能发生,且编译器通常只警告不报错。
- 浮点→整型:舍弃小数部分,如
int a = 3.9;→a == 3 - 大整型→小整型:只保留低位字节,如
short s = 0x12345678;→ 实际存入0x5678 - 有符号→无符号(同宽):按位复制,负数变成大正数,如
unsigned u = -1;→u通常是UINT_MAX - char→int:符号扩展(signed char)或零扩展(unsigned char),取决于平台和声明方式
复合赋值中的右值需整体参与转换
像a += b这样的表达式,本质是a = a + b,其中a + b先计算为右值,再整体转换为a的类型。注意括号影响优先级,也影响转换时机。
-
char c = 100; c += 200;:先算c + 200(提升为int得300),再转回char→ 结果通常是44(300 % 256) -
float f = 1.5f; f *= 2.1;:2.1是double,先升为double运算,再截断为float赋回 - 错误写法:
int i = 0; i += 3.5 + 2.7;→ 右值是double,转换为int时直接截断,得i = 6,而非四舍五入











