zoukankan      html  css  js  c++  java
  • talk about string,char

    【1】.关于sprintf和snprintf的正确使用

    考虑以下有缺陷的例子:
    void f(const char *p)
    {
    char buf[11]={0};
    sprintf(buf,"%10s",p); // very dangerous
    printf("%sn",buf);
    }

    不要让格式标记“%10s”误导你。如果p的长度大于10个字符,那么sprintf() 的写操作就会越过buf的边界,从而产生一个缓冲区溢出。
    检测这类缺陷并不容易,因为它们只在 p 的长度大于10个字符的时候才会发生。黑客通常利用这类脆弱的代码来入侵看上去安全的系统。

    要修正这一缺陷,可以使用函数snprintf()代替函数sprintf()。

    函数原型:int snprintf(char *dest, size_t n, const char *fmt, ...);
    函数说明: 最多从源串中拷贝n-1个字符到目标串中,然后再在后面加一个0。所以如果目标串的大小为n的话,将不会溢出。
    函数返回值: 若成功则返回存入数组的字符数,若编码出错则返回负值。

    推荐的用法:
    void f(const char *p)
    {
    char buf[11]={0};
    snprintf(buf, sizeof(buf), "%10s", p); // 注意:这里第2个参数应当用sizeof(str),而不要使用硬编码11,也不应当使用sizeof(str)-1或10
    printf("%sn",buf);
    }


    【转载】http://kapok.blog.51cto.com/517862/113471




    【2】.返回值、缓冲区问题

    snprintf函数的返回值
    sprintf函数返回的是实际输出到字符串缓冲中的字符个数,包括null结束符。而snprintf函数返回的是应该输出到字符串缓冲的字符个数,所以snprintf的返回值可能大于给定的可用缓冲大小以及最终得到的字符串长度。看代码最清楚不过了:

    char tlist_3[10] = {0};
    int len_3 = 0;
    len_3 = snprintf(tlist_3,10,"this is a overflow test! ");
    printf("len_3 = %d,tlist_3 = %s ",len_3,tlist_3);
    上述代码段的输出结果如下:

    //////////////////////////////////////////////
    // 输出的数据:"25", "this is a".
    len_3 = 25, tlist_3 = this is a
    所以在使用snprintf函数的返回值时,需要小心慎重,避免人为造成的缓冲区溢出,不然得不偿失。


    snprintf函数的字符串缓冲
    int sprintf(char *str, const char *format, ...);
    int snprintf(char *str, size_t size, const char *format, ...);
    上面的函数原型大家都非常熟悉,我一直以为snprintf除了多一个缓冲区大小参数外,表现行为都和sprintf一致,直到今天遇上的bug。在此之前我把下面的代码段的两个输出视为一致。

    char tlist_1[1024] = {0},tlist_2[1024]={0};
    char fname[7][8] = {"a1","b1","c1","d1","e1","f1","g1"};
    int i = 0, len_1,len_2 = 0;

    len_1 = snprintf(tlist_1,1024,"%s;",fname[0]);
    len_2 = snprintf(tlist_2,1024,"%s;",fname[0]);

    for(i=1;i<7;i++)
    {
    len_1 = snprintf(tlist_1,1024,"%s%s;",tlist_1,fname[i]);
    len_2 = sprintf(tlist_2,"%s%s;",tlist_2,fname[i]);
    }

    printf("tlist_1: %s ",tlist_1);
    printf("tlist_2: %s ",tlist_2);
    可实际上得到的输出结果却是:

    ////////////////////////////////////////////
    // snprintf()会清除缓冲区,sprintf()不会清除缓冲区;
    tlist_1: g1;
    tlist_2: a1;b1;c1;d1;e1;f1;g1;

    【转载】http://my.oschina.net/shelllife/blog/177279


    【3】.三个打印函数printf()/sprintf()/snprintf()区别

    先贴上其函数原型
    printf( const char *format, ...) 格式化输出字符串,默认输出到终端-----stdout
    sprintf(char *dest, const char *format,...) 格式化输出字符串到指定的缓冲区
    snprintf(char *dest, size_t size,const char *format,...) 按指定的SIZE格式化输出字符串到指定的缓冲区

    printf()函数在这就不再讨论,这里主要讨论sprintf()与snprintf()的用法及区别,

    #include "stdafx.h"
    #include <stdio.h>
    using namespace std;
    int _tmain(int argc, _TCHAR* argv[])
    {
    char *p1="China";
    char a[20];
    sprintf(a,"%s",p1);
    printf("%s ",a);
    //////////////////////////
    memset(a,0,sizeof(a));

    _snprintf(a,3,"%s",p1);
    printf("%s ",a);
    printf("%d ",strlen(a));
    return 0;
    }

    结果输出:
    China
    Chi
    3

    分析:
    sprintf(a,"%s",p1) 把p1字符串拷贝到数组a中(''也拷贝过去了)。
    snprintf(a,3,"%s",p1) 拷贝P1中前3个字符到数组a中,并在末尾自动添加''。
    sprintf属于I/O库函数,snprintf函数并不是标准c/c++中规定的函数,但是在许多编译器中,厂商提供了其实现的版本。在gcc中,该函数名称就snprintf,而在VC中称为_snprintf。 如果你在VC中使用snprintf(),会提示此函数未声明,改成_snprintf()即可。

    注意点:
    1 sprintf是一个不安全函数,src串的长度应该小于dest缓冲区的大小,(如果src串的长度大于或等于dest缓冲区的大小,将会出现内存溢出。)
    2 snprintf中源串长度应该小于目标dest缓冲区的大小,且size等于目标dest缓冲区的大小。(如果源串长度大于或等于目标dest缓冲区的大小,且size等于目标dest缓冲区的大小,则只会拷贝目标dest缓冲区的大小减1个字符,后加'';该情况下,如果size大于目标dest缓冲区的大小则溢出。)
    3 snprintf ()函数返回值问题, 如果输出因为size的限制而被截断,返回值将是“如果有足够空间存储,所应能输出的字符数(不包括字符串结尾的'')”,这个值和size相等或者比size大!也就是说,如果可以写入的字符串是"0123456789ABCDEF"共16位,但是size限制了是10,这样 snprintf() 的返回值将会是16 而不是10!

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    【转载】http://www.cnblogs.com/ningskyer/articles/4037964.html




    【4】. <string.h> 、<string>

    <string.h> 是C语言的一个【头文件】,里
    面有 定义了 很多操作字符串的【方法】,例如:
    strcpy(), strncpy(),
    strcat(), stncat(),
    strcmp(), strncmp(),
    // 找子串
    strstr()
    strlen()
    memset()


    <string> 里面定义了一个 string 类,其功能和一容器类似。
    string类的方法有:
    at(), append(), assign(), begin(), end(),
    earse(), find(), insert(),
    clear(), length(), c_str(),
    // Returns whether the string is empty
    empty(),
    push_back(),
    size(),
    string 类定义的对象,可以直接加上字符串。
    要访问string类对象中保存的字符串,需要调用类的c_str()方法。

    ----------------------------------------------------------------------------


    -----------------------------------------------------
    strlen,sizeof()
    char mystr[100]="test string";
    defines an array of characters with a size of 100 chars, but the C string with which mystr has ben initialized has a length of only 11 characters.
    Therefore, while sizeof(mystr) evaluates to 100,
    strlen(mystr) returns 11. 



    【5】.C里操作字符串很高效,但也很麻烦。

    1. char * strcpy ( char * destination, const char * source );

    最常用的函数,但是却不安全,原因在于,一是要destination有足够的空间,二是要保证source和destination指向的空间没有overlap。

    2. int sprintf ( char * str, const char * format, ... );

    也许要问,这个怎么用于字符串拷贝呢?可以这么用 sprintf(dest, "%s", src); 但是要调用者保证dest有足够的内存存放src。

    3. char * strncpy ( char * destination, const char * source, size_t num );

    比起strcpy,多了个长度的控制。从source拷贝num个字符到destination。如果source里不够num字符怎么办呢?会补充0。

    一个典型的用法是:

    char buf[MAX];
    strncpy(buf, src, MAX-1);

    这段代码的本意是,一个长为MAX的buf,最多也就放MAX-1个字符,最后一个位置放‘'。因此最多能从src里拷贝MAX-1个字符,如果src里没这么多,剩余的填充0就是了。

    但是这样做就安全了么?不是,如果src刚好MAX-1个字符。注意到strncpy只复制了MAX-1个字符,最后一个位置未知,有潜在的隐患。下段代码可以诠释:

    #define MAX 4
    char buf[MAX];
    char* src="123";
    memset(buf, 'x', MAX);

    // solution 1. memset(buf, 0, MAX);
    strncpy(buf, src, MAX-1);
    // solution 2. buf[MAX-1] = '';

    printf("%s ", buf);

    有两个办法可以解决:
    1.调用strncpy之前memset为0,有点浪费。
    2.在strncpy之后对最后一个字符赋值为0。

    都可以,但不够优雅。

    4. int snprintf( char *buffer, int buff_size, const char *format, ... );

    用作字符串拷贝的用法:

    char buf[MAX];
    snprintf(buf, sizeof(buf), "%s", src);

    即安全,又简洁。

    你可能会关心:如果src的长度大于dest(buf)呢?这个是另外一个问题,这里需要的是安全的字符串拷贝,在C语言里,如果一个字符串指针指向的内存没有结尾字符'',是非常危险的。

    snprintf会把buf的最后一个位置保留为''。

    关于返回值:如果当前buf够用,返回实际写入的字符数;如果不够用,返回将要写入的字符数。换句话说,返回值就是传入的字符数目。

    假设当前的buf[4].

    待写入 实际写入 返回值
    12 12 2 够用
    123 123 3 够用
    1234 123 4 不够用
    12345 123 5 不够用

    sprintf/snprintf的另外一个用法:

    itoa不是ANSI C或C++的一部分,可以变相的用sprintf来代替:

    sprintf(str,"%d",value) 转换为十进制数值。
    sprintf(str,"%x",value) 转换为十六进制数值。
    sprintf(str,"%o",value) 转换为八进制数值。


    【转载】http://blog.csdn.net/kadwf123/article/details/7819439



    【6】.C++危险的函数 strcpy strncpy

    1.strcpy

    函数原型为char *strcpy(char *dest,const char *src);

    函数说明:strcpy函数会将参数src字符串拷贝至参数dest所指的地址。

    参数说明:dest,我们说的出参,最终得到的字符串。src,入参,因为其有const修饰。表示在此函数中不会也不能修改src的值。

    返回值:返回dest字符串的起始地址。

    附加说明:如果参数dest所指的内存空间不够大,可能会造成缓冲溢出的错误情况。

    特别强调:此函数很好用,可是它也很危险。如果在用的时候加上相关的长度判断,则会大大降低出此错误的危险。此函数还有一个特点,就是它在把字符串b拷贝到字符串a的时候,会在拷贝的a字符串的末尾加上一个结束标志。这个不同于strncpy()函数。

    例如:

    #include<string.h>

    main(){
    char a[30]="string(1)";
    char b[]="string(2)";
    printf("before strcpy():%s ",a);
    if(strlen(b)<strlen(a))
    {
    printf("after strcpy():%s ",strcpy(a,b));
    }
    }

    2.strncpy
    函数原型为:char *strncpy(char *dest,const char *src ,size_t n);

    函数说明:strncpy会将参数src字符串拷贝前n个字符至参数dest所指的地址。

    返回值:返回参数dest的字符串起始地址。

    特别强调:不要以为这个函数是个好东西,往往在定位问题时,它是罪魁祸首,到顶了,它是静态的容值函数,程序跑起来你就等着dbug吧。

    strncpy的正确用法:

    strncpy(dest, src, sizeof(dest));
    dest[sizeof(dest)-1] = ‘’;

    size一定要用sizeof(dest)或sizeof(dest)-1,不可误用sizeof(src).
    手工填0. 务必要把dest的最后一个字节手工设置为0. 因为strncpy仅在src的长度小于dest时,对剩余的字节填0.
    性能问题。当dest长度远大于src时,由于strncpy会对多余的每个字节填0,会有很大的性能损失。
    返回值。strncpy返回dest,因而无法知道拷贝了多少个字节。


    【转载】http://blog.csdn.net/kadwf123/article/details/7819052




    strcpy
    char * strcpy ( char * destination, const char * source );
    Copy string
    Copies the C string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point).

    To avoid overflows, the size of the array pointed by destination shall be long enough to contain the same C string as source (including the terminating null character), and should not overlap in memory with source.

    /* strcpy example */
    #include <stdio.h>
    #include <string.h>

    int main ()
    {
    char str1[]="Sample string";
    char str2[40];
    char str3[40];
    // 拷贝的时候,包含了最后的字符串结束符
    strcpy (str2,str1);
    strcpy (str3,"copy successful");
    printf ("str1: %s str2: %s str3: %s ",str1,str2,str3);
    return 0;
    }


    --------------------------------------------------------------------------------
    strncpy
    char * strncpy ( char * destination, const char * source, size_t num );
    Copy characters from string
    Copies the first num characters of source to destination. If the end of the source C string (which is signaled by a null-character) is found before num characters have been copied, destination is padded with zeros until a total of num characters have been written to it.

    No null-character is implicitly appended at the end of destination if source is longer than num. Thus, in this case, destination shall not be considered a null terminated C string (reading it as such would overflow).

    destination and source shall not overlap (see memmove for a safer alternative when overlapping).
    /* strncpy example */
    #include <stdio.h>
    #include <string.h>

    int main ()
    {
    char str1[]= "To be or not to be";
    char str2[40];
    char str3[40];

    /* copy to sized buffer (overflow safe): */
    strncpy ( str2, str1, sizeof(str2) );

    /* partial copy (only 5 chars): */
    strncpy ( str3, str2, 5 );
    // 需要你手动添加字符串结束符!!!!!
    str3[5] = ''; /* null character manually added */

    puts (str1);
    puts (str2);
    puts (str3);

    return 0;
    }

    Output:
    To be or not to be
    To be or not to be
    To be

  • 相关阅读:
    component
    js的for循环中DOM变化不了,在循环结束才起效的问题
    mongodb 遇见不可行
    vue.js中$emit的理解
    最近前端关键词-模块化、前后端分离、单页面应用
    webpack的使用1
    一维数组的声明和使用
    ASP.net中的几种分页方法
    各种排序及原理
    数据结构-算法
  • 原文地址:https://www.cnblogs.com/music-liang/p/11901045.html
Copyright © 2011-2022 走看看