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拿数组长度,必须手动传递数组长度参数。
相关文章
|
5月前
|
安全 编译器 C语言
C语言「宏的暗门」:预处理阶段的隐形篡改与避坑守则
宏是C语言预处理阶段的纯文本替换工具,无类型、无作用域、不检语法,易引发括号缺失、副作用、分号错误、类型混乱和命名污染等六大陷阱。安全使用须严守括号规范、避免参数复用、善用`do{...}while(0)`、优先选用内联函数,并及时`#undef`。(239字)
431 10
|
5月前
|
存储 网络协议 安全
C语言「内存对齐潜规则」:结构体里看不见的填充字节
内存对齐是CPU硬件要求的数据地址约束规则:变量须存于其字节大小的整数倍地址。编译器自动插入填充字节确保对齐,导致结构体体积“膨胀”、硬件寄存器读写错位或协议异常。合理排序成员(从大到小)、慎用`packed`、明确对齐控制,是嵌入式与底层开发的关键避坑要点。(239字)
|
5月前
|
缓存 前端开发 JavaScript
前端渲染性能的底层逻辑:跳出重排重绘的表层认知
本文揭示前端渲染优化的本质:性能瓶颈不在“重排重绘”本身,而在于浏览器渲染流水线(JS→样式→布局→绘制→合成)的**触发频次与全链路开销**。主流框架的优化逻辑——批量更新、精准更新、合成层隔离——正是围绕降低流水线调用成本展开。落地只需三招:收敛更新时机、善用编译期优化、慎用合成层。
|
5月前
|
Java API
Java MethodHandle:超越反射的轻量化方法调用底层引擎
Java 7引入的MethodHandle是JVM级动态调用机制,相比反射:仅一次权限校验、强类型绑定、零装箱开销、支持方法适配与invokedynamic。性能达反射3–10倍,是Lambda、动态代理及现代框架的底层引擎。(239字)
316 6
|
5月前
|
人工智能 安全 编译器
C语言的「隐形时序契约」:序列点、副作用与求值顺序终极拆解
本文深入解析C语言中极易被忽视的“序列点”机制,揭示Debug/Release模式差异、跨平台结果不一致等玄学bug的根源——未定义行为(UB)。从副作用定义出发,系统梳理7类标准序列点,剖析4大高频陷阱(如`i=i++ + ++i`),并提供6条安全编码铁律,助你写出稳定、可移植的C代码。(239字)
489 11
|
5月前
|
存储 安全 算法
C语言高频错误实例对比:8段代码帮你避开90%的坑
本文精选8组典型C语言错误与正确代码对比,直击数组越界、字符串溢出、野指针、内存泄漏、有无符号混用、返回局部地址、sizeof误用、未定义行为等高频陷阱,以实例培养安全编码直觉。(239字)
|
5月前
|
缓存 编译器 程序员
C语言深度解析:restrict关键字——编译器性能优化的终极钥匙
C99的`restrict`关键字是C语言性能优化的“终极钥匙”:它向编译器承诺指针独占访问内存,彻底解决同类型指针别名问题,解锁循环向量化、寄存器缓存等激进优化。滥用致未定义行为,善用则性能飙升数倍——这才是真正高阶C程序员的必修课。(239字)
|
5月前
|
存储 C语言 内存技术
C语言深度解析:大小端字节序——多字节数据的底层存储规则
大小端指CPU对多字节数据在内存中的存放顺序:大端高字节存低地址,小端反之。x86/ARM默认小端,网络字节序统一为大端。跨平台、网络通信、二进制协议开发中必须显式处理字节序转换,否则数据解析必错。
1161 138
|
5月前
|
JavaScript 前端开发 Java
闭包:被误解最深的 JavaScript 特性
闭包本质是JS保留作用域上下文的能力,非语法而是引擎机制。它支撑私有变量封装、模块化与防抖/节流等高级模式。内存泄漏主因是未及时解除引用,而非闭包本身。理解闭包,方懂JS作用域与生命周期精髓。(239字)
|
5月前
|
存储 缓存 前端开发
前端状态管理的本质:不是框架插件,是数据流的秩序重建
状态管理不是“锦上添花”,而是前端从页面迈向系统时,解决数据流无序、跨层传值繁琐、变更难追踪等核心问题的必然选择。其本质是建立可预测、可追踪、可共享的数据秩序,关键在单一数据源、只读状态与逻辑分离。选型重适配,落地讲边界。