一  原型说明

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

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

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

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

二  代码实现

2.1 汇编实现

 char *AssemStrcat(char *pszDest, const char *pszSrc)
{
int d0, d1, d2, d3;
__asm__ __volatile__(
"repne\n\t"
"scasb\n\t"
"decl %1\n"
"1:\tlodsb\n\t"
"stosb\n\t"
"testb %%al,%%al\n\t"
"jne 1b"
: "=&S" (d0), "=&D" (d1), "=&a" (d2), "=&c" (d3)
: "" (pszSrc), "" (pszDest), "" (), "" (0xffffffff):"memory");
return pszDest;
}

2.2 模拟C库

 char *SimuStrcat(char *pszDest, const char *pszSrc)
{
char *pszOrigDst = pszDest; while(*pszDest)
pszDest++;
while((*pszDest++ = *pszSrc++) != '\0')
; return pszOrigDst;
}

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

2.3 快速拼接

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

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

 char *FastStrcat(char *pszDest, const char* pszSrc)
{
while(*pszDest)
pszDest++;
while((*pszDest++ = *pszSrc++));
return --pszDest;
}

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

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

 char *FastStrcat(char *pszDest, const char* pszSrc)__attribute__((__nonnull__(1, 2)));
char *NullPtr(void){return NULL;}
int main(void)
{
char *pszBuf = NULL;
FastStrcat(pszBuf, "Elizabeth ");
FastStrcat(NullPtr(), "Elizabeth ");
8 FastStrcat(NullPtr()?pszBuf:NULL, "Elizabeth ");
FastStrcat(NULL, "Elizabeth ");
return ;
}

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

编译结果如下:

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

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

三  性能比较

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

 #define TIME_ELAPSED(codeToTime) do{ \
struct timeval beginTime, endTime; \
gettimeofday(&beginTime, NULL); \
{codeToTime;} \
gettimeofday(&endTime, NULL); \
long secTime = endTime.tv_sec - beginTime.tv_sec; \
long usecTime = endTime.tv_usec - beginTime.tv_usec; \
printf("[%s(%d)]Elapsed Time: SecTime = %lds, UsecTime = %ldus!\n", __FILE__, __LINE__, secTime, usecTime); \
}while()

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

 #ifdef strcat //检查标准库strcat是否用宏实现
#warning strcat has been defined!
#endif
int main(void)
{
char szCatBuf[];
szCatBuf[] = '\0'; //字符串快速初始化 char *pszBuf = szCatBuf;
TIME_ELAPSED(
pszBuf = FastStrcat(pszBuf, "Abraham, ");
pszBuf = FastStrcat(pszBuf, "Alexander, ");
pszBuf = FastStrcat(pszBuf, "Maximilian, ");
pszBuf = FastStrcat(pszBuf, "Valentine ")
);
printf(" [FastStrcat]szCatBuf = %s\n", szCatBuf); szCatBuf[] = '\0';
TIME_ELAPSED(
SimuStrcat(szCatBuf, "Abraham, ");
SimuStrcat(szCatBuf, "Alexander, ");
SimuStrcat(szCatBuf, "Maximilian, ");
SimuStrcat(szCatBuf, "Valentine ")
);
printf(" [SimuStrcat]szCatBuf = %s\n", szCatBuf); szCatBuf[] = '\0';
TIME_ELAPSED(
AssemStrcat(szCatBuf, "Abraham, ");
AssemStrcat(szCatBuf, "Alexander, ");
AssemStrcat(szCatBuf, "Maximilian, ");
AssemStrcat(szCatBuf, "Valentine ")
);
printf(" [AssemStrcat]szCatBuf = %s\n", szCatBuf); szCatBuf[] = '\0';
TIME_ELAPSED(
strcat(szCatBuf, "Abraham, ");
strcat(szCatBuf, "Alexander, ");
strcat(szCatBuf, "Maximilian, ");
strcat(szCatBuf, "Valentine ")
);
printf(" [LibStrcat]szCatBuf = %s\n", szCatBuf); szCatBuf[] = '\0';
TIME_ELAPSED(
sprintf(szCatBuf, "%s%s%s%s","Abraham, ", "Alexander, ", "Maximilian, ", "Valentine ")
);
printf(" [LibSprintf]szCatBuf = %s\n", szCatBuf); return ;
}

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

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

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

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

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

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

 int main(void)
{
#define ROUND (unsigned short)1000
char szCatBuf[sizeof("Elizabeth ")*ROUND];
szCatBuf[] = '\0'; char *pszBuf = szCatBuf;
TIME_ELAPSED(
unsigned short wIdx = ;
for(; wIdx < ROUND; wIdx++)
pszBuf = FastStrcat(pszBuf, "Elizabeth ");
); szCatBuf[] = '\0';
TIME_ELAPSED(
unsigned short wIdx = ;
for(; wIdx < ROUND; wIdx++)
strcat(szCatBuf, "Elizabeth ");
); return ;
}

测试结果如下:

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

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

四  总结

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

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

strcat的几种实现及性能比较的更多相关文章

  1. python获取字母在字母表对应位置的几种方法及性能对比较

    python获取字母在字母表对应位置的几种方法及性能对比较 某些情况下要求我们查出字母在字母表中的顺序,A = 1,B = 2 , C = 3, 以此类推,比如这道题目 https://project ...

  2. PHP生成随机密码的4种方法及性能对比

    PHP生成随机密码的4种方法及性能对比 http://www.php100.com/html/it/biancheng/2015/0422/8926.html 来源:露兜博客   时间:2015-04 ...

  3. Dynamics CRM2016 查询数据的三种方式的性能对比

    之前写过一个博客,对非声明验证方式下连接组织服务的两种方式的性能进行了对比,但当时只是对比了实例化组织服务的时间,并没有对查询数据的时间进行对比,那有朋友也在我的博客中留言了反映了查询的时间问题,一直 ...

  4. Windows五种IO模型性能分析和Linux五种IO模型性能分析

    Windows五种IO模型性能分析和Linux五种IO模型性能分析 http://blog.csdn.net/jay900323/article/details/18141217 http://blo ...

  5. DBA 需要知道N种对数据库性能的监控SQL语句

    --DBA 需要知道N种对数据库性能的监控SQL语句 -- IO问题的SQL内部分析 下面的DMV查询可以来检查当前所有的等待累积值. Select wait_type, waiting_tasks_ ...

  6. ArcEngine数据删除几种方法和性能比较

    转自原文 ArcEngine数据删除几种方法和性能比较 一.  几种删除方法代码 1.  查询结果中删除 private void Delete1(IFeatureClass PFeatureclas ...

  7. C#实例化对象的三种方式及性能对比

    前言 做项目过程中有个需求要实例化两万个对象并添加到List中,这个过程大概需要1min才能加载完(传参较多),于是开启了代码优化之旅,再此记录. 首先想到的是可能实例化比较耗时,于是开始对每种实例化 ...

  8. KingbaseES 两表关联Update的两种写法与性能

    熟悉oracle 的人都知道,对于两表的关联更新,其执行计划主要有 Filter 和 Outer Join 两种方式.对于大批量数据的update,Join方式明显是更优的选择.KingbaseES ...

  9. ArcEngine数据删除几种方法和性能比较[转]

    四个解决方案: 1.IFeatureCursor 游标查询后,遍历删除 2.更新游标删除IFeatureCursor.DeleteFeature() 3.ITable.DeleteSearchedRo ...

随机推荐

  1. 第三百四十八节,Python分布式爬虫打造搜索引擎Scrapy精讲—通过自定义中间件全局随机更换代理IP

    第三百四十八节,Python分布式爬虫打造搜索引擎Scrapy精讲—通过自定义中间件全局随机更换代理IP 设置代理ip只需要,自定义一个中间件,重写process_request方法, request ...

  2. Mac 创建证书(以 创建gdb证书 为例 )

    open /Applications/Utilities/Keychain\ Access.app/ 打开 钥匙串访问 继续继续 创建完毕. Now that we have a certificat ...

  3. Java如何使用线程解决死锁?

    在Java编程中,如何使用线程解决死锁? 以下示例演示如何使用线程的概念解决死锁问题. // from W w w .Y I I b AI.c o M package com.yiibai; impo ...

  4. JUnit4时间(超时)测试实例

    “时间测试”是指,一个单元测试运行时间是否超过指定的毫秒数,测试将终止并标记为失败. import org.junit.*; /** * JUnit TimeOut Test * @author yi ...

  5. 阿里云Centos6.9安装图形化界面

    yum -y groupinstall "X Window System" "Chinese Support" "Desktop"

  6. SharePoint PowerShell使用Backup-SPSite指令来备份网站集

    备份网站集: Backup-SPSite -Identity http://win2012sp2013:1000/ -Path "C:\KenmuTemp\Test File\Temp\si ...

  7. Android学习之——切换应用主题实现日间和夜间效果的更换

    前言 智能手机的迅速普及,大大的丰富了我们的娱乐生活.现在大家都喜欢晚上睡觉前玩会儿手机,但是应用的日间模式往往亮度太大,对眼睛有较为严重的伤害. 因此,如今的应用往往开发了日间和夜间两种模式供用户切 ...

  8. Alpine Linux:如何配置GUI的图形桌面环境:x Desktop Environment

    alpine linux 真是不错.小巧.迅捷! 官方的各个版本的alpine镜像内没有带图形环境的.那我们如何构建自己的桌面图形环境呢? 其实:这个问题,在起官网的wiki内有指南,我们根据那些相关 ...

  9. php下webservice使用总结

    基于thinkphp3.2的 1.修改php配置 php.ini extension=php_soap.dll soap.wsdl_cache_enabled=0 2.soap有两种模式 wsdl和 ...

  10. centos6.8 安装Python2.7后, yum出现“No module named yum”错误

    出现yum错误:No module named yum 解决方法,查看 /usr/bin下python有哪几个版本 ll /usr/bin 我这里是:2.6  和  2.7 (刚安装的) 由于yum命 ...