sizeof在C语言中的作用是什么?详解用法与常见坑点

来源:网站主作者:何守业头衔:网络博主
导读:本期聚焦于何守业创作的《sizeof在C语言中的作用是什么?详解用法与常见坑点》,敬请观看详情。sizeof是C语言中一个容易被低估的运算符,它在编译期就能确定数据类型或变量所占内存的字节数。本文围绕sizeof的作用展开,先讲清它的基本语法和求值时机,再通过代码示例演示如何对基本类型、数组、指针、结构体求大小,重点剖析数组名与指针在传参后sizeof结果不同的原因,以及结构体内存对齐带来的字节数膨胀问题。文末还整理了使用sizeof时的常见误区,比如sizeof(指针)不等于数组长度、size_t与int比较引发的符号问题等,帮助你写出更可靠的C代码。

sizeof是C语言里一个很特别的存在:它长得像函数,实际却是运算符;它出现在表达式里,却在编译阶段就能算出结果。不少初学者把它当成“求数组长度”的函数来用,结果在数组作为参数传递的函数里拿到了意想不到的值,或者被结构体的内存对齐搞得一头雾水。这篇文章就来把sizeof的作用、求值规则和常见坑点讲透。

sizeof在C语言中的作用是什么?详解用法与常见坑点

一、sizeof到底是什么:运算符而非函数

首先纠正一个普遍的误解:sizeof不是函数,而是C语言的关键字、一个编译期运算符。它的作用是求出一个操作数在内存中占用的字节数,结果类型是size_t(无符号整数类型,定义在stddef.h中)。写成sizeof(x)带括号只是习惯写法,当操作数是类型名时括号是必须的,比如sizeof(int);当操作数是变量时括号可以省略,比如int a; sizeof a;也是合法的。

sizeof最关键的特性是:它几乎总在编译期完成求值(C99引入变长数组VLA后是唯一的例外)。也就是说,编译器在生成目标代码时就已经把sizeof替换成一个常数值了,运行时不会执行任何计算,更不会对表达式产生副作用。看下面这个经典例子:

#include <stdio.h>

int main(void)
{
    int a = 10;
    printf("before: %d\n", a);
    printf("sizeof size: %zu\n", sizeof(a++));  /* a++不会被执行 */
    printf("after:  %d\n", a);
    return 0;
}

这段代码里sizeof(a++)并不会让a自增,输出的after值仍然是10。因为sizeof只关心操作数的类型,不去真正求值表达式,编译器直接用sizeof(int)也就是4替代了整个表达式。这个特性意味着sizeof几乎零运行时开销,非常适合用在数组遍历、内存分配等场景。

二、sizeof的各种求值场景与代码示例

sizeof可以作用于基本类型、变量、数组、指针、结构体甚至函数返回值类型。不同对象的结果差异很大,需要逐个理解。先看一组常见类型的字节数:

#include <stdio.h>

int main(void)
{
    printf("char      : %zu\n", sizeof(char));      /* 恒为1 */
    printf("int       : %zu\n", sizeof(int));       /* 常见为4 */
    printf("double    : %zu\n", sizeof(double));    /* 常见为8 */
    printf("指针      : %zu\n", sizeof(int*));      /* 64位系统为8 */

    int arr[10];
    printf("数组      : %zu\n", sizeof(arr));       /* 40 = 10 * 4 */
    printf("元素个数  : %zu\n", sizeof(arr) / sizeof(arr[0]));  /* 10 */

    return 0;
}

这里最实用的技巧就是sizeof(arr) / sizeof(arr[0]),即用数组总字节数除以单个元素的字节数,得到元素个数。这是C语言里遍历数组的标准写法,比硬编码数字更安全,以后修改数组长度时循环代码完全不用动。

需要注意的是,sizeof对数组求值得到的是整个数组占用的字节数,对指针求值得到的却是指针变量本身的字节数。在64位平台上无论指针指向什么,sizeof(指针)都是8;而32位平台上是4。这个区别是无数bug的源头,下一节会展开讨论。

三、数组传参后sizeof失效:最经典的坑

很多初学者写过这样的代码:在main里用sizeof求数组长度没问题,把数组传给函数后sizeof却变成了固定值。比如下面这段:

#include <stdio.h>

void print_size(int arr[])
{
    /* 数组作为参数退化为指针,sizeof得到的是指针大小 */
    printf("in function: %zu\n", sizeof(arr));  /* 64位系统输出8 */
}

int main(void)
{
    int arr[10];
    printf("in main    : %zu\n", sizeof(arr));  /* 输出40 */
    print_size(arr);
    return 0;
}

原因在于C语言的数组退化规则:数组作为函数参数传递时,会自动退化为指向首元素的指针。函数参数列表里写int arr[]或者int arr[10],编译器实际上都按int *arr处理。所以函数内部对arr做sizeof,得到的只是指针本身的大小。

正确的做法是把长度作为额外参数一起传进去,例如void func(int arr[], int len),调用时用func(arr, sizeof(arr) / sizeof(arr[0]))。这也是为什么标准库的很多函数(比如memcpy、qsort)都要求调用者显式传入长度或元素个数。理解了数组退化,就能明白为什么sizeof只能在数组定义所在的作用域内正确工作。

四、结构体与内存对齐:sizeof结果为何比想象中大

对结构体使用sizeof时,结果常常比所有成员大小之和还要大,这是内存对齐造成的。编译器为了提高CPU访问内存的效率,会在成员之间插入填充字节,让每个成员的地址落在其自身对齐要求的边界上。看这个例子:

#include <stdio.h>

struct A {
    char  c;   /* 1字节,但后面填充3字节 */
    int   i;   /* 4字节 */
    char  d;   /* 1字节,结构体尾部填充3字节 */
};

struct B {
    int   i;   /* 4字节 */
    char  c;   /* 1字节 */
    char  d;   /* 1字节,尾部填充2字节 */
};

int main(void)
{
    printf("sizeof(struct A) = %zu\n", sizeof(struct A));  /* 12 */
    printf("sizeof(struct B) = %zu\n", sizeof(struct B));  /* 8 */
    return 0;
}

结构体A和B的成员完全相同,只是顺序不同,大小却分别是12和8。A中char c后面要填充3字节让int i对齐到4的倍数,结构体总大小还必须是最大成员对齐值的倍数,所以尾部又补了3字节。这个现象说明:合理安排结构体成员顺序(把大成员放前面)可以有效节省内存。在做网络传输或文件存储时,如果需要紧凑布局,可以使用#pragma pack(1)取消填充,但要清楚这会牺牲一部分访问效率。

五、使用sizeof的其他常见误区

除了数组退化,还有几个坑值得留意。第一个是符号问题:sizeof返回的是无符号的size_t,如果拿它和负数int比较,int会被隐式转换成无符号数,导致判断逻辑完全颠倒。经典死循环写法:

#include <stdio.h>

int main(void)
{
    int i = -1;
    /* i被提升为无符号数,变成一个巨大的正数,条件恒成立 */
    if (i < sizeof(int)) {
        printf("这行会执行,逻辑出错\n");
    }
    return 0;
}

第二个坑是对字符串求sizeof和strlen分不清。sizeof("hello")得到6,因为它算的是字符数组包括结尾'\0'的总大小;而strlen("hello")得到5,它数的是'\0'之前的字符个数。一个算内存,一个算长度,语义完全不同。此外,sizeof对函数调用表达式求值时,得到的是函数返回类型的大小,函数本身并不会被调用。

总结一下,sizeof的核心作用是编译期计算类型或对象占用的字节数,它是实现可移植代码的重要工具。配合sizeof(arr) / sizeof(arr[0])求数组元素个数、用malloc(sizeof(struct X))动态分配内存,都是日常开发的标准操作。只要记住数组退化、内存对齐和无符号比较这三个坑,就能把sizeof用得又稳又准。

sizeofC语言sizeof运算符修改时间:2026-09-15 02:12:33

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260915/56971.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。