Chinaunix首页 | 论坛 | 博客
  • 博客访问: 121419
  • 博文数量: 41
  • 博客积分: 2564
  • 博客等级: 少校
  • 技术积分: 455
  • 用 户 组: 普通用户
  • 注册时间: 2007-09-20 19:17
文章分类

全部博文(41)

文章存档

2009年(41)

我的朋友

分类: C/C++

2009-04-21 15:58:40

1、strncpy

strncpy有了一个控制复制最大字符的限制,比strcpy要安全一些,但是它依然存在一些bug,在对它的用法不是完全清楚的情况下,稍不注意也会造成溢出。

strncpy说明:

DESCRIPTION 

The stpcpy() and strcpy() functions copy the string s2 to s1 (including the terminating '\0' character). 

The strncpy() function copies at most n characters from s2 into s1. If s2 is less than n characters long, the remainder of s1 is filled with `\0' characters. Otherwise, s1 is not terminated.

注意最后一段,说的是如果源字符串的长度(指strlen长度)大于或等于n,那么s1将不会以'\0'终结。看下面例子:

#include <stdio.h>
#include <string.h>

int main()
{
    char s1[] = "abcdefgh";
    char s2[9];
    char s3[10] = "xxxxxxxxz";


    strncpy(s3, s1, strlen(s1));
    printf("s3: %s %d %d\n", s3, strlen(s3), sizeof(s3));


    return 0;
}

输出:

s3: abcdefghz 9 10

这个例子还看不出会造成什么溢出问题,再看一个例子:

#include <stdio.h>
#include <string.h>

int main()
{
    char s1[] = "abcdefgh";
    char s2[9];
    char s3[8] = {0};  

    strncpy(s2, s1, sizeof(s2));
    strncpy(s3, s1, strlen(s1));
    
    printf("s2: %s %d %d\n", s2, strlen(s2), sizeof(s2));
    printf("s3: %s %d %d\n", s3, strlen(s3), sizeof(s3));

    return 0;
}

输出:

s2: abcdefgh 8 9
s3: abcdefghabcdefgh 16 8

看,问题来了!此时s3没有NIL结尾,因此作为作为字符串操作时,有可能会溢出。由此,在使用strncpy时,如果目标缓存的大小小于或等于源字符串的长度时,就要非常注意,千万要手动把末位置为'\0'。

最安全的方法就是:

...
strncpy(dst, src, sizeof(dst) - 1);
dst[sizeof(dst) - 1] = '\0';

...

2、snprintf

在同的平台上snprintf的行为是不一样的。Linux上的版本比较友好,它可以有效的防止溢出,一下是man的说明:

The snprintf() and vsnprintf() functions will write at most n-1 of the characters printed into the output string (the n'th character then gets the terminating `\0'); if the return value is greater than or equal to the n argument, the string was too short and some of the printed characters were discarded. The output is always null-terminated.

蓝色部分是关键点。假如有以下代码:

...
char src[] = "abcdefg";
char dst[8];

snprintf(dst, sizeof(dst), "%s", src);
...

将不会有任何问题,因为snprintf只向dst写入sizeof(dst) - 1个字符,也就是7个,最有一个字符自动置为'\0'。

然而,这样的代码在win下就会有大问题,代码(win下为_snprintf):

#include <stdio.h>
#include <string.h>

int main()
{
    char s1[] = "abcdefgh";
    char s2[9];
    char s3[8] = {0};

    strncpy(s2, s1, sizeof(s2));

    _snprintf(s3, sizeof(s3), "%s", s1);
    
    printf("s2: %s %d %d\n", s2, strlen(s2), sizeof(s2));
    printf("s3: %s %d %d\n", s3, strlen(s3), sizeof(s3));
    
    return 0;
}

输出:

s2: abcdefgh 8 9
s3: abcdefghabcdefgh 16 8

为什么会这样呢?因为在win下_snprintf的行为不遵循标准,缓冲区不够大时,它将不会输出结尾的'\0'!

3、strncat

最多从源中拷贝n个字符到目标串中,并在后面加一个0;也就是说,最多会有n+1个字符被写进dest。如果dest的容量为n,那么将会dest将会溢出。看下面代码:

#include <stdio.h>
#include <string.h>

int main()
{
    char s1[] = "zz";
    char s2[2] = "aa";
    char s3[2] = {0};
    
    printf("%d %d\n", s2[0], s2[1]);
    strncat(s3, s1, sizeof(s3));
    printf("%s\n", s3);
    printf("%d %d\n", s2[0], s2[1]);

    return 0;
}

输出:

97 97
zz
0 97

由输出可以看出,s2[0]原来是'a',而经过strncat之后,被覆盖成'\0'了!(s2在内存中的开头紧接着s3的末尾)这个例子很好的说明了问题。

4、总结

这几个C标准库的函数从名字上看很相似,但是它们的行为却不是统一的。因此,在使用的时候,一定要搞清楚它们的真正含义(特别是平台不同时),否则很容易出现莫名其妙的错误。

希望此为对你有所帮助。

阅读(707) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~