zoukankan      html  css  js  c++  java
  • sprintf_s与_snprintf与_snprintf_s

    int snprintf(char *str, size_t size, const char *format, ...);

      将可变个参数(...)按照format格式化成字符串,然后将其复制到str中

      (1) 如果格式化后的字符串长度 < size,则将此字符串全部复制到str中,并给其后添加一个字符串结束符('\0');

      (2) 如果格式化后的字符串长度 >= size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符('\0')

      函数返回值:若成功则返回欲写入的字符串长度,若出错则返回负值。 --------------------------------------------

      #include <stdio.h> //snprintf()

      #include <string.h> //strlen()

      int main()

      {

      char toname[30];

      char *name = "yangtaiping";

      printf("strlen(name) = %d\n", strlen(name));

      snprintf(toname, strlen(name)+strlen("姓名:")+1, "姓名:%s", name);

      printf("toname = %s\n", toname);

      printf("strlen(name) = %d\n", strlen(name));

      }

      strcpy() sprintf() strcat() 存在安全隐患, 其对应的安全版为:strncpy() snprintf() strncat() 。 

      snprintf(s, 100, "%.*S", 3, "abcd");s的值为abc %.*s 表示有两项, 第一项指定了长度,第二项则是%s的内容,所以取前三位 词条图册更多图册

      类似的函数还有:

      #include <stdio.h>

      int printf(const char *format, ...); //格式化输出到标准输出设备

      int fprintf(FILE *stream,const char *format, ...); //格式化输出到文件流

      int sprintf(char *str,const char *format, ...); //格式化的输出字符串输出到str指向的地址空间

    int snprintf(char *str, size_t size, const char *format, ...);
      将可变个参数(...)按照format格式化成字符串,然后将其复制到str中
      (1) 如果格式化后的字符串长度 < size,则将此字符串全部复制到str中,并给其后添加一个字符串结束符('\0');
      (2) 如果格式化后的字符串长度 => size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符('\0')
      函数返回值:若成功则返回欲写入的字符串长度,若出错则返回负值。
      --------------------------------------------
      #include <stdio.h> //snprintf()
      #include <string.h> //strlen()
      int main()
      {
      char array[30];
      char *name = "yangtaiping";
      printf("strlen(array) = %d\n", strlen(array));
      snprintf(array, strlen(array), "姓名:%s", name);
      printf("array = %s\n", array);
      printf("strlen(array) = %d\n", strlen(array));
      }
      strcpy() sprintf() strcat() 存在安全隐患, 其对应的安全版为:
      strncpy() snprintf() strncat()
      snprintf(s, 100, "%.*S", 3, "abcd");
      s的值为abc
      %.*s 表示有两项, 第一项指定了长度,第二项则是%s的内容,所以取前三位

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

    MSDN页面分别如下:

    spirntf_s:

    http://msdn.microsoft.com/zh-cn/library/ce3zzk1k%28VS.80%29.aspx

    _snprintf:

    http://msdn.microsoft.com/zh-cn/library/2ts7cx93%28v=VS.90%29.aspx

    _snprintf_s:

    http://msdn.microsoft.com/zh-cn/library/f30dzcf6.aspx

    三个页面都有自己的例子。。其中后2个的例子比较多内容一些。

    为免将来页面失效:

    int sprintf_s(
    char *buffer,
    size_t sizeOfBuffer,
    const char *format [,
    argument] ...

    );

    template <size_t size>
    int sprintf_s(
    char (&buffer)[size],
    const char *format [,
    argument] ...

    ); // C++ only

    int _snprintf(
    char *buffer,
    size_t count,
    const char *format [,
    argument] ...

    );

    int _snprintf(
    char (&buffer)[size],
    size_t count,
    const char *format [,
    argument] ...

    ); // C++ only

    int _snprintf_s(
    char *buffer,
    size_t sizeOfBuffer,
    size_t count,
    const char *format [,
    argument] ...

    );

    int _snprintf_s(
    char (&buffer)[size],
    size_t count,
    const char *format [,
    argument] ...

    ); // C++ only

    呼呼。。内容还挺多。


    这里比较引人注目的是,_snprintf_s为什么在sizeOfBuffer的基础上,还要多加一个count?

    count似乎是用来控制理想的宽度的。

    如果得到的字符串超过了count,于是会被截断到count的长度后面再加一个null-teminate

    当然,更高优先级的应该是sizeOfBuffer,必须不超过这个大小。这个就说到点子上了。

    如果应该输出的字符串的大小已经达到了sizeOfBuffer,那么就溢出了。溢出的情况下,sprintf_s函数把这当做一个错误,会把buffer缓冲区置为一个空字符串""。

    而_snprintf_s的好处就是,有了count参数,输出的字符串就算超过缓冲区长度,仍然会有输出,输出字符串被截断到count大小,在这个大小的字符串后面加null-teminate。


    当然,如果count被设置成和sizeOfBuffer同样大,或者不合理的更大,那么这个count参数就失去了意义。

    这时候,如果输出字符串将要达到或者超过sizeOfBuffer,一样导致一个错误,输出缓冲区被置为空字符串。

    因此,如果希望缓冲区被尽量利用,可以把count参数置为_TRUNCATE,这样的情况下,实际上效果相当于是将count设置为sizeOfBuffer - 1。

    至于C语言环境下,sprintf_s与_snprintf的对比:

    注意到,_snprintf的参数用的是count,而sprintf_s的参数用的是sizeOfBuffer。这很能说明问题。

    看下对_snprintf的说明:

    Let len be the length of the formatted data string (not including the terminating null). len and count are in bytes for _snprintf, wide characters for _snwprintf.

    If len < count, then len characters are stored in buffer, a null-terminator is appended, and len is returned.

    If len = count, then len characters are stored in buffer, no null-terminator is appended, and len is returned.

    If len > count, then count characters are stored in buffer, no null-terminator is appended, and a negative value is returned.

    也就是说,_snprintf的count参数明明白白的就是一个count。

    如果输出字符串刚好达到count,由于期待的最大长度就是count,那么输出字符串肯定要完整,不能截断。

    但是假如字符串缓冲区的大小其实就是count,这怎么办?MS VCRT的设计者认为,在这种情况下应该把输出字符串的长度告知调用者,让调用者来决定是否自己添加null-teminate。

    换句话说,调用_snprintf时要注意了,必须检查_snprintf的返回值,如果返回值不是正数,那么还得注意你的字符串缓冲区并不是null-teminate结尾的。

    总结来说,sprintf_s在缓冲区不够大时会失败,失败时缓冲区中是一个空字符串。

    _snprintf不会失败,但是必须注意如果缓冲区不够大,缓冲区的内容将不是null-teminate的,必须自己注意字符串的结束。

    _snprintf_s结合了2者的优点,只要count参数设置合理,函数就不会因缓冲区不够而失败。

    但是观察_snprintf_s的说明,有一个很有趣的内容。

    这3族函数中,有失败情况的2个函数sprintf_s和_snprintf_s中,(再次强调,我这里的失败的意思是,调用后缓冲区里是一个空字符串),_set_invalid_parameter_handler设置的错误处理器,在失败的情况下会被调用。

    而截断的情况下,错误处理器并不会被调用。

    VC的库开发者总是提供一些怪怪的东西。无论如何,让代码更加安全总是符合大家的总体期望的。

    另外补充一下,查阅这些字符串安全函数的资料的时候要注意,

    对微软来说,凡是限制字符串复制长度的函数,这些设计者仍然认为是不安全的,因为逻辑上来说,

    这些长度参数只是限制了源字符串被复制的长度,而不是目标缓冲区的长度。

    也就是说,微软的这些设计者认为,安全的方式其实是依赖C++的机制,辨认出目标缓冲区的真正大小,以此实现安全的复制。


  • 相关阅读:
    Java面向对象编程之异常处理机制
    LinkedList链式集合
    Java之反射机制
    适配器模式
    QuickHit
    Java之单例模式
    Java面向对象之接口
    多线程
    线程同步
    试题分析
  • 原文地址:https://www.cnblogs.com/kungfupanda/p/2438657.html
Copyright © 2011-2022 走看看