模拟实现strlen()函数 (C语言)

C字符串长度 - strlen()

一、strlen() 函数概览

strlen() 是 C 标准库中用于计算字符串长度的函数,声明在 <string.h> 中。

c 复制代码
size_t strlen(const char *str);

功能

计算字符串 str 的长度,直到遇到空结束字符 '\0' 为止,但不包括 '\0'。

参数

  • str:指向以 '\0' 结尾的字符串。

返回值

  • 返回字符串中字符的个数,类型为 size_t。
  • size_t 是无符号整数类型,因此不要用 int 接收后直接参与可能为负的比较。

示例:

c 复制代码
#include <stdio.h>
#include <string.h>

int main(void)
{
    const char *s = "hello";
    printf("%zu\n", strlen(s)); // 输出 5
    return 0;
}

二、strlen() 的模拟实现

理解 strlen() 的最好方式之一,就是自己模拟实现它。下面给出三种常见写法。

1. 计数器方式

c 复制代码
#include <stddef.h>

size_t my_strlen(const char *str)
{
    size_t count = 0;

    while (*str != '\0') {
        ++count;
        ++str;
    }

    return count;
}

说明:

  • 从字符串首地址开始逐个字符检查。
  • 每遇到一个非 '\0' 字符,计数器加一。
  • 直到遇到 '\0' 停止。
  • 时间复杂度为 O(n)。

也可以写成:

c 复制代码
while (*str) {
    ++count;
    ++str;
}

因为 '\0' 的 ASCII 值为 0,在条件判断中相当于假。


2. 递归方式

c 复制代码
#include <stddef.h>

size_t my_strlen(const char *str)
{
    if (*str == '\0')
        return 0;

    return 1 + my_strlen(str + 1);
}

说明:

  • 如果当前字符是 '\0',说明到达字符串末尾,返回 0。
  • 否则当前字符算作 1 个长度,再递归计算后续字符串长度。
  • 代码简洁,但每次递归都会产生函数调用开销。
  • 对于非常长的字符串,可能导致栈溢出,实际工程中不推荐。

3. 指针相减方式

c 复制代码
#include <stddef.h>

size_t my_strlen(const char *s)
{
    const char *p = s;

    while (*p != '\0') {
        ++p;
    }

    return (size_t)(p - s);
}

说明:

  • 用 p 从字符串开头向后移动,直到遇到 '\0'。
  • 最终 p - s 就是两个指针之间的元素个数,即字符串长度。
  • 指针相减的结果类型是 ptrdiff_t,这里转换为 size_t 返回。
  • 这种写法简洁高效,是常见的模拟实现方式。

三种模拟实现对比

实现方式 优点 缺点
计数器 直观易懂 需要额外计数变量
递归 代码简洁 栈开销大,长字符串可能溢出
指针相减 简洁高效 需要理解指针运算

三、sizeof 与 strlen 的核心区别

sizeof 和 strlen 经常被初学者混淆,但它们完全不同。

对比项 sizeof strlen
本质 运算符 库函数
头文件 语言内置,无需头文件 <string.h>
计算内容 类型或对象占用的内存字节数 字符串中字符个数
是否包含 '\0' 对字符串数组/字面量通常包含 不包含
适用类型 任意类型 只能用于以 '\0' 结尾的字符串
求值时机 通常编译时,VLA 除外 运行时
返回值类型 size_t size_t
是否受数组退化影响 数组名得到数组大小,指针得到指针大小 都按指向的字符串计算

四、典型示例

c 复制代码
#include <stdio.h>
#include <string.h>

int main(void)
{
    char s1[] = "hello";
    char *s2 = "hello";
    char s3[100] = "hello";

    printf("sizeof(s1) = %zu\n", sizeof(s1)); // 6
    printf("strlen(s1) = %zu\n", strlen(s1)); // 5

    printf("sizeof(s2) = %zu\n", sizeof(s2)); // 4 或 8,取决于平台
    printf("strlen(s2) = %zu\n", strlen(s2)); // 5

    printf("sizeof(s3) = %zu\n", sizeof(s3)); // 100
    printf("strlen(s3) = %zu\n", strlen(s3)); // 5

    printf("sizeof(\"hello\") = %zu\n", sizeof("hello")); // 6
    printf("strlen(\"hello\") = %zu\n", strlen("hello")); // 5

    return 0;
}

关键点:

  • char s1[] = "hello"; 是字符数组,sizeof(s1) 包含末尾的 '\0',所以是 6。
  • char *s2 = "hello"; 中 s2 是指针,sizeof(s2) 得到的是指针大小,不是字符串长度。
  • char s3[100] = "hello"; 中数组大小固定为 100,sizeof(s3) 是 100,但 strlen(s3) 仍然是 5。

五、常见陷阱与注意事项

1. 字符串必须由 '\0' 结尾

c 复制代码
char buf[5] = {'h', 'e', 'l', 'l', 'o'};
printf("%zu\n", strlen(buf)); // 错误:buf 没有 '\0',越界访问,行为未定义

strlen() 会一直向后寻找 '\0',如果字符串没有正确终止,就会越界读取,导致未定义行为。


2. 返回类型是 size_t,不是 int

c 复制代码
if (strlen(s) - 1 >= 0) {
    // 可能永远为真
}

因为 strlen() 返回 size_t,strlen(s) - 1 仍然是无符号数。如果 strlen(s) 为 0,结果会变成一个很大的正数。

建议显式比较:

c 复制代码
if (strlen(s) > 0) {
    // ...
}

或先转换为有符号类型,但要确保长度不会溢出。


3. 函数参数中的数组会退化为指针

c 复制代码
void f(char arr[])
{
    printf("%zu\n", sizeof(arr)); // 输出指针大小,不是数组大小
}

在函数参数中,char arr[] 等价于 char *arr,所以 sizeof 得到的是指针大小。


4. strlen 不适用于二进制数据

如果数据中间可能包含 '\0',例如二进制文件内容、网络协议包等,不能使用 strlen() 计算长度。应使用 memchr() 或额外保存长度。


5. 标准库实现可能经过高度优化

标准库中的 strlen() 通常不是简单的逐字节循环,可能按机器字长读取并检测零字节,以提高性能。但语义始终不变:返回 '\0' 之前的字符数。


六、总结

  • strlen() 用于计算以 '\0' 结尾的字符串长度,不包含 '\0'。
  • 可以通过计数器、递归、指针相减等方式模拟实现。
  • 工程中推荐使用标准库 strlen(),并注意其返回类型是 size_t。
  • sizeof 是运算符,计算对象或类型占用的字节数;strlen 是函数,计算字符串字符个数。
  • 使用 strlen() 前必须确保字符串已经以 '\0' 正确结尾,否则会越界访问,导致未定义行为。

一句话记忆:

sizeof 看内存大小,strlen 数字符个数;sizeof 常含 '\0',strlen 不含 '\0'。

相关推荐
llqbzllll7 小时前
Spring AI 工具调用不是反射一下就结束:用 2.0.1 跑通失败恢复与调用上限
人工智能·后端
小灰灰搞电子7 小时前
C语言 <time.h> 头文件详解
c语言·time.h
狼爷8 小时前
Rust/Go/Java/Python/PHP 大比拼:负载下后端框架到底差多少?
java·后端·编程语言
C++ 老炮儿的技术栈9 小时前
sizeof操作符
c语言·c++·人工智能·mfc·c
mantou1329 小时前
我给 AI Agent 做了个「油猴」:让 Claude Code / Codex 直接用你已登录的浏览器
前端·javascript·后端
程序员老赵9 小时前
Docker 部署 DeepSeek Harness:轻松搭建局域网里的 AI Agent 平台
后端·ai编程·deepseek
YIAN9 小时前
实战|用 DeepSeek + SQLite 从零搭建轻量 Text2SQL 查询助手
后端·sqlite·deepseek
王中阳Go9 小时前
自己摸了 2 个月零 offer,补底子只用了 3 块:Go 后端转 AI 最难的不是技术
后端·agent·ai编程
高频因子挖掘机9 小时前
第一次补历史行情:按股票拆,还是按日期拆请求?
后端·github·api
小园子的小菜9 小时前
Python协程深度解析:从原理演进到实战避坑
后端·python