如何防止scanf在C中导致缓冲区溢出?
我使用这个代码:
while ( scanf("%s", buf) == 1 ){
什么是防止可能的缓冲区溢出的最好方法,以便它可以传递随机长度的string?
我知道我可以通过调用例如限制inputstring:
while ( scanf("%20s", buf) == 1 ){
但我更喜欢能够处理任何用户input。 或者不能这样做安全使用scanf,我应该使用fgets?
Kernighan和派克在他们的书“编程实践” (这是值得一读的)中讨论了这个问题,他们通过使用snprintf()
创build了正确的缓冲区大小的string来传递给scanf()
函数族。 有效:
int scanner(const char *data, char *buffer, size_t buflen) { char format[32]; if (buflen == 0) return 0; snprintf(format, sizeof(format), "%%%ds", (int)(buflen-1)); return sscanf(data, format, buffer); }
请注意,这仍将input限制为“缓冲区”提供的大小。 如果你需要更多的空间,那么你必须做内存分配,或者使用一个非标准的库函数为你分配内存。
请注意, scanf()
系列函数的POSIX 2008(2013)版本支持stringinput( %s
, %c
, %[
))的格式修饰符m
(分配分配字符)。 而不是采取一个char *
参数,它需要一个char **
参数,并为它读取的值分配必要的空间:
char *buffer = 0; if (sscanf(data, "%ms", &buffer) == 1) { printf("String is: <<%s>>\n", buffer); free(buffer); }
如果sscanf()
函数未能满足所有转换规范,那么在函数返回之前释放为%ms
类转换分配的所有内存。
如果你使用的是gcc,你可以使用GNU扩展的a
说明符让scanf()为你分配内存来保存input:
int main() { char *str = NULL; scanf ("%as", &str); if (str) { printf("\"%s\"\n", str); free(str); } return 0; }
编辑:正如乔纳森指出的,你应该咨询scanf
手册页,因为说明符可能不同( %m
),你可能需要在编译时启用某些定义。
大部分时间, fgets
和sscanf
的组合完成这项工作。 另一件事是编写自己的parsing器,如果input格式良好。 还要注意你的第二个例子需要一些修改才能安全使用:
#define LENGTH 42 #define str(x) # x #define xstr(x) str(x) /* ... */ int nc = scanf("%"xstr(LENGTH)"[^\n]%*[^\n]", array);
上面放弃inputstream,但不包括换行符( \n
)字符。 你将需要添加一个getchar()
来消耗这个。 还要检查你是否到达了stream的末尾:
if (!feof(stdin)) { ...
就是这个。
直接使用scanf(3)
及其变种会带来一些问题。 通常,用户和非交互式用例是根据input行来定义的。 很less见到这样的情况,如果没有find足够的对象,更多的线将解决问题,但这是scanf的默认模式。 (如果用户不知道在第一行input号码,第二行和第三行可能不会帮助。)
至less如果你fgets(3)
你知道你的程序将需要多lessinput行,并且你将不会有任何缓冲区溢出…
限制input的长度肯定比较容易。 您可以使用循环接受任意长度的input,每次只读一点,必要时为string重新分配空间。
但是这是很多工作,所以大多数C程序员只是在任意长度上切断input。 我想你已经知道这一点,但使用fgets()不会允许你接受任意数量的文本 – 你仍然需要设置一个限制。
做一个为你的string分配所需内存的函数并没有太多的工作。 这是我前段时间写的一个小函数,我总是用它来读取string。
它将返回读取string,或者如果发生内存错误NULL。 但请注意,您必须释放()您的string,并始终检查它的返回值。
#define BUFFER 32 char *readString() { char *str = malloc(sizeof(char) * BUFFER), *err; int pos; for(pos = 0; str != NULL && (str[pos] = getchar()) != '\n'; pos++) { if(pos % BUFFER == BUFFER - 1) { if((err = realloc(str, sizeof(char) * (BUFFER + pos + 1))) == NULL) free(str); str = err; } } if(str != NULL) str[pos] = '\0'; return str; }