zoukankan      html  css  js  c++  java
  • strcat的几种实现及性能比较

    一  原型说明

         strcat()为C语言标准库函数,用于字符串拼接。函数原型声明在string.h头文件中:

    char *strcat(char *dest, const char *src);

         该函数将参数src所指字符串拷贝到参数dest所指字符串的结尾处(覆盖dest结尾处的'')并添加''。其返回值为参数dest所指字符串的起始地址。注意,dest必须有足够的空间来容纳要拷贝的src字符串。

         本文将给出strcat函数的几种实现,并比较其执行效率。代码运行环境如下:

     

    二  代码实现

    2.1 汇编实现

     1 char *AssemStrcat(char *pszDest, const char *pszSrc)
     2 {
     3     int d0, d1, d2, d3;
     4     __asm__ __volatile__(
     5         "repne
    	"
     6         "scasb
    	"
     7         "decl %1
    "
     8         "1:	lodsb
    	"
     9         "stosb
    	"
    10         "testb %%al,%%al
    	"
    11         "jne 1b"
    12         : "=&S" (d0), "=&D" (d1), "=&a" (d2), "=&c" (d3)
    13         : "0" (pszSrc), "1" (pszDest), "2" (0), "3" (0xffffffff):"memory");
    14     return pszDest;
    15 }

    2.2 模拟C库

     1 char *SimuStrcat(char *pszDest, const char *pszSrc)
     2 {
     3     char *pszOrigDst = pszDest;
     4 
     5     while(*pszDest)
     6         pszDest++;
     7     while((*pszDest++ = *pszSrc++) != '')
     8         ;
     9 
    10     return pszOrigDst;
    11 }

         因strcat的C标准库函数的实现方式未知,故使用SimuStrcat函数模拟标准库实现。

    2.3 快速拼接

         由strcat函数的原型说明可知,每次调用时都必须扫描整个目的字符串(以寻找结束符)。这会降低该函数的执行效率,尤其是在频繁拼接时。

         FastStrcat函数每次返回拼接后的字符串尾部,即指向结束符所在位置。下次调用时便不再需要扫描整串,从而提高执行效率。

    1 char *FastStrcat(char *pszDest, const char* pszSrc)
    2 {
    3     while(*pszDest)
    4         pszDest++;
    5     while((*pszDest++ = *pszSrc++));
    6     return --pszDest;
    7 }

         注意,第二个while循环若不加双层括号则会报”suggest parentheses around assignment used as truth value”的警告。因返回时对pszDest作减法运算,故单次执行时FastStrcat慢于SimuStrcat。

         为提高执行速度,本节函数实现均未对指针参数做判空处理。若兼求安全性,可在函数内使用assert断言指针合法性。Gcc编译器还对函数声明提供一种nonnull扩展属性,可在编译时进行有限的判空保护:

     1 char *FastStrcat(char *pszDest, const char* pszSrc)__attribute__((__nonnull__(1, 2)));
     2 char *NullPtr(void){return NULL;}
     3 int main(void)
     4 {
     5     char *pszBuf = NULL;
     6     FastStrcat(pszBuf, "Elizabeth ");
     7     FastStrcat(NullPtr(), "Elizabeth ");
    8 FastStrcat(NullPtr()?pszBuf:NULL, "Elizabeth ");
    9 FastStrcat(NULL, "Elizabeth "); 10 return 0; 11 }

         其中,__nonnull__(1, 2)指示编译器对FastStrcat函数调用的第一个和第二个参数判空。

         编译结果如下:

    1 [wangxiaoyuan_@localhost test1]$ gcc -Wall -o test test.c
    2 test.c: In function 'main':
    3 test.c:8: warning: null argument where non-null required (argument 1)
    4 test.c:9: warning: null argument where non-null required (argument 1)

         可见,除非将指针参数显式地置空,否则nonnull机制也检测不到。此外,使用nonnull机制时,若打开编译优化选项-O2,则函数内关于指针参数的校验将被优化掉。

     

    三  性能比较

         本节采用《Linux用户态程序计时方式详解》一文的TIME_ELAPSED()宏进行程序计时。

    1 #define TIME_ELAPSED(codeToTime) do{ 
    2     struct timeval beginTime, endTime; 
    3     gettimeofday(&beginTime, NULL); 
    4     {codeToTime;} 
    5     gettimeofday(&endTime, NULL); 
    6     long secTime  = endTime.tv_sec - beginTime.tv_sec; 
    7     long usecTime = endTime.tv_usec - beginTime.tv_usec; 
    8     printf("[%s(%d)]Elapsed Time: SecTime = %lds, UsecTime = %ldus!
    ", __FILE__, __LINE__, secTime, usecTime); 
    9 }while(0)

         基于该宏,编写测试代码如下:

     1 #ifdef strcat //检查标准库strcat是否用宏实现
     2     #warning strcat has been defined!
     3 #endif 
     4 int main(void)
     5 {
     6     char szCatBuf[1000];
     7     szCatBuf[0] = ''; //字符串快速初始化
     8 
     9     char *pszBuf = szCatBuf;
    10     TIME_ELAPSED(
    11         pszBuf = FastStrcat(pszBuf, "Abraham, ");
    12         pszBuf = FastStrcat(pszBuf, "Alexander, ");
    13         pszBuf = FastStrcat(pszBuf, "Maximilian, ");
    14         pszBuf = FastStrcat(pszBuf, "Valentine ")
    15     );
    16     printf("  [FastStrcat]szCatBuf = %s
    ", szCatBuf);
    17 
    18     szCatBuf[0] = '';
    19     TIME_ELAPSED(
    20         SimuStrcat(szCatBuf, "Abraham, ");
    21         SimuStrcat(szCatBuf, "Alexander, ");
    22         SimuStrcat(szCatBuf, "Maximilian, ");
    23         SimuStrcat(szCatBuf, "Valentine ")
    24     );
    25     printf("  [SimuStrcat]szCatBuf = %s
    ", szCatBuf);
    26 
    27     szCatBuf[0] = '';
    28     TIME_ELAPSED(
    29         AssemStrcat(szCatBuf, "Abraham, ");
    30         AssemStrcat(szCatBuf, "Alexander, ");
    31         AssemStrcat(szCatBuf, "Maximilian, ");
    32         AssemStrcat(szCatBuf, "Valentine ")
    33     );
    34     printf("  [AssemStrcat]szCatBuf = %s
    ", szCatBuf);
    35 
    36     szCatBuf[0] = '';
    37     TIME_ELAPSED(
    38         strcat(szCatBuf, "Abraham, ");
    39         strcat(szCatBuf, "Alexander, ");
    40         strcat(szCatBuf, "Maximilian, ");
    41         strcat(szCatBuf, "Valentine ")
    42     );
    43     printf("  [LibStrcat]szCatBuf = %s
    ", szCatBuf);
    44 
    45     szCatBuf[0] = '';
    46     TIME_ELAPSED(
    47         sprintf(szCatBuf, "%s%s%s%s","Abraham, ", "Alexander, ", "Maximilian, ", "Valentine ")
    48     );
    49     printf("  [LibSprintf]szCatBuf = %s
    ", szCatBuf);
    50 
    51     return 0;
    52 }

         除上节三种strcat实现外,代码还对齐库实现及sprintf方式进行测试。测试结果如下:

     1 [wangxiaoyuan_@localhost test1]$ gcc -Wall -o test test.c
     2 [wangxiaoyuan_@localhost test1]$ ./test
     3 [test.c(15)]Elapsed Time: SecTime = 0s, UsecTime = 2us!
     4   [FastStrcat]szCatBuf = Abraham, Alexander, Maximilian, Valentine 
     5 [test.c(24)]Elapsed Time: SecTime = 0s, UsecTime = 2us!
     6   [SimuStrcat]szCatBuf = Abraham, Alexander, Maximilian, Valentine 
     7 [test.c(33)]Elapsed Time: SecTime = 0s, UsecTime = 1us!
     8   [AssemStrcat]szCatBuf = Abraham, Alexander, Maximilian, Valentine 
     9 [test.c(42)]Elapsed Time: SecTime = 0s, UsecTime = 1us!
    10   [LibStrcat]szCatBuf = Abraham, Alexander, Maximilian, Valentine 
    11 [test.c(48)]Elapsed Time: SecTime = 0s, UsecTime = 6us!
    12   [LibSprintf]szCatBuf = Abraham, Alexander, Maximilian, Valentine

         因每次测试仅调用一次待测函数,故计时不太精准。对比另一次测试结果:

     1 [wangxiaoyuan_@localhost test1]$ ./test
     2 [test.c(15)]Elapsed Time: SecTime = 0s, UsecTime = 4us!
     3   [FastStrcat]szCatBuf = Abraham, Alexander, Maximilian, Valentine 
     4 [test.c(24)]Elapsed Time: SecTime = 0s, UsecTime = 4us!
     5   [SimuStrcat]szCatBuf = Abraham, Alexander, Maximilian, Valentine 
     6 [test.c(33)]Elapsed Time: SecTime = 0s, UsecTime = 3us!
     7   [AssemStrcat]szCatBuf = Abraham, Alexander, Maximilian, Valentine 
     8 [test.c(42)]Elapsed Time: SecTime = 0s, UsecTime = 2us!
     9   [LibStrcat]szCatBuf = Abraham, Alexander, Maximilian, Valentine 
    10 [test.c(48)]Elapsed Time: SecTime = 0s, UsecTime = 15us!
    11   [LibSprintf]szCatBuf = Abraham, Alexander, Maximilian, Valentine

         可见,单次执行时,strcat库函数最快,FastStrcat居中,sprintf库函数最慢。

         接着,比较批量执行时strcat库函数和FastStrcat的速度。测试代码如下:

     1 int main(void)
     2 {
     3 #define ROUND    (unsigned short)1000
     4     char szCatBuf[sizeof("Elizabeth ")*ROUND];
     5     szCatBuf[0] = '';
     6 
     7     char *pszBuf = szCatBuf;
     8     TIME_ELAPSED(
     9         unsigned short wIdx = 0;
    10         for(; wIdx < ROUND; wIdx++)
    11             pszBuf = FastStrcat(pszBuf, "Elizabeth ");
    12     );
    13 
    14     szCatBuf[0] = '';
    15     TIME_ELAPSED(
    16         unsigned short wIdx = 0;
    17         for(; wIdx < ROUND; wIdx++)
    18             strcat(szCatBuf, "Elizabeth ");
    19     );
    20 
    21     return 0;
    22 }

         测试结果如下:

    1 [wangxiaoyuan_@localhost test1]$ gcc -Wall -o test test.c
    2 [wangxiaoyuan_@localhost test1]$ ./test
    3 [test.c(12)]Elapsed Time: SecTime = 0s, UsecTime = 99us!
    4 [test.c(19)]Elapsed Time: SecTime = 0s, UsecTime = 3834us!

         可见,批量执行时,FastStrcat远快于strcat库函数。

     

    四  总结

         单次拼接时,strcat库函数最快,FastStrcat居中,sprintf库函数最慢(若非格式化需要不建议用于字符串拼接)。

         频繁拼接时,FastStrcat相比strcat库函数具有明显的速度优势。

     

     

  • 相关阅读:
    微信 token ticket jsapi_ticket access_token 获取 getAccessToken get_jsapi_ticket方法
    PHP 日志 记录 函数 支持 数组 对象 新浪 sae 环境 去掉 空格 换行 格式化 输出 数组转字符串
    原生 原始 PHP连接MySQL 代码 参考mysqli pdo
    PHP 数字金额转换成中文大写金额的函数 数字转中文
    使用PHPMailer发送带附件并支持HTML内容的邮件
    设置输出编码格式 header 重定向 执行时间 set_time_limit 错误 报告 级别 error_reporting
    html5 bootstrap pannel table 协议 公告 声明 文书 模板
    指向指针的指针
    二级指针
    c语言:当指针成为参数后
  • 原文地址:https://www.cnblogs.com/clover-toeic/p/3885181.html
Copyright © 2011-2022 走看看