设为首页 加入收藏

TOP

写高并发程序时慎用strncpy和sprintf(一)
2019-01-05 20:09:04 】 浏览:507
Tags:并发 程序 慎用 strncpy sprintf

 

         分享一下最近做程序优化的一点小心得:在写高并发交易代码时要谨慎使用strncpy和sprintf。

         下面详细介绍一下这样说的原因及建议实践:

 

1 慎用strncpy因为它的副作用极大

 

         我们平时使用strncpy防止字符串拷贝时溢出,常常这样写

char buf[1024] = {0};

char str[16] = "hello";

strncpy(buf, sizefo(buf), str);

 

         这样写当然没问题,但有些人不知道的是:strncpy一行代码执行时是往buf写了sizeof(buf) = 1024个字节,而不是直观以为的strlen(str) + 1 = 6个字符。

         也就是说我们为了复制6个字符却写了1024个字节,多了不少额外消耗。如果这个函数被频繁调用,会导致系统性能出现不少损失。

         因为调用strncpy(dest, n, str)时,函数首先将字符从源缓冲区str逐个复制到目标缓冲区dest,直到拷贝了n碰上\0。

         紧接着,strncpy函数会往buf填充\0字符直到写满n个字符。

         所以我才会说上面的代码strncpy才会写了1024个字节。

         可以做一个小实验:

 

         看上面代码及输出结果,我们可以知道在执行strncpy之前dest是用'1'填充的,但在执行strncpy后,前面几个字符变成hello,后面的字符全变成\0;

         我个人的解决方法是写一个宏专用于往字符数组拷贝的,与大家分享一下,抛砖引玉。

 

 
// 静态断言  从vc拷贝过来(_STATIC_ASSERT) 稍微修改了一下 
// 原来是typedef char __static_assert_t[ (expr) ]
// 现在是typedef char __static_assert_t[ (expr) - 1 ] 
// 原因是gcc支持0字符数组
//TODO: 这里在win上编译有警告 有待优化 另外在linux宏好像不起作用 原因待查。暂时只有在win编译代码可以用
#ifndef _STATIC_ASSERT_RCC
#   ifdef __GNUC__
#       define _STATIC_ASSERT_RCC(expr) typedef char __static_assert_t[ (expr) - 1 ]
#   else
#       define _STATIC_ASSERT_RCC(expr) do { typedef char __static_assert_t[ (expr) ]; } while (0)
#   endif
#endif
 
//将src复制到字符数组arr 保证不会越界并且末尾肯定会加\0
//_STATIC_ASSERT_RCC这里作用是防止有人传字符串指针进来
#define strncpy2arr(arr, src) do { \
    char *dest_ = arr; \
    size_t n = strnlen(src, sizeof(arr) - 1); \
    _STATIC_ASSERT_RCC(sizeof(arr) != sizeof(char *)); \
    memcpy(dest_, src, n); \
    dest_[n] = '\0'; \
} while (0)
 
 
#ifdef WIN32
int main(int argc, char *argv[])
{
    char dest[16];
    char *src = "hello                                    222";
    int i = 0;
 
    for (i = 0; i < sizeof(dest); ++i)
    {
        dest[i] = '1';
    }
 
    printf("before strncpy\n");
    for (i = 0; i < sizeof(dest); ++i)
    {
        printf("%d ", dest[i]);
    }
    printf("\n");
 
    strncpy2arr(dest, src);
    printf("after strncpy\n");
    for (i = 0; i < sizeof(dest); ++i)
    {
        printf("%d ", dest[i]);
    }
    printf("\n");
 
    strncpy(dest, src, sizeof(dest));
    printf("after strncpy\n");
    for (i = 0; i < sizeof(dest); ++i)
    {
        printf("%d ", dest[i]);
    }
    printf("\n");
    
 
    return 0;
   
    //return CompressPerformanceTestMain(argc, argv);
}
#endif
 

 

 

 

2 慎用sprintf,因为它的效率比你想象的低

 

         之前我一直没注意到sprintf效率低的问题,直到有一次使用callgrind对程序进行性能分析时,发现有相当大的资源消耗在sprintf上面,我才有所警觉。

         为此,我写了一点测试代码,对常用的函数做了一下基准测试,结果如下:

 

测试内容

耗时(us

for循环赋值40亿次

13023889

调用简单函数40亿次

16967986

调用memset函数4亿次

(256个字节)

6932237

调用strcpy函数4亿次

(12个字节)

3239218

调用memcpy函数4亿次

(12个字节)

3239201

调用strcmp函数4亿次

(12个字节)

2500568

调用memcmp函数4亿次

(12个字节)

26683

首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇cf121C. Lucky Permutation(康托.. 下一篇洛谷P2973 [USACO10HOL]赶小猪(高..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目