一  原型说明

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. 转换基于Maven的Java项目支持Eclipse IDE

    在过去的教程中,使用 Maven 创建了一个Java项目,但是这个项目不能导入到Eclipse IDE中,因为它不是 Eclipse 风格的项目. 这里有一个指南,向您演示如何转换 Maven 生成 ...

  2. (实用)Ubuntu 开启NFS服务

    本文介绍如何在Ubuntu下开启NFS文件系统,从而挂载网络上其他机器的文件系统. NFS, Network File System, 即网络文件系统,通常NFS有提供者和使用者,提供者export自 ...

  3. Xianfeng轻量级Java中间件平台:基于RBAC模型实现权限控制的原理

    首先,白话一下RBAC模型.RBAC是基于角色的访问控制(Role-Based Access Control)的简称.RBAC认为权限授权实际上是Who.What.How的问题.在RBAC模型中,wh ...

  4. 制作CentOS fence-agents 镜像

    1, Download centos7 iso file: Centos7 ISO download: http://buildlogs.centos.org/rolling/7/isos/x86_6 ...

  5. Thinkphp5笔记七:设置错误页面①

    设置网站的错误提示页面,也是一个很重要的环节. 一.空操作 在当前控制器里面增加E_empty操作 public function _empty(){ $this->error('方法不存在') ...

  6. Linq 实现两个对象实例List之间的赋值

    public class UserCopy { public class LoginEntity { public string UserName { get; set; } public strin ...

  7. 《HTTP权威指南》学习笔记——HTTP报文

    HTTP报文 HTTP:互联网的信使 HTTP报文:信使用来搬东西的包裹 1.报文流 HTTP报文:HTTP应用程序之间发送的数据块 组成:元信息开头(文本形式,描述报文的内容和含义)+可选的数据部分 ...

  8. 禁止选中文本JS

    if (typeof(element.onselectstart) != "undefined") { // IE下禁止元素被选取 element.onselectstart = ...

  9. symfony获取POST数据

    $request = $this->getRequest(); $messageid = $this->strip_tags($request->get('messageid')); ...

  10. JS去除字符串左右两端的空格

    去除字符串左右两端的空格,在vbscript里面可以轻松地使用 trim.ltrim 或 rtrim,但在js中却没有这3个内置方法,需要手工编写.下面的实现方法是用到了正则表达式,效率不错,并把这三 ...