C语言「sizeof的谎言」:90%人天天用,却全用错了

简介: C语言中sizeof常被误认为函数,实为编译期运算符。本文揭示三大反常识真相:它不执行副作用、括号非必需(仅类型需)、数组传参即退化。助你避开99%的高频陷阱。(238字)

几乎每个C语言开发者天天都在用sizeof,但90%的人对它的认知都是错的——它不是函数,不参与运行时计算,甚至你写的很多括号都是多余的。本文用3个反常识真相,彻底讲透sizeof的底层规则,避开所有高频坑。


真相1:sizeof是纯编译期运算符,不是运行时函数

除了C99变长数组(VLA),所有sizeof的计算都在编译阶段完成,直接替换为常量值,不会生成任何运行时代码。

最经典的踩坑示例:

#include <stdio.h>
int main() {
   
    int a = 10;
    printf("sizeof结果:%zu\n", sizeof(a++));
    printf("a的值:%d\n", a); // 输出10,a++根本没执行
    return 0;
}

底层逻辑:编译期就确定了sizeof(a)是4,直接替换为常量,a++的副作用表达式被完全丢弃,不会进入运行时代码。

真相2:括号不是「函数调用符」,只是优先级控制

sizeof是单目运算符,和++--同级,根本不是函数。跟类型名必须加括号,跟变量名完全可以不加。

int a;
sizeof a;    // 完全合法,等价于sizeof(a)
// sizeof int; // 编译报错,类型名必须加括号,正确写法是sizeof(int)

高频优先级陷阱:

int *p;
// 你以为是sizeof(*p + 1),实际是(sizeof(p)) + 1
printf("%zu\n", sizeof p + 1); // 64位系统输出9,而非预期的4

底层逻辑:sizeof优先级高于加法,先算sizeof p(8字节),再加1,结果完全偏离预期。

真相3:数组名的sizeof,仅在定义的作用域内有效

数组名在函数传参时会强制退化为指针,函数内用sizeof永远只能拿到指针的大小,绝对拿不到数组总长度。

#include <stdio.h>
// 形参arr实际是int*,不是完整数组
void get_len(int arr[]) {
   
    printf("函数内sizeof:%zu\n", sizeof(arr)); // 64位系统固定输出8
}
int main() {
   
    int arr[5] = {
   1,2,3,4,5};
    printf("main内sizeof:%zu\n", sizeof(arr)); // 输出20(完整数组总大小)
    get_len(arr);
    return 0;
}

终极避坑总结

记住这3条,就能避开99%的sizeof相关坑:

  1. 非VLA场景,sizeof在编译期完成,不会执行表达式内的自增、函数调用等副作用;
  2. 它是运算符不是函数,注意优先级,复杂表达式必须加括号明确计算顺序;
  3. 函数内永远别用sizeof拿数组长度,必须手动传递数组长度参数。
相关文章
|
22天前
|
存储 安全 编译器
C语言「存储期四象限」:变量生死的底层宪法,90%内存bug的根源
本文深入剖析C语言四大存储期(静态、自动、分配、线程),揭示“变量消失”“指针错乱”“内存泄漏”等顽疾的根源——**访问了生命周期已结束的内存**。用四象限模型厘清变量生死规则,助你从底层杜绝90%内存bug。(239字)
171 15
|
28天前
|
缓存 监控 Java
Java 四大引用体系:从GC回收规则到框架底层实现的完整真相
Java四大引用(强、软、弱、虚)是JDK1.2引入的核心内存管理机制,精准控制对象回收时机。强引用防回收,软引用保缓存(OOM前清理),弱引用防泄漏(GC即回收),虚引用唯一可靠跟踪回收——配合ReferenceQueue实现堆外内存释放等关键兜底。90%开发者仅知皮毛,实为解决OOM、内存泄漏及理解ThreadLocal/NIO底层的基石。(239字)
260 4
|
28天前
|
安全 Java 编译器
Java 泛型体系:从类型擦除到底层实现的完整真相
Java泛型远不止“类型擦除”四字可概括:它深度融合javac编译机制、JVM分派、反射与字节码,是保障类型安全与向后兼容的精密设计。本文深度剖析擦除本质、桥接方法、Signature属性及所有限制根源,破除90%开发者的认知误区,助你真正掌握这一进阶核心。
218 5
|
1月前
|
存储 安全 C语言
C语言深度解析:函数指针的底层本质与避坑指南
本文深入剖析C语言函数指针的本质——函数名即代码段入口地址,厘清其与数据指针的根本差异;系统梳理回调、跳转表、中断向量、动态库等核心应用场景;重点警示签名不匹配、`void*`强转、野指针调用三大致命陷阱,并给出`typedef`封装、空值校验、边界防护等最佳实践。(239字)
405 134
|
1月前
|
存储 安全 编译器
C语言深度解析:变长数组(VLA)的底层逻辑与避坑指南
变长数组(VLA)是C99引入的栈上动态数组,长度运行时确定,访问快但无安全检查。易致栈溢出、野指针、跨平台兼容问题,仅适用于小尺寸、短生命周期场景,大数组务必用malloc。
289 38
|
1月前
|
存储 C语言 内存技术
C语言深度解析:大小端字节序——多字节数据的底层存储规则
大小端指CPU对多字节数据在内存中的存放顺序:大端高字节存低地址,小端反之。x86/ARM默认小端,网络字节序统一为大端。跨平台、网络通信、二进制协议开发中必须显式处理字节序转换,否则数据解析必错。
615 138
|
1月前
|
网络协议 编译器 C语言
C语言深度解析:内存对齐与结构体填充的底层逻辑
C语言中,内存对齐是CPU硬件强制要求的底层规则,直接影响结构体大小、访问性能与硬件兼容性。合理排列成员可减少填充、节省内存;滥用`#pragma pack`则易致崩溃或性能暴跌。嵌入式、网络协议与跨平台开发必备核心知识。(239字)
265 14
|
2月前
|
存储 编译器 程序员
C语言核心剖析:堆与栈的本质差异及避坑指南
C语言中,栈与堆是内存管理的两大核心区域:栈由编译器自动管理,高效但易栈溢出;堆由程序员手动管理,灵活却易致内存泄漏、野指针等陷阱。本文深入剖析二者本质差异与典型风险,助你夯实底层基础。
669 11
|
1月前
|
存储 缓存 Java
Java 对象内存布局:从堆内存储到伪共享优化的底层真相
Java对象内存布局是JVM核心基础:含对象头(Mark Word+Klass指针)、实例数据(字段重排序优化)和对齐填充(8字节对齐)。它直接影响内存占用、GC效率、锁升级与伪共享性能。掌握此机制,是深入理解并发优化(如@Contended)、指针压缩及高性能编程的必经之路。(239字)
311 111
|
14天前
|
缓存 安全 编译器
C语言「volatile 关键字」:被90%开发者误解的硬件同步原语
`volatile` 是C语言中至关重要的硬件同步原语,核心作用是禁止编译器对变量进行优化:每次读写都必须真实访问内存,确保能感知硬件、中断或其它线程的“意外修改”。它非为多线程而生,却是嵌入式、驱动和底层开发的基石。

热门文章

最新文章