导读:本期聚焦于刘卫东创作的《C语言中sizeof和strlen的区别是什么?sizeof和strlen有什么区别》,敬请观看详情。sizeof和strlen都常被拿来计算长度,但一个在编译期求值,一个在运行时扫描。两者处理对象不同,返回值含义不同,混用容易导致内存分配不足或数组越界。本文从运算符和库函数的本质定位出发,对比两者在数组、指针、字符串字面量等场景下的实际表现,并通过代码示例演示典型误用。读完你会清楚什么情况下该用sizeof,什么情况下该用strlen,以及为什么用strlen申请内存时必须加一。

C语言中,sizeof和strlen都经常和“长度”这个概念联系在一起,但它们的底层机制完全不同。sizeof是一个运算符,在编译期根据类型或者变量的声明信息得到内存占用字节数;strlen是一个标准库函数,在运行期从指定地址开始逐个字符扫描,直到遇到字符串结束符\0才停止计数。如果把两者混为一谈,很容易在动态内存分配、数组拷贝和字符串处理中埋下隐患。

C语言中sizeof和strlen的区别是什么?sizeof和strlen有什么区别

一、本质定位:运算符与库函数的源头差异

sizeof是C语言的关键字,属于运算符家族,和+、-、*、/一样在语法层面被识别。它不需要包含任何头文件,编译器在生成代码时直接根据类型系统计算表达式结果。sizeof的结果是一个无符号整数,类型为size_t,表示对象或者类型所占用的字节数。对于变量来说,它取的是变量声明的类型大小,而不是变量内存中实际存储的数据长度。

strlen是定义在string.h头文件中的函数,它的原型是size_t strlen(const char *s);。函数内部是一个循环,从指针s指向的地址开始,逐字节判断是否为\0,不是则计数加一并移动指针。这个扫描过程发生在程序运行阶段,因此strlen的计算结果取决于字符串实际内容,而不是变量本身的大小。

用一个简单的例子就能看清两者差异:

#include <stdio.h>
#include <string.h>

int main(void) {
    char str[] = "hello";
    printf("sizeof(str) = %zu\n", sizeof(str));
    printf("strlen(str) = %zu\n", strlen(str));
    return 0;
}

这段代码的输出是sizeof(str)=6,而strlen(str)=5。原因在于sizeof将整个数组纳入计算,包括编译器在字符串末尾自动追加的\0,所以6个字节;strlen只统计hello五个可见字符,遇到\0就停下,因此返回5。这个例子直观地说明了运算符和函数在定位上的本质差异。

二、求值时机:编译期确定与运行时扫描

sizeof的求值发生在编译阶段,编译器会分析操作数的类型和声明大小,然后直接将结果替换为对应的整型常量。这意味着sizeof不会带来任何运行时开销,也不会真正读取变量内存中的内容。甚至对于未定义长度的数组,只要声明时能通过初始化推导出大小,sizeof也能在编译期得到正确结果。还可以用sizeof来定义数组长度,例如char buf[sizeof(int) * 4];,因为这时的sizeof已经是常量表达式。

strlen则必须在程序运行时真实地遍历内存。它每读取一个字符,就要做一次内存访问,直到遇到\0。如果字符串很长,strlen的时间复杂度就是O(n),并且无法通过任何方式提前得知结束位置。更关键的是,strlen要求传入的指针必须指向一个以\0结尾的合法字符串。如果字符数组没有结尾符,strlen会一直向后扫描,直到撞上非法的内存区域,甚至触发段错误。

这个区别直接影响编程习惯。凡是需要在编译期决定数组大小、计算结构体占用空间、分配临时缓冲区等场景,都用sizeof。凡是需要获取字符串的可显示长度、用于逐字符处理或逻辑判断时,都用strlen。拿strlen去计算字符数组的容量,或者拿sizeof去获取字符串的实际内容长度,都会得到错误结论。

三、数组与指针:两种容易被混淆的表达式

数组名在表达式中通常会被隐式转换为指向首元素的指针,但在sizeof和取地址操作中,数组名保持着数组类型本身。因此,sizeof(数组名)得到的是整个数组占用的总字节数,这是许多C语言教材反复强调的例外。而strlen接收的是const char*指针,当数组名作为参数传入时,自动发生“数组退化”,strlen看到的只是一个指针,它并不知道数组边界在哪里,只能靠\0来识别终点。

如果定义一个指针变量指向字符串字面量,sizeof和strlen的结果差异会更加明显:

#include <stdio.h>
#include <string.h>

int main(void) {
    const char *p = "hello";
    char arr[] = "hello";

    printf("sizeof(p) = %zu\n", sizeof(p));
    printf("sizeof(arr) = %zu\n", sizeof(arr));
    printf("strlen(p) = %zu\n", strlen(p));
    printf("strlen(arr) = %zu\n", strlen(arr));
    return 0;
}

在64位系统上,sizeof(p)输出8,因为p是指针变量,占8个字节;sizeof(arr)输出6,因为arr是数组,占6个字节。而strlen(p)和strlen(arr)都输出5,因为两者指向的内容都是hello加\0。这说明sizeof对指针只会返回指针本身的大小,绝不反映指针所指向的数据长度;strlen则永远只看指针指向的内存内容,和指针类型无关。

这一点在函数参数传递中尤其重要。当数组作为函数参数传递时,形参实际上被调整为指针,因此在函数内部使用sizeof(形参)得到的是一个指针大小,而不是传入数组的大小。比如:

#include <stdio.h>
#include <string.h>

void print_len(char str[]) {
    // 形参str本质上是指针,sizeof(str)得到的是指针大小
    printf("inside function: sizeof(str) = %zu\n", sizeof(str));
    printf("inside function: strlen(str) = %zu\n", strlen(str));
}

int main(void) {
    char data[] = "test";
    print_len(data);
    return 0;
}

输出结果中,函数内部的sizeof(str)是8(64位系统),而strlen(str)是4。实际数组data的大小在外部是5。如果程序员在函数内部用sizeof去计算数组元素个数,例如sizeof(str) / sizeof(str[0]),得到的结果将是8而不是5,这种情况下就必须把数组长度作为独立参数传入。

四、常见误用场景与实用建议

最常见的误用发生在动态内存分配时。很多人想要为一个字符串分配恰好容纳它的空间,于是写成char *buf = malloc(strlen(src));,结果拷贝时把\0丢在外面,导致后续操作越界。正确写法是malloc(strlen(src) + 1),多出来的一个字节用于存放字符串结束符。如果把这里的strlen换成sizeof,对于指针类型的src,sizeof只会返回指针大小,完全得不到内容长度,分配的内存或者过大,或者过小。

另一个常见误区是用strlen计算数组容量。比如读取文件内容到char buffer[1024]中,然后试图用strlen(buffer)来知道缓冲区被写入了多少字节。但如果文件内容本身包含\0,或者缓冲区没有被清空,strlen得到的结果就和真实数据长度不一致,甚至可能超过数组边界。这类场景应该使用read、fread等函数的返回值来确定实际读取长度,而不是依赖strlen。

针对实战中的需求,可以总结几条实用原则:

  • 计算类型占用的内存字节数、数组容量、结构体大小时,使用sizeof。
  • 获取字符串的有效字符个数、遍历字符串、判断字符串是否为空时,使用strlen。
  • 申请字符串容量的内存时,一定要在strlen的结果上加上1,为\0留出位置。
  • 将数组传入函数后,函数内无法用sizeof获得原数组大小,需要单独传递长度。
  • strlen要求参数必须是以\0结尾的合法地址,否则会越界扫描,这种错误极难排查。

理解sizeof和strlen的区别,不只是在面试中回答一个概念题,更直接关系到代码的安全性和可维护性。记住编译期和运行期这条分界线,远离未定义行为,C语言的内存操作就会可靠得多。

sizeofstrlen区别修改时间:2026-08-27 07:51:48

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