简体   繁体   English

使用scanf()时出现段错误

[英]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 “字符串”字面量的存储是

  1. constant 不变
  2. and even if it weren't, it does not provide any additional room for anything to be appended. 即使没有,它也没有提供任何额外的空间来附加任何内容。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM