既然我们有snprintf,为什么我们没有snscanf?

Lid*_*Guo 15 c posix

我有snprintf它可以避免缓冲区溢出,但为什么没有调用函数snscanf

码:

int main()
{
     char * src = "helloeveryone";
     char buf1[5];
     sscanf(src,"%s",buf1); // here is a  array out of bounds

}
Run Code Online (Sandbox Code Playgroud)

所以,我认为snscanf还需要一个.为什么我们只有snprintf

R..*_*R.. 11

有争议的(和可选的)附件K到C11增加了一个sscanf_s函数,该函数rsize_t在指针参数之后采用类型的附加参数(也在附件K中定义),指定了指向数组的大小.无论好坏,这些功能都没有得到广泛支持.您可以通过将大小放在转换说明符中来获得相同的结果,例如

char out[20];
sscanf(in, "%19s", out);
Run Code Online (Sandbox Code Playgroud)

但是如果目标对象的大小在运行时可能会有所不同,那么这很容易出错并且容易出错(您必须以编程方式构造转换说明符snprintf).请注意,转换说明符中的字段宽度是要读取的最大输入字符数,并且sscanf还会为%s转换写入终止空字节,因此您传递的字段宽度必须严格小于目标对象的大小.

  • 嗯...§K.3.3常见定义`<stddef.h>`说:'...类型是`rsize_t`,类型是`size_t`.385)'这意味着实际上你可以传递`size_t `不需要强制转换 - 只要传递的值在`<stdint.h>`中的`RSIZE_MAX`定义的范围内(和脚注385交叉引用`<stdint.h.>`). (2认同)

Jon*_*ler 10

没有必要,snscanf()因为没有写入第一个缓冲区参数.缓冲区长度in snprintf()指定写入的缓冲区大小:

char buffer[256];

snprintf(buffer, sizeof(buffer), "%s:%d", s, n);
Run Code Online (Sandbox Code Playgroud)

对应位置的缓冲区sscanf()是以空字符结尾的字符串; 不需要明确的长度,因为你不打算写它(它是const char * restrict buffer在C99和C11中).

char buffer[256];
char string[100];
int n;
if (sscanf(buffer, "%s %d", string, &n) != 2)
    ...oops...
Run Code Online (Sandbox Code Playgroud)

在输出中,您已经期望指定字符串的长度(尽管如果您使用%s而不是%99s或者严格适当的话,您可能占多数):

if (sscanf(buffer, "%99s %d", string, &n) != 2)
    ...oops...
Run Code Online (Sandbox Code Playgroud)

这将是很好/有用的,如果你可以使用%*s,你可以用snprintf(),但你不能-中sscanf()*意思是"不分配扫描的价值",而不是长度.请注意,您不会写snscanf(src, sizeof(buf1), "%s", buf1),尤其是因为您可以%s在一次调用中拥有多个转换规范.写作snscanf(src, sizeof(buf1), sizeof(buf2), "%s %s", buf1, buf2)毫无意义,尤其是因为它在解析varargs列表时留下了一个不可解决的问题.有一个符号snscanf(src, "%@s %@s", sizeof(buf1), buf1, sizeof(buf2), buf2)可以避免在格式字符串中指定字段大小(减一)的需要.不幸的是,现在你不能这样做sscanf().

ISO/IEC 9899:2011(以前为TR24731)的附录K 规定sscanf_s()了字符串的长度,可以用作:

if (sscanf_s(buffer, "%s %d", string, sizeof(string), &n) != 2)
    ...oops...
Run Code Online (Sandbox Code Playgroud)

(感谢R ..提醒我这个理论选项 - 理论上是因为只有Microsoft实现了'安全'功能,并且他们并没有完全按照标准的要求实现它们.)

注意§K.3.3 常见定义<stddef.h>说:'......类型是rsize_t哪种类型size_t.385) '(和脚注385说:'参见RSIZE_MAX宏的描述<stdint.h>.'这意味着事实上你可以在size_t不需要演员的情况下通过- 只要传递的值在RSIZE_MAXin中定义的范围内<stdint.h>.(一般意图)这RSIZE_MAX是一个较大的数字,但小于SIZE_MAX.有关更多详细信息,请阅读2011标准,或从Open Standards网站获取TR 24731.)


ivo*_*lch 5

皱纹多一点。“n”通常指 snprintf 中的第一个参数。现在,确实没有写入 sscanf 中的第一个字符串参数。然而,它被读取了。因此,以下内容可能会出现段错误:

char s[2];
s[0]='1'; s[1]='3';
int x;
sscanf(s, "%d", &x);
Run Code Online (Sandbox Code Playgroud)

因为在 s 之外步进一个 char 可能会无意中从未定义的内存中读取(或继续从另一个变量中读取整数)。所以,像这样的东西会很有用:

 snscanf(s, 2, "%d", &x);
Run Code Online (Sandbox Code Playgroud)

当然,s 不是字符串,而是字符数组。snscanf 中的“n”将防止超越(读取)第一个(源字符串)参数,并且与目标参数无关。

避免这种情况的方法是首先确保 s 以 2 个字符内的 '\0' 结尾。当然,你不能使用 strlen 。你需要strnlen,并测试它是否小于2。如果是2,那么首先需要更多的复制工作。

  • 你不能输入 s[1]='\0',因为它会丢失 '3'。此外,我只是将其作为说明。我的 s 不是格式字符串。它是源字符串。我的示例中的格式称为“%d”。 (2认同)