【发布时间】:2011-12-04 07:10:13
【问题描述】:
我想读取用户输入的字符串。我不知道字符串的长度。由于 C 中没有字符串,我声明了一个指针:
char * word;
并使用scanf 从键盘读取输入:
scanf("%s" , word) ;
但我遇到了分段错误。
当长度未知时,如何从 C 中的键盘读取输入?
【问题讨论】:
我想读取用户输入的字符串。我不知道字符串的长度。由于 C 中没有字符串,我声明了一个指针:
char * word;
并使用scanf 从键盘读取输入:
scanf("%s" , word) ;
但我遇到了分段错误。
当长度未知时,如何从 C 中的键盘读取输入?
【问题讨论】:
您没有为 word 分配存储空间 - 它只是一个 dangling pointer。
变化:
char * word;
到:
char word[256];
请注意,此处 256 是任意选择 - 此缓冲区的大小需要大于您可能遇到的最大可能字符串。
另请注意,fgets 是比 scanf 更好(更安全)的选项,用于读取任意长度的字符串,因为它需要一个 size 参数,这反过来有助于防止缓冲区溢出:
fgets(word, sizeof(word), stdin);
【讨论】:
256 的选择完全是任意的,所以您刚刚(嗯,2.5 年前)教他如何编写缓冲区溢出和巨大的安全漏洞。恭喜。
fgets 而不是scanf 任意长度的建议字符串输入。
我不明白为什么建议在这里使用scanf()。 scanf() 只有在格式字符串中添加限制参数时才是安全的——比如%64s 左右。
更好的方法是使用char * fgets ( char * str, int num, FILE * stream );。
int main()
{
char data[64];
if (fgets(data, sizeof data, stdin)) {
// input has worked, do something with data
}
}
(未经测试)
【讨论】:
从不知道长度的任何文件(包括标准输入)读取输入时,通常最好使用getline 而不是scanf 或fgets,因为getline 将处理您的字符串的内存分配只要您提供一个空指针来接收输入的字符串,它就会自动执行。这个例子将说明:
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[]) {
char *line = NULL; /* forces getline to allocate with malloc */
size_t len = 0; /* ignored when line = NULL */
ssize_t read;
printf ("\nEnter string below [ctrl + d] to quit\n");
while ((read = getline(&line, &len, stdin)) != -1) {
if (read > 0)
printf ("\n read %zd chars from stdin, allocated %zd bytes for line : %s\n", read, len, line);
printf ("Enter string below [ctrl + d] to quit\n");
}
free (line); /* free memory allocated by getline */
return 0;
}
相关部分是:
char *line = NULL; /* forces getline to allocate with malloc */
size_t len = 0; /* ignored when line = NULL */
/* snip */
read = getline (&line, &len, stdin);
将line 设置为NULL 会导致getline 自动分配内存。示例输出:
$ ./getline_example
Enter string below [ctrl + d] to quit
A short string to test getline!
read 32 chars from stdin, allocated 120 bytes for line : A short string to test getline!
Enter string below [ctrl + d] to quit
A little bit longer string to show that getline will allocated again without resetting line = NULL
read 99 chars from stdin, allocated 120 bytes for line : A little bit longer string to show that getline will allocated again without resetting line = NULL
Enter string below [ctrl + d] to quit
因此,使用getline,您无需猜测用户字符串的长度。
【讨论】:
fgets() 诉getline()。 无限 getline(),IMO 太接近黑客利用,因为它允许外部提供的数据压倒程序。然而,固定缓冲区 (fgets()) 的限制太大。我玩弄过为fgets() 分配一个巨大的缓冲区并依靠*delayed allocation 来控制事情。
getline 来缓冲一个868789 字符串——没有抱怨。我将深入研究您的延迟分配链接,看看那里有什么珍珠/危险。 getline 提供的灵活性无可替代。我已经修改了使用fgetc 编写替换,但你或多或少地重写了getline。 getline 中的保护是它确实继续分配,防止溢出,但它让您有责任检查结果的大小是否合理......
#include<stdio.h>
int main()
{
char str[100];
scanf("%[^\n]s",str);
printf("%s",str);
return 0;
}
输入:读取字符串
输出:打印字符串
此代码打印带有空格的字符串,如上所示。
【讨论】:
你需要让指针指向某个地方才能使用它。
试试这个代码:
char word[64];
scanf("%s", word);
这会创建一个长度为 64 的字符数组并读取它的输入。请注意,如果输入长度超过 64 字节,则字数组会溢出,您的程序将变得不可靠。
正如 Jens 指出的,最好不要使用 scanf 来读取字符串。这将是安全的解决方案。
char word[64]
fgets(word, 63, stdin);
word[63] = 0;
【讨论】:
scanf 来读取字符串;使用 fgets 代替它接受一个长度参数。
fgets 已经为零终止输入字符串。此外,与*scanf 不同,它说明了这一点,因此使用数组的完整大小是惯用的:fgets(word, sizeof word, stdin)
fgets(word, 63, stdin); word[63] = 0; --> fgets(word, sizeof word, stdin);
以下代码可用于读取用户的输入字符串。但是它的空间限制在 64 个。
char word[64] = { '\0' }; //initialize all elements with '\0'
int i = 0;
while ((word[i] != '\n')&& (i<64))
{
scanf_s("%c", &word[i++], 1);
}
【讨论】: