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

优化`strlen()`:从原理到代码审查实践

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

优化`strlen()`:从原理到代码审查实践

引用
CSDN
9
来源
1.
https://blog.csdn.net/lonelysky/article/details/6614422
2.
https://blog.csdn.net/weixin_65931202/article/details/133608862
3.
https://blog.csdn.net/nplplus/article/details/145196544
4.
https://blog.csdn.net/tianmohust/article/details/6667111
5.
https://blog.csdn.net/fengbingchun/article/details/107282503
6.
https://cloud.tencent.com/developer/information/strlen%E6%80%A7%E8%83%BD%E5%AE%9E%E7%8E%B0
7.
https://juejin.cn/post/7154559908069769223
8.
https://docs.pingcode.com/baike/1251388
9.
https://my.oschina.net/emacs_8752108/blog/17179415

在C语言编程中,strlen()函数用于计算字符串的长度,是字符串处理中最常用的函数之一。然而,随着软件系统越来越复杂,对性能的要求也越来越高,传统的strlen()实现已经难以满足高性能需求。本文将介绍一种针对现代CPU优化的strlen()实现,并探讨如何利用这种优化版本提升代码审查效率。

01

优化实现原理

传统的strlen()实现是逐个字节地检查字符串中的字符,直到遇到字符串结束符\0为止。这种实现虽然简单直观,但效率较低,特别是在处理长字符串时。为了提高性能,可以利用现代CPU的特性,一次读取多个字节进行检查。

[[1]] 提供了一种针对32位CPU优化的strlen()实现,其核心思想是:

  1. 首先进行内存对齐检查,如果在对齐之前遇到\0则直接返回。
  2. 然后一次读取一个WORD(4字节)的数据,使用位操作快速检查其中是否包含\0
  3. 如果发现包含\0,则进一步精确定位到具体的字节位置。

下面是具体的实现代码:

typedef unsigned long ulong;

size_t strlen_c(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;
        }
    }
}

这段代码的关键在于使用了magic_bits(0x7efefeffL)和位操作来检测字符串中的空字符。具体来说:

  • magic_bits的二进制表示为:01111110 11111110 11111110 11111111,每个字节的最高位左侧都有一个"hole"(0位)。
  • 通过longword + magic_bits操作,如果某个字节为0,则进位会落在该字节左侧的hole上,而不会影响更高位的hole。
  • 使用^ ~longword& ~magic_bits操作,可以检测出哪些hole没有发生变化,从而判断出字符串中是否存在空字符。

这种优化方法充分利用了现代CPU的数据宽度,可以显著提高字符串长度计算的效率。

02

代码审查要点

在代码审查中,需要特别关注strlen()的以下使用场景:

  1. 字符串必须以\0结尾:如果字符串没有以\0结尾,strlen()会继续读取内存,直到意外碰到\0,这可能导致访问越界或程序崩溃。

    错误示例

    char badStr[] = {'a', 'b', 'c', 'd', 'e', 'f'};
    size_t len = strlen(badStr);  // 结果不可预测
    

    正确示例

    char goodStr[] = "abcdef";
    size_t len = strlen(goodStr);  // 正确计算长度
    
  2. 传入有效指针strlen()的参数必须是一个有效的内存地址,不能是空指针。

    错误示例

    char *nullPtr = NULL;
    size_t len = strlen(nullPtr);  // 导致程序崩溃
    
  3. 注意返回值类型strlen()的返回值是size_t类型,这是一个无符号整数类型。在进行比较运算时需要注意类型转换问题。

    错误示例

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

    正确示例

    if ((ssize_t)strlen("abc") - (ssize_t)strlen("abcdef") > 0) {
        printf(">=\n");
    } else {
        printf("<\n");
    }
    
03

实践建议

在代码审查中,可以采用以下方法来检查strlen()的使用是否安全和高效:

  1. 静态代码分析工具:使用如Splint、PVS-Studio等静态代码分析工具,可以自动检测出潜在的strlen()使用错误。

  2. 代码走查:在团队代码审查时,重点关注字符串处理部分,检查是否遵循了上述使用要点。

  3. 单元测试:编写针对字符串处理函数的单元测试,特别是边界条件测试,确保strlen()的使用不会导致程序崩溃或产生错误结果。

  4. 代码规范:在团队的编码规范中明确指出strlen()的正确使用方法,避免新手犯错。

通过以上方法,可以有效地提升代码审查的效率和质量,减少因strlen()使用不当导致的bug。

04

结论

优化后的strlen()通过利用现代CPU的数据宽度,显著提高了字符串长度计算的效率。在代码审查中,通过关注字符串的空终止符、有效指针和返回值类型等问题,可以确保strlen()的正确使用,从而提升代码质量和运行效率。这种优化不仅适用于性能敏感的场景,更能在日常开发中帮助我们写出更健壮的代码。

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