Skip to content

C 类型转换(Type Casting)

类型转换用于在不同类型之间进行值或表示的变换,分为隐式转换与显式转换(强制类型转换)。理解整数提升、常规算术转换与指针转换规则,是写出可靠 C 程序的基础。

1. 隐式转换(隐式类型提升)

  • 整数提升:charshort 在表达式中通常提升为 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. 结构体、联合与内存视图

  • 不能直接在不同结构体类型间转换指针并解引用(未定义行为)。
  • 若需多视图表示,考虑使用 unionmemcpy 安全地在字节级复制:
c
float f = 1.0f; unsigned u;
memcpy(&u, &f, sizeof u); // 按位复制,避免严格别名 UB

7. 示例:安全的字符串转数值

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. 小结

  • 优先利用隐式转换的规则与编译器告警,减少不必要的强转。
  • 涉及指针与别名的转换要格外谨慎,必要时通过 memcpyunion 或重新设计接口规避。

本站内容仅供学习和研究使用。