问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

程序员必知:正确使用`strlen()`避坑指南

创作时间:
作者:
@小白创作中心

程序员必知:正确使用`strlen()`避坑指南

引用
CSDN
11
来源
1.
https://blog.csdn.net/NICHUN12345/article/details/120387743
2.
https://blog.csdn.net/qq_53101718/article/details/124500945
3.
https://blog.csdn.net/jijie_ming/article/details/137333969
4.
https://blog.csdn.net/lujiandong1/article/details/70193877
5.
https://blog.csdn.net/lonelysky/article/details/6614422
6.
https://blog.csdn.net/Wuzm_/article/details/103096778
7.
https://blog.csdn.net/Hashmat/article/details/6054046
8.
https://www.cnblogs.com/lqerio/p/12870816.html
9.
https://developer.aliyun.com/article/1089884
10.
https://learn.microsoft.com/zh-cn/cpp/c-runtime-library/reference/strlen-wcslen-mbslen-mbslen-l-mbstrlen-mbstrlen-l?view=msvc-170
11.
https://developer.aliyun.com/article/1192735

在C语言编程中,strlen()函数用于计算字符串的实际长度,是每个程序员必备的基础知识。然而,这个看似简单的函数却隐藏着不少陷阱,不当使用会导致程序出错甚至引发安全问题。本文将详细介绍strlen()的常见错误用法,并提供相应的解决方案,帮助你写出更安全、更高效的代码。

01

strlen()的基本功能

strlen()函数用于计算字符串的实际字符数,不包括结尾的空字符\0。其函数原型为:

size_t strlen(const char *s);

其中,size_t是无符号整数类型,返回值表示字符串的长度。

02

常见错误用法及解决方案

1. 字符串未以'\0'结尾

错误示例:

char arr[] = {'a', 'b', 'c'};
printf("%zu\n", strlen(arr));  // 输出结果不确定

在上面的代码中,字符数组arr没有以\0结尾,strlen()会继续扫描后面的内存直到遇到\0,这可能导致访问未分配的内存区域,结果是不可预测的。

解决方案:确保所有字符串都以\0结尾。

char arr[] = {'a', 'b', 'c', '\0'};
printf("%zu\n", strlen(arr));  // 正确输出3

2. 字符数组部分初始化

错误示例:

char arr[5] = {'a', 'b', 'c', 'd'};
printf("%zu\n", strlen(arr));  // 输出4

在这个例子中,虽然只初始化了前4个元素,但未初始化的部分会被自动填充为0,相当于字符串的结束符\0。因此,strlen()返回的长度是4,而不是数组的实际大小。

解决方案:如果需要计算数组大小,应使用sizeof()而不是strlen()

3. 无符号数溢出问题

错误示例:

if (strlen("abc") - strlen("abcdef") > 0) {
    printf("hehe\n");
} else {
    printf("haha\n");
}

这段代码会输出"hehe",因为strlen()的返回值是无符号类型,两个无符号数相减的结果仍然是无符号数,不会产生负数。

解决方案:在进行比较时,注意无符号数的特性,避免直接相减。

4. 缓冲区溢出风险

错误示例:

char buf[10];
strcpy(buf, argv[1]);  // 可能导致缓冲区溢出

argv[1]的长度超过9个字符时(包括结尾的\0),strcpy()会导致缓冲区溢出,覆盖相邻的内存区域。

解决方案:使用strncpy()等安全函数,限制复制的字符数量。

char buf[10];
strncpy(buf, argv[1], sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';  // 确保字符串以'\0'结尾

5. 越界访问问题

错误示例:

char arr[] = {'a', 'b', 'c'};
printf("%zu\n", strlen(arr));  // 可能访问未分配的内存

与第一个问题类似,未正确终止的字符串可能导致strlen()访问超出数组范围的内存。

解决方案:确保字符串正确终止,避免越界访问。

6. 类型转换问题

错误示例:

void *ptr = "hello";
printf("%zu\n", strlen(ptr));  // 类型不匹配

这里将char*类型的字符串转换为void*类型,会导致编译错误或运行时错误。

解决方案:确保函数参数类型匹配,避免不必要的类型转换。

03

安全使用指南

  1. 始终确保字符串以'\0'结尾:这是使用strlen()的前提条件,任何未正确终止的字符串都可能导致错误结果。

  2. 使用安全的字符串处理函数:如strncpy()snprintf()等,它们允许指定最大复制长度,可以有效防止缓冲区溢出。

  3. 注意无符号数运算的特殊性strlen()的返回值是size_t类型,进行算术运算时要特别小心,避免意外的溢出或错误结果。

  4. 区分strlen()sizeof()的使用场景strlen()用于计算字符串长度,而sizeof()用于获取变量或类型的内存占用大小。

04

性能优化建议

虽然标准库提供的strlen()函数已经足够高效,但在某些对性能要求极高的场景下,可以考虑使用更优化的实现。例如,通过一次读取多个字节来加速字符串长度的计算。

typedef unsigned long ulong;

size_t strlen_optimized(const char *str) {
    const char *char_ptr;
    const ulong *longword_ptr;
    register ulong longword, magic_bits;

    // 数据对齐处理
    for (char_ptr = str; ((ulong)char_ptr & (sizeof(ulong) - 1)) != 0; ++char_ptr) {
        if (*char_ptr == '\0') return char_ptr - str;
    }

    longword_ptr = (ulong*)char_ptr;
    magic_bits = 0x7efefeffL;

    while (1) {
        longword = *longword_ptr++;
        if ((((longword + magic_bits) ^ ~longword) & ~magic_bits) != 0) {
            const char *cp = (const char*)(longword_ptr - 1);
            if (cp[0] == 0) return cp - str;
            if (cp[1] == 0) return cp - str + 1;
            if (cp[2] == 0) return cp - str + 2;
            if (cp[3] == 0) return cp - str + 3;
        }
    }
}

这个优化版本通过位操作和字节对齐技术,显著提高了字符串长度计算的效率。不过,对于大多数应用场景来说,标准库函数已经足够使用,不建议为了微小的性能提升而牺牲代码的可读性和可维护性。

通过掌握这些使用技巧和注意事项,你可以更安全、更高效地使用strlen()函数,避免常见的陷阱,写出更高质量的C语言代码。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号