wef*_*fa3 15 c size io buffer scanf
我可以使用这种技术指定scanf要读取的最大字符数buffer:
char buffer[64];
/* Read one line of text to buffer. */
scanf("%63[^\n]", buffer);
Run Code Online (Sandbox Code Playgroud)
但是如果我们在编写代码时不知道缓冲区长度怎么办?如果它是函数的参数怎么办?
void function(FILE *file, size_t n, char buffer[n])
{
/* ... */
fscanf(file, "%[^\n]", buffer); /* WHAT NOW? */
}
Run Code Online (Sandbox Code Playgroud)
此代码容易受到缓冲区溢出的影响,因为fscanf不知道缓冲区有多大.
我记得以前见过这个,并开始认为这是解决问题的方法:
fscanf(file, "%*[^\n]", n, buffer);
Run Code Online (Sandbox Code Playgroud)
我的第一个想法是,*in "%*[*^\n]"意味着最大字符串大小传递一个参数(在这种情况下n).这就是*in 的含义printf.
当我检查文档时,scanf我发现它意味着scanf应该丢弃结果[^\n].
这让我有些失望,因为我认为能够动态传递缓冲区大小是一个非常有用的功能scanf.
有没有办法可以scanf动态地传递缓冲区大小?
Jon*_*ler 13
没有一个模拟的printf()格式说明*在scanf().
在编程实践中,Kernighan和Pike建议使用snprintf()创建格式字符串:
size_t sz = 64;
char format[32];
snprintf(format, sizeof(format), "%%%zus", sz);
if (scanf(format, buffer) != 1) { …oops… }
Run Code Online (Sandbox Code Playgroud)
将示例升级为完整函数:
int read_name(FILE *fp, char *buffer, size_t bufsiz)
{
char format[16];
snprintf(format, sizeof(format), "%%%zus", bufsiz - 1);
return fscanf(fp, format, buffer);
}
Run Code Online (Sandbox Code Playgroud)
这强调格式规范中的大小比缓冲区的大小小一个(它是可以存储的非空字符数,而不计算终止空值).请注意,这与fgets()大小(a int,偶然;不是a size_t)是缓冲区大小的情况形成对比,而不是一个.有多种方法可以改进功能,但它说明了这一点.(您可以将s格式替换为[^\n]您想要的格式.)
另外,正如TimČas在评论中指出的那样,如果你想要(其余的)一行输入,你通常最好使用fgets()读取行,但要记住它在输出中包含换行符(而%63[^\n]留下换行符)由下一个I/O操作读取).对于更一般的扫描(例如,2或3个字符串),此技术可能更好 - 特别是与fgets()或一起使用getline()然后sscanf()解析输入时.
此外,TR 24731-1"安全"功能由Microsoft(或多或少)实施并在ISO/IEC 9899-2011(C11标准)的附录K中标准化,需要明确的长度:
if (scanf_s("%[^\n]", buffer, sizeof(buffer)) != 1)
...oops...
Run Code Online (Sandbox Code Playgroud)
这可以避免缓冲区溢出,但如果输入太长,可能会产生错误.可以/应该在格式字符串中指定大小,如前所述:
if (scanf_s("%63[^\n]", buffer, sizeof(buffer)) != 1)
...oops...
if (scanf_s(format, buffer, sizeof(buffer)) != 1)
...oops...
Run Code Online (Sandbox Code Playgroud)
请注意,对于使用生成的格式字符串的代码,必须忽略或抑制关于'非常量格式字符串'的警告(来自某些标记集下的某些编译器).
在scanf函数族中确实没有可变宽度说明符.替代方案包括动态创建格式字符串(尽管如果宽度是编译时常量,这看起来有点傻)或者只是接受幻数.一种可能性是使用预处理器宏来指定缓冲区和格式字符串宽度:
#define STR_VALUE(x) STR(x)
#define STR(x) #x
#define MAX_LEN 63
char buffer[MAX_LEN + 1];
fscanf(file, "%" STR_VALUE(MAX_LEN) "[^\n]", buffer);
Run Code Online (Sandbox Code Playgroud)