【问题标题】:How can a simple assignment cause a segmentation fault?一个简单的分配如何导致分段错误?
【发布时间】:2016-10-25 15:33:34
【问题描述】:

我正在编写一个程序来将文件拆分为词位,但我不熟悉 C,并且遇到了一个奇怪的分段错误。以下代码运行并打印:

1
2
Segmentation Fault: 11

我查找了导致分段错误的其他原因,但在 2 之后我没有对数组做任何事情。我做错了什么?一个简单的赋值怎么会导致分段错误?

#include <stdio.h>

#define CHAR_CODE 1
#define NUM_CODE 2
#define RESERVED_CHAR_CODE 3
#define SPACE_CODE 4
#define ERR_CODE 5

char token[16];
char line[128];
int lineLen = 0;
int counter = 0;
FILE *file;

void cleanToken(){
    for(int i = 0 ; i < 16; i++){
        token[i] = 0;
    }
}

void cleanLine(){
    for(int i = 0 ; i < 128; i++){
        line[i] = 0;
    }
}

void next(){
    cleanToken();
    printf("1\n");
    char c;
    if(counter == lineLen){
        printf("2\n");
        lineLen = 0;
        printf("3");
        cleanLine();
        printf("4");
        counter = 0;
        while((c=getc(file)) != 10 && c != EOF){
            if(c!=32){
                line[lineLen] = c;
                lineLen++;
            }
            printf("%d\n", lineLen);
        }
        printf("%s\n", line);
    }
    c = line[counter];
    int type = getType(c);
    while(getType(c)==type){
        token[counter] = c; 
        counter = counter + 1;
        c = line[counter];
        if(c == EOF | c == 4 | type == RESERVED_CHAR_CODE){
            break;  
        }
    }
}

int getType(int c){
    if((c > 96 & c < 123) | (c>64 & c < 91)){
        return CHAR_CODE;
    } else if(c < 58 & c > 46){
        return NUM_CODE;
    } else if(c == 32){
        return SPACE_CODE;
    } else if(c == 33 | c == 34 | (c > 36 & c < 44) | c == 45 | c == 47 | (c > 58 & c < 63)){

        return RESERVED_CHAR_CODE;  
    } else {
        return ERR_CODE;
    }
}

int main(){
    int c;
    FILE* file;
    file = fopen("test.txt", "r");
    for(int i = 0; i < 10 ;i++){
        next();
        printf("%s\n", token);
    }
    return 0;
}

【问题讨论】:

  • 编译时启用所有警告。
  • 您的test.txt 文件中有什么内容?
  • 请缩进你的代码,给我们一个最小的例子......也就是说,这样一个简单的printf 语句的段错误通常意味着你的token 不是正确的空终止。
  • 与您的问题无关,但您应该避免使用以下行中的幻数:if(c == 33 | c == 34 | (c &gt; 36 &amp; c &lt; 44) | c == 45 | c == 47 | (c &gt; 58 &amp; c &lt; 63))。什么是3334 等?顺便说一句:你可以写 'A',而不是写 65。
  • 另外,我很确定您希望使用 || 运算符(逻辑或)而不是 |(按位或)。

标签: c


【解决方案1】:

你的代码有很多问题,大部分会在你编译时使用-Wall来显示,但具体来说,崩溃是由于尝试访问全局变量file而导致的,而这并没有被初始化。

您在 main() 中使用另一个同名变量对其进行了隐藏。您初始化了那个,但不是next() 访问的全局变量

此外,您的 printf 语句不足以进行调试,因为无法保证在程序崩溃之前将输出缓冲区刷新到您的控制台。如果您将\n 添加到printf("3") and printf("4") 行,您将看到它们在崩溃之前被打印出来。或者,您可以在printf() 调用之后调用fflush(stdout) 以确保它们被打印。通过这样做,您将看到崩溃并没有因为它出现的分配而发生。

gdb 之类的调试器对调试代码非常有帮助。

【讨论】:

    【解决方案2】:

    编辑

    去吧。刚刚看到 eddiem 的回答,他是对的 - 这是您代码中的真正问题。

    但是,您仍应在循环中添加 lineLen 签入。

    原创

    要赌真钱问题就在这里:

    while((c=getc(file)) != 10 && c != EOF){
        if(c!=32){
            line[lineLen] = c;  
            lineLen++;
        }
    

    如果文件在一行中包含超过 128 个字符会怎样?你最终会写到数组的末尾,这可能会导致各种混乱。

    您需要添加一个检查以确保您没有超出数组边界:

    while ( lineLen < 128 && ( c = getc( file ) ) != 10 && c != EOF ) { ... }
    

    您没有看到 34 打印出来,因为您在这些 printf 语句中没有换行符。 stdout 通常是行缓冲的,这意味着除非缓冲区已满或发送换行符,否则输出不会出现在终端上。

    【讨论】:

      【解决方案3】:

      您有两个不同的file 变量,一个是在程序顶部声明的全局变量,另一个是在main 函数中声明的全局变量。

      next 函数中,您使用从未分配过的全局file

      #include <stdio.h>
      ...
      FILE *file;  // <<< global one
      ...
      
      
      int main() {
        int c;
        FILE* file;   // <<<local one
      
        file = fopen("x.txt", "r");
         ...
        return 0;
      }
      

      又一个例子说明了为什么滥用全局变量会导致问题。

      很可能还有其他问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-04-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-02
        • 1970-01-01
        相关资源
        最近更新 更多