C语言中fgets()导致的分段错误

4
这是我不理解的问题 - 我在主函数中使用 fgets() 它可以工作。但是我认为在一个函数中以完全相同的方式使用它时,会出现错误(段错误核心已转储 - 退出代码 139)。
这段代码基于Ivor Horton的书籍“Beginning C”中的示例程序(虽然这是一本旧书,但我只想从中学习基础知识)。
我的程序如下。我正在*nix上使用Geany(基本上使用GCC编译)。您可以看到fgets在主函数中工作(输出是您输入的字符串)。但是在函数str_in()中无法正常工作。它最多只能执行第二个printf()语句来输入字符串,没有更进一步了。请注意,在书中,Horton使用gets()。我试图在这里实现一个更安全的字符串输入函数,但没有成功。
顺便说一下,该程序应该对存储在字符串指针数组中的字符串进行排序。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TRUE 1
#define FALSE 0
#define MAX_NUM_STRINGS 50


int str_in(char **);              /*Pointer to a string pointer*/
void str_sort(char *[], int n);   /*Array of pointers to strings, number of strings in array*/
void str_out (char *[], int n);   /*Array of pointers to strings, number of strings in array*/


int main(){

    char *pS[MAX_NUM_STRINGS] = { NULL };                  /*Array of pointers to strings stored in str_space*/
    int numStrings = 0;                                     /*Count of strings*/
    char buffer[BUFSIZ];

    printf("Enter a string\n");
    fgets(buffer, BUFSIZ, stdin);
    printf("%s", buffer);
    printf("fgets works here\n\n");


    /* get string input from user - a pointer to each string is saved in pS */
    while ( str_in(&pS[numStrings]) && numStrings < MAX_NUM_STRINGS)
        numStrings++;
    if ( numStrings > 0 ){
        str_sort(pS, numStrings);
        str_out(pS, numStrings);
    }

    return 0;
}


    int str_in(char** pString){

        char buffer[BUFSIZ];
        char *p;

        printf ("Enter string:\n");

        fgets(buffer, 60, stdin);
        printf("fgets doesn't work here!!\n");

        if( buffer != NULL ){
            printf("here");
            if ((p = strchr(buffer, '\n')) != NULL)
                *p = '\0';                                  /*replace newline with null character*/
            else
                return FALSE;
            if ( strlen(buffer) > 0 ){
                strcpy(*pString, buffer);
                return TRUE;
            }
            else
                return FALSE;                               /*blank line - end of input*/
        }
        else
            return FALSE;

    }


    void str_sort(char* pStrings[], int n){
    /*sort strings by manipulating array of string pointers*/

        char *temp;
        int sorted = FALSE;
        int i = 0;

        while (!sorted){

            sorted = TRUE;
            for(i = 0; i < n - 1; i++){
                temp = pStrings[i];
                if ( strcmp(temp, pStrings[i+1]) > 1 ){
                    pStrings[i] = pStrings[i+1];
                    pStrings[i+1] = temp;
                    sorted = FALSE;
                    break;
                }
            }

        }

    }


    void str_out(char* pStrings[], int n){
    /*print strings to standard output.  Free memory as each string is printed */

        int i = 0;

        printf("Sorted strings:\n");
        for(i = 0; i < n; i++){
            printf("%s", pStrings[i]);
            free(pStrings[i]);
        }

    }

我有什么遗漏吗?我没有看到任何分配字符串的空间。另外:在“str_in”缓冲区中不可能为NULL... - John3136
我认为只有在发生某种非常不寻常的输入错误的情况下才会为空-我遵循的示例在这里:http://faq.cprogramming.com/cgi-bin/smartfaq.cgi?answer=1044652485&id=1043284385 - topsail
@topsail: buffer 是一个数组对象,不是一个指针;它会衰变为其第一个元素的指针,该指针永远不会是 null。 - Keith Thompson
好的。现在看来这并不是必要的,但是根据Sundar下面的评论,如果fgets()返回NULL,那么空指针会被存储在我的变量缓冲区中(即使它不会改变数组中的任何内容)。 - topsail
2个回答

2
分段错误不是由 fgets() 引起的,而是由 strcpy() 引起的。
strcpy(*pString, buffer);

你试图对*pString进行写入,但你从未为其分配内存。main()中的pS只是一个空指针数组。
另一件事是在测试if(buffer != NULL)时,这将永远不会成立,因为buffer是一个数组,而不是一个指针。

谢谢。就是这样了。实际上,Horton的示例中有一行代码我没有提到:*pString = (char*)malloc(strlen(buffer) + 1); - topsail

1

您必须检查fgets的返回值,以确定是否成功接收到内容,如果没有,则不应将缓冲区用作字符串,因为您没有对缓冲区进行NUL终止。

/* Checking for buffer != NULL is of no use */
/* as buffer will always be not NULL since */
/* since you have allocated it as char buffer[BUFSIZ] */

if (fgets(buffer, BUFSIZ, stdin) == NULL) {
   /* buffer may not be a valid string */
}

因此,您可以在进入函数后(在声明完成后),将缓冲区初始化为NUl字符串。

buffer[0] = 0; /* initialize to NUL string */

现在你可以在任何地方将缓冲区用作字符串。

还要注意,如果BUFSIZ太大超过几KB,则可能由于堆栈溢出而导致段错误。如果它们太大,您可以将缓冲区设置为"static char"而不是"char"。


我有点明白你的意思,但是fgets()不会将空字符附加到输入中吗?只要它没有完全无法从用户那里获取输入,它应该至少是一个空字符(可能是一个换行符后跟一个空字符)。 - topsail
根据C标准(第7.19.7.2节),“如果遇到文件结尾并且没有字符读入数组,则数组内容保持不变,并返回空指针”。当fgets的返回值为NULL时,无需附加NUL。 - Sundar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接