C 类型转换(Type Casting)
类型转换用于在不同类型之间进行值或表示的变换,分为隐式转换与显式转换(强制类型转换)。理解整数提升、常规算术转换与指针转换规则,是写出可靠 C 程序的基础。
1. 隐式转换(隐式类型提升)
- 整数提升:
char、short在表达式中通常提升为int(或unsigned int,取决于范围)。 - 常规算术转换:参与二元算术运算时,操作数会转换到共同类型(如
double > float > long long > long > int的层级;具体依实现和标准规则)。 - 赋值、比较与函数实参传递时,编译器会按目标类型进行转换并可能发出警告。
示例:
c
char c = 120; // 存储为 8 位(实现相关),在表达式中会提升为 int
int x = c + 10; // c 被提升为 int 再参与运算2. 显式转换(强制类型转换)
- 语法:
(T)expr - 用途:
- 缩小或扩大类型范围(可能溢出/截断)。
- 在数值类型与指针类型之间转换(需极度谨慎)。
- 解除
void*的通用指针到具体指针类型。
示例:
c
double d = 3.14;
int i = (int)d; // 截断为 3
void *pv = &i;
int *pi = (int*)pv; // 从 void* 转回具体类型3. 指针与地址转换
void*是通用指针,可与任意对象指针互转,但需确保原始类型一致。- 不同对象类型指针之间的互转(非
char*/unsigned char*)可能违反严格别名规则,导致未定义行为。 - 指针与整数互转依赖实现,只在特定平台/场景下安全。
c
float f = 1.0f;
void *p = &f; // OK:任意对象指针 -> void*
float *pf = (float*)p; // OK:void* -> 原类型4. const/volatile 合法转换
const int *p转为int *会丢弃常量限定,编译器允许但一旦通过该指针修改对象即为未定义行为(若原对象是常量)。- 推荐保持限定符正确传播;必要时使用不同 API 设计避免移除限定。
5. 数值转换的风险与实践
- 截断与溢出:
c
unsigned char b = (unsigned char)300; // 可能得到 44(被截断至 8 位)- 有符号/无符号混用:比较与运算易出现意外提升与结果。
- 浮点到整数:截断而非四舍五入;使用
lround/round等库函数更可控。
实践建议:
- 打开编译器警告(如
-Wall -Wextra),显式转换处加注释说明意图。 - 先进行范围检查,再做显式转换。
6. 结构体、联合与内存视图
- 不能直接在不同结构体类型间转换指针并解引用(未定义行为)。
- 若需多视图表示,考虑使用
union或memcpy安全地在字节级复制:
c
float f = 1.0f; unsigned u;
memcpy(&u, &f, sizeof u); // 按位复制,避免严格别名 UB7. 示例:安全的字符串转数值
c
long parse_i32(const char *s, int *ok) {
char *end = NULL; errno = 0;
long v = strtol(s, &end, 10);
if (errno != 0 || end==s || *end!='\0' || v < INT_MIN || v > INT_MAX) {
if (ok) *ok = 0; return 0;
}
if (ok) *ok = 1; return v; // 后续再显式转换为 int
}8. 小结
- 优先利用隐式转换的规则与编译器告警,减少不必要的强转。
- 涉及指针与别名的转换要格外谨慎,必要时通过
memcpy、union或重新设计接口规避。