【问题标题】:C Program Segmentation Fault main()C 程序分段错误 main()
【发布时间】:2015-06-14 02:17:27
【问题描述】:

我是 C 编程的新手,我已经为需求规范编写了代码,但我一直遇到分段错误并且无法继续进行。 如果文件名是“code.c”并且它运行时出现未传递参数(文件名)的错误。但是如果文件名通过了,我们就会陷入分段错误。 任何帮助/建议将不胜感激。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>



struct _data 
{               
   char *firstName;
   char *lastName;
   long number;
}; 

// SCAN FILE
int SCAN(FILE *(*stream))
{
    *stream = fopen("inputFile.data", "r");

    int ch = 0, lines = 0;

    while (!feof(*stream))
    {
        ch = fgetc(*stream);
        if (ch == '\n')
        {
            lines++;
        }
    }
    return lines;
}

// LOAD FILE
struct _data *LOAD(FILE *stream, int size) 
{
    int i;
    size_t chrCount;
    char *text, *number, *firstName, *lastName;
    struct _data *BlackBox;

    if ((BlackBox = (struct _data*)calloc(size, sizeof(struct _data))) == NULL) 
    {
          printf("ERROR - Could not allocate memory.\n");
          exit(0);
    }

    rewind(stream);


    for (i = 0; i < size; i++) 
    {
        getline(&text, &chrCount, stream);
        firstName   = strtok(text, " ");
        lastName    = strtok(text, " ");
        number      = strtok(NULL, "\n");

        // Allocate memory for name part of struct.
        if ((BlackBox[i].firstName = (char*)calloc(strlen(firstName), sizeof(char))) == NULL) 
        {
           printf("ERROR - Could not allocate memory.\n");
           exit(0);
        }
        if ((BlackBox[i].lastName = (char*)calloc(strlen(lastName), sizeof(char))) == NULL)
        {
           printf("ERROR - Could not allocate memory.\n");
           exit(0);
        }

        strcpy(BlackBox[i].firstName, firstName);
        strcpy(BlackBox[i].lastName, lastName);
        BlackBox[i].number = atol(number);
    }
    fclose(stream);
    return BlackBox;
}


void SEARCH(struct _data *BlackBox, char *name, int size, int inputs) 
{
    int i;
    int found = 0;
    char *search = " ";
    char *firstName;
    char *lastName; 

    if (inputs == 2)
    {
        firstName = strtok(name, search);
        lastName = strtok(NULL, search);
    }


    printf("*******************************************\n");
    if (inputs == 2)
    {   
        for (i = 0; i < size; i++) 
        {          
            if (!strcasecmp(firstName, BlackBox[i].firstName) && !strcasecmp(firstName, BlackBox[i].firstName))
            {
                printf("The name was found at the %d entry.\n", i);
                found = 1;
                break;
            }
        }
    }
    else
    {
        for (i = 0; i < size; i++) 
        {          
            if (!strcasecmp(firstName, BlackBox[i].firstName) || !strcasecmp(firstName, BlackBox[i].firstName))
            {
                printf("The name was found at the %d entry.\n", i);
                found = 1;
                break;
            }
        }
    }

    if (found == 0) 
    {
          printf("The name was NOT found.\n");
    }
    printf("*******************************************\n");
}

// FREE MEMORY
void FREE(struct _data *BlackBox, int size) 
{
    int i;
    for (i = 0; i < size; i++) 
    {
        free(BlackBox[i].firstName);
        free(BlackBox[i].lastName);
    } 
    free(BlackBox);
    BlackBox = NULL;
}


// MAIN
int main(int argv, char **argc) 
{
    int size;
    FILE *stream;
    struct _data *BlackBox;

    // argv == 1 WORKS, Below message is printed.
    if (argv == 1) 
    {          
        printf("*******************************************\n");
        printf("* You must include a name to search for.  *\n");
        printf("*******************************************\n");
    }
    // argv == 2 DOES NOT WORK, Segmentation Fault.     
    if (argv == 2) 
    {
        size = SCAN (&stream);
        BlackBox = LOAD(stream, size);
        SEARCH(BlackBox, argc[1], size, 1);
    }
    if (argv == 3) 
    {
        size = SCAN(&stream);
        BlackBox = LOAD(stream, size);
        SEARCH(BlackBox, argc[2], size, 2);
    }
    return 0;
}

【问题讨论】:

  • 你永远不会检查fopen() 是否成功!而while (!feof(file)) 总是错的。
  • 您是否尝试在它崩溃时留下的核心文件上运行pstack(假设您在系统上正确配置了核心转储设置),或者在 gdb 等调试器中运行它,所以当它崩溃了你可以学习#?当一个程序 segv 的第一件事你需要做的是找出它发生在哪一行。
  • 实际上pstack &lt;core&gt; 可以在 Solaris 上工作,但在 Linux 上可能不行,但这应该是:gdb path/to/corefile/&lt;core&gt;(gdb) where(gdb) thread apply all bt
  • if ((BlackBox[i].firstName = (char*)calloc(strlen(firstName), NUL +1
  • @iharob 我没有,因为文件在我身边,这不是企业代码。我确实理解这是错误的,但这是否会导致 SF 故障?

标签: c segmentation-fault argv argc


【解决方案1】:

这段代码有问题:

    firstName   = strtok(text, " ");
    lastName    = strtok(text, " ");
    number      = strtok(NULL, "\n");

...

    BlackBox[i].number = atol(number);

第二个strtok() 调用应该传递NULL 作为它的第一个参数。事实上,第三个strtok() 调用一定返回NULL,因为第一个调用修改了text,使得第二个调用消耗了整个东西(从头开始再次标记时) ,因为它错误地这样做)。但是,您不会对此进行测试,因此,atol() 会尝试取消对空指针的引用。

更新

此外,正如@chqrlie 和后来的@JamesWilkins 所观察到的,您没有为BlackBox[i].firstNameBlackBox[i].lastName 分配足够的空间,因为您还需要为字符串终止符留出空间。这是一个完全独立的问题,可能产生段错误。我喜欢@chqrlie 建议切换到strdup(),但只需将每个分配增加一个单位就足够了。

更新 2

此外,您对此行有疑问:

    getline(&text, &chrCount, stream);

你没有在第一次调用之前初始化变量text,所以它包含一个垃圾值。仅当函数的第一个参数指向 NULL 指针时,该函数才分配缓冲区;否则,它将该行写入通过取消引用第一个参数获得的指针所指向的缓冲区。写入内存中的随机位置肯定会产生未定义的行为,这在实践中通常表现为段错误。

此外,除非您可以依赖文件的任何行都不会比第一行长,否则您还需要在每次循环迭代结束时释放text 指针并将其值重置为NULL,以便@ 987654336@ 在下一次迭代中分配一个新的缓冲区。如果您不在每次迭代时释放它,那么您需要在循环结束后释放它;否则你会泄漏内存。

【讨论】:

  • 我正在尝试读取包含格式为“John Doe 777777”的行的文件,并尝试以firstName, lastName, number 的形式拆分它们。纠正这种情况的最佳方法是什么。谢谢。
  • 我告诉过你。第二个strtok() 调用必须传递NULL 作为它的第一个参数。这就是您如何指示它返回 next 标记,而不是从头开始再次标记。作为次要(但仍然很重要)的问题,您应该始终检查 strtok() 的返回值,以便比崩溃更优雅地处理输入数据中的错误。
  • 我已经进行了更改。 lastName = strtok(NULL, " "); 我仍然得到同样的错误。
  • 这是因为你有更多的错误,正如其他人所观察到的那样。我已经更新了我的答案,再喊两个。
【解决方案2】:

试试这个(虽然我在 Windows 上使用 Visual Studio)。我添加了代码来检查最后一行是否缺少“\n”,并且还允许使用可变数量的搜索词。我还将字符串的内存分配增加了 1,以解决空终止字符的问题。我注意到您使用的是getline(const char*...,我认为它是GNU(Linux?),所以我将其更改为fgets(),这样我就可以在VS中编译和测试它(所以如果你愿意,你可以把它改回来)。为了更安全,我还进行了各种空检查。

#include <iostream>

using namespace std;

struct _data
{
    char *firstName;
    char *lastName;
    long number;
};

// SCAN FILE
int SCAN(FILE *(*stream))
{
    *stream = fopen("inputFile.data", "r");
    if (*stream == NULL)
    {
        perror("Error opening file");
        return 0;
    }

    char ch = 0, lines = 0, linesize = 0;

    while ((ch = fgetc(*stream)) != EOF)
    {
        if (ch == '\n')
        {
            lines++;
            linesize = 0;
        }
        else linesize++;
    }

    if (linesize > 0)
        lines++; // (last line doesn't have '\n')

    return lines;
}

// LOAD FILE
struct _data *LOAD(FILE *stream, int lineCount)
{
    int i;
    size_t chrCount = 256;
    char text[256], *result, *number, *firstName, *lastName;
    struct _data *BlackBox;

    if ((BlackBox = (struct _data*)calloc(lineCount, sizeof(struct _data))) == NULL)
    {
        printf("ERROR - Could not allocate memory.\n");
        exit(0);
    }
    else memset(BlackBox, 0, sizeof(struct _data) * lineCount); // (make sure all data members are null to begin)

    rewind(stream);


    for (i = 0; i < lineCount; i++)
    {
        result = fgets(text, chrCount, stream);
        if (result == NULL)
            break; // (EOF)

        firstName = strtok(text, " ");
        lastName = strtok(NULL, " ");
        number = strtok(NULL, "\n");

        // Allocate memory for name part of struct.
        if ((BlackBox[i].firstName = (char*)calloc(strlen(firstName) + 1, sizeof(char))) == NULL)
        {
            printf("ERROR - Could not allocate memory.\n");
            exit(0);
        }
        if ((BlackBox[i].lastName = (char*)calloc(strlen(lastName) + 1, sizeof(char))) == NULL)
        {
            printf("ERROR - Could not allocate memory.\n");
            exit(0);
        }

        strcpy(BlackBox[i].firstName, firstName);
        strcpy(BlackBox[i].lastName, lastName);
        BlackBox[i].number = atol(number);
    }

    fclose(stream);

    return BlackBox;
}


void SEARCH(struct _data *BlackBox, char **names, int lineCount, int inputs)
{
    int i, l;
    int found = 0;

    printf("*******************************************\n");

    for (i = 0; i < inputs; ++i)
    {
        for (l = 0; l < lineCount; ++l)
        {
            if (BlackBox[l].firstName != NULL && !_stricmp(names[i], BlackBox[l].firstName)
                || BlackBox[l].lastName != NULL && !_stricmp(names[i], BlackBox[l].lastName))
            {
                printf("The name was found on line %d.\n", 1 + l);
                found = 1;
                break;
            }
        }
        if (found) break;
    }

    if (!found)
        printf("The name was NOT found.\n");

    printf("*******************************************\n");
}

// FREE MEMORY
void FREE(struct _data *BlackBox, int lineCount)
{
    int i;
    for (i = 0; i < lineCount; i++)
    {
        if (BlackBox[i].firstName != NULL)
            free(BlackBox[i].firstName);
        if (BlackBox[i].lastName != NULL)
            free(BlackBox[i].lastName);
    }
    free(BlackBox);
}


// MAIN
int main(int argc, char **argv)
{
    int lineCount;
    FILE *stream;
    struct _data *BlackBox;

    // argc == 1 WORKS, Below message is printed.
    if (argc == 1)
    {
        printf("*******************************************\n");
        printf("* You must include a name to search for.  *\n");
        printf("*******************************************\n");
    }
    // argc == 2 DOES NOT WORK, Segmentation Fault.     
    if (argc > 1)
    {
        lineCount = SCAN(&stream);
        if (lineCount > 0)
        {
            BlackBox = LOAD(stream, lineCount);
            SEARCH(BlackBox, argv + 1, lineCount, argc - 1);
            FREE(BlackBox, lineCount);
        }
    }
    return 0;
}

在命令行上测试过,效果不错。

【讨论】:

    【解决方案3】:

    问题在于argvargcargc 应该是一个 int(想想参数 count),而 argv 应该是 char**。你把它们混在你的main里了。

    【讨论】:

    • 这是非常规的,但它是正确的代码。 main 的参数没有特殊名称,如果你愿意,你可以称它们为donaldsean
    • 我明白了,感谢您指出这一点。无论如何,在这种情况下最好遵循约定(尤其是在使用名称argcargv 时)。它只是使代码对其他人更具可读性。
    猜你喜欢
    • 2012-01-29
    • 2021-01-08
    • 1970-01-01
    • 1970-01-01
    • 2012-10-12
    • 2018-11-18
    • 2011-05-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多