C - 程序为SEGFAULT提供了for循环,但并非没有

Enz*_*ura 2 c segmentation-fault

我正在做一个读取3x3矩阵的程序.

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

typedef struct { 
    int row;
    int col;
    long **tab;
} matr;

int SIZE = 3;

void *emalloc(size_t size) {
    void *memory = malloc(size);

    if (!memory) {
        fprintf(stderr, "ERROR: Failed to malloc.\n");
        exit(1);
    }

    return memory;
}

void file_to_matrix(FILE *path_matr, matr *m) {
    long **matrix = (long**) emalloc(SIZE * sizeof(long*));
    for (int i = 0; i < SIZE; i++) matrix[i] = (long*) emalloc(SIZE * sizeof(long));

    char line[4];
    fscanf(path_matr, " %[^\n]", line);

    // This code does not give SEGFAULT
    // for (int i = 0; i < SIZE; i++) {
    //  fscanf(path_matr, "%ld%ld%ld", &matrix[i][0], &matrix[i][1], &matrix[i][2]);
    // }

    // The code below gives SEGFAULT
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            fscanf(path_matr, "%ld", &matrix[i][j]);
        }
    }

    m->row = SIZE;
    m->col = SIZE;
    m->tab = matrix;
}

int main(int args, char *argv[]) {
    FILE *path_matr = fopen(argv[1], "r");

    /*Getting the matrices*/
    int n_matr; // Number of matrices
    fscanf(path_matr, "%d", &n_matr);
    matr *matrices = emalloc(n_matr * sizeof(matr));

    for (int i = 0; i < n_matr; i++) {
        file_to_matrix(path_matr, &matrices[i]);
    }

    for (int i = 0; i < n_matr; i++)
        free(matrices[i].tab);
    free(matrices);
    fclose(path_matr);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

请注意,file_to_matrix函数中有两个for循环.一个版本给出了分段错误,另一个版本没有.为什么?有趣的是:如果我启用-O2,两个版本都有效.

使用gcc -std = c11 test.c -o test -g && ./test in.txt(gcc version 4.9.2)编译.

in.txt:

3
???
11 12 1444
21 22 23
31 32 33
???
11 12 13
21 22 23
31 32 33
???
11 12 13
21 22 23
31 31 33
???
Run Code Online (Sandbox Code Playgroud)

PS:我在这里发布的代码是另一个代码的一部分,为了简单起见,我从中删除了一些块(例如检查参数的数量,fopen的返回等).我在这里描述的问题也发生在原始代码中.

Dav*_*ins 6

我认为你正在造成缓冲区溢出

char line[4];
fscanf(path_matr, " %[^\n]", line);
Run Code Online (Sandbox Code Playgroud)

尝试改为

fscanf(path_matr, " %3[^\n]", line);
Run Code Online (Sandbox Code Playgroud)

或以其他方式减轻溢出的可能性.

字符宽度

确保您使用*(unicode 0x2a)而不是?(unicode 0xE2 0x88 0x97)用于分隔符.后者中较大的字符宽度导致scanf()提前终止.(当我在你的问题上复制并粘贴样本输入时,它包括更广泛的字符.)

当你这样做时,最大字段宽度说明符scanf()并不是绝对必要的 - 但我仍然会使用它.

优化

当您优化时增加优化-O2 matrix- 因此程序甚至不会尝试写入它.

PS:你应该检查来自的返回值fopen().

  • UB是UB.你读了一个数组的结尾,发生了狂野而有趣的事情.也许阵列有更大的空间.对齐变化?好/坏运气...... (2认同)
  • @EnzoNakamura这可能与优化有关.一个版本继续工作,因为该值在寄存器中,它忽略了您在内存中覆盖它.也许.正如迈克尔所说,未定义的行为. (2认同)
  • @EnzoNakamura:嗨.第一个版本对我的任何一个都不起作用(使用gcc 6.3.0).关键点是"矩阵"的地址被覆盖,因此 - 即使第一个循环不会导致分段错误 - 这只是一个巧合.第一个版本可能不是段错误,但您是否尝试过从"矩阵"读取值? (2认同)