[英]Segfault when using scanf()
I am a beginner with C. This is a simple program that prompts for a name from keyboard input, creates a greeting using the name, then prints it. 我是C的初学者。这是一个简单的程序,可以从键盘输入中提示输入名称,使用该名称创建问候语,然后进行打印。 At runtime, immediately after entering the name at the console and pressing enter, a segmentation fault occurs.
在运行时,在控制台上输入名称并按Enter后,立即发生分段错误。 After debugging I suspect the fault lies at the scanf() function.
调试后,我怀疑故障在于scanf()函数。 I've tried tweaking the argument 'name' with '*' and '&', and initializing the char array 'name' with an empty string, but none of this helped.
我尝试用'*'和'&'调整参数'name',并用空字符串初始化char数组'name',但是这些都没有帮助。
// Prompt for a name and print a greeting using the name.
#include <stdio.h>
#include <string.h>
int main()
{
// Prompt for a name.
printf("What is your name? ");
// Get the name.
char name[20];
scanf("%s", name); // Suspect segfault occurs here...
// Construct the greeting.
char *greeting;
char *suffix;
greeting = "Hello, ";
suffix = ", nice to meet you!";
strcat(greeting, name);
strcat(greeting, suffix);
// Display the greeting.
printf("%s", greeting);
return 0;
}
The two calls to strcat()
对
strcat()
的两次调用
char *greeting;
char *suffix;
greeting = "Hello, ";
suffix = ", nice to meet you!";
strcat(greeting, name);
strcat(greeting, suffix);
provoke undefined behaviour by trying to append to storage belonging to a "string"-literal ( "Hello, "
). 通过尝试将附加到属于“字符串”字面量(
"Hello, "
)的存储区追加来激发未定义的行为。
A "string"-literal's storage is “字符串”字面量的存储是
To fix this provide a buffer sufficiently large and copy in there everything that is needed: 要解决此问题,请提供足够大的缓冲区并在其中复制所需的所有内容:
char *greeting;
char *suffix;
greeting = "Hello, ";
suffix = ", nice to meet you!";
char buffer[7 + 20 + 19 + 1]; /* 7 for "Hello, ",
20 for name (which in fact for your code needed to be 19 only),
19 for ", nice to meet you!" and
1 for the 0-terminator. */
strcpy(buffer, greeting); /* Use strcpy() to copy to an uninitialised buffer. */
strcat(buffer, name);
strcat(buffer, suffix);
Also to make sure the user does not overflow the memory provided for name
tell scanf()
how much is available; 同样要确保用户不会溢出为
name
提供的内存,请告诉scanf()
多少可用空间。
char name[20 + 1]; /* If you need 20 characters, define 1 more to hold the
"string"'s 0-terminator. */
scanf("%20s", name); /* Tell scanf() to read in a maximum of 20 chars. */
strcat(greeting, name);
This call to strcat
is modifying a string constant - that's not legal, and that's what's causing your segfault (techincally, what you're seeing is the result of undefined behaviour ). 这种对
strcat
调用正在修改一个字符串常量-这是不合法的,这就是导致段错误的原因(从技术上讲,您所看到的是未定义行为的结果)。
For completeness: 为了完整性:
scanf("%s", name);
If the buffer is limited to a size of 20, then you should use: 如果缓冲区的大小限制为20,则应使用:
scanf("%19s", name);
... to limit the number of characters actually stored (though there are better ways to read a variable-length line). ...以限制实际存储的字符数(尽管有更好的方法来读取可变长度的行)。 I used 19 because there needs to be space for the nul terminator character.
我使用19是因为nul终止符必须有空间。
Then, allocate a suitable storage for your complete string: 然后,为您的完整字符串分配合适的存储空间:
char full_greeting[20 + 7 + 19] = ""; // name + "hello"... + "nice to meet"...
And copy into that: 并复制到其中:
strcpy(full_greeting, greeting);
strcat(full_greeting, name);
strcat(full_greeting, suffix);
printf("%s", full_greeting);
Dynamic string solution (POSIX) 动态字符串解决方案(POSIX)
On a POSIX system, you can have scanf allocate a buffer for the name it reads: 在POSIX系统上,可以让scanf为读取的名称分配一个缓冲区:
char *name = NULL;
scanf("%ms", &name); // you could also use 'getline' function
if (name == NULL) {
exit(1); // scanf failed or memory allocation failed
}
(Note that using getline
would read an entire line, which is not the same as the current scanf
, which reads a string up to the first whitespace). (请注意,使用
getline
将读取整行,这与当前的scanf
,该scanf
读取直到第一个空格的字符串)。
Then, you calculate the length of your buffer dynamically: 然后,您可以动态计算缓冲区的长度:
int req_len = strlen(name) + strlen(greeting) + strlen(suffix) + 1;
// (+1 is for nul terminator)
char * buffer = malloc(req_len);
if (buffer == NULL) {
exit(1); // or handle the error somehow
}
strcpy(buffer, greeting);
strcat(buffer, name);
strcat(buffer, suffix);
printf("%s", buffer);
free(buffer);
free(name);
The problem is with 问题出在
strcat(greeting, name);
greeting
points to read-only
memory. greeting
指向read-only
存储器。 Appending name
to greeting
attempts altering contents in it. 将
name
附加到greeting
尝试更改其内容。 The result is segfault. 结果是段错误。
char *greeting;
char *suffix;
greeting = "Hello, ";
suffix = ", nice to meet you!";
strcat(greeting, name);
strcat(greeting, suffix);
You are making an strcat
when the destination is not only non-modifiable, but also too small. 当目的地不仅不可修改,而且目的地太小时,您将产生
strcat
。 You may know that writing 您可能知道写作
char *p = "hello";
*p = 'x';
is undefined behavior. 是未定义的行为。 This is what
strcat
is doing to the greeting
argument that you pass in. The solution is 这就是
strcat
对传入的greeting
参数所做的事情。解决方案是
#define MAXBUF 64
char *mystrcat(char *dest, char *src)
{
while (*dest)
dest++;
while (*dest++ = *src++)
;
return --dest;
}
char greeting[MAXBUF], *p;
strcpy(greeting, "hello, ");
p = mystrcat(greeting, name);
mystrcat(p, ", nice to meet you");
Also, notice the new function mystrcat
. 另外,请注意新功能
mystrcat
。 This is an optimization that is explained in Joel Spolsky's famous post . 这是乔尔·斯波斯基(Joel Spolsky)着名文章中解释的一种优化。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.