ANSI C getc在Linux上導致段錯誤,但不在OS X上




file-io malloc (2)

我在我的Mac上開發了一些ANSI C代碼,但是當我嘗試在我們學校的Linux服務器上運行它時,出現了段錯誤。

引起我麻煩的特定行是來自文件指針的getc

該文件確實存在。

這是有問題的方法:

// inits lists with all data in fp file pointer
// returns # of lines read
int init_intlists(FILE *fp, INTLIST *lists[]) {
    int c, ctr;

    ctr = 0;

    // need to use a linked list to store current number
    // for non 1-digit numbers...
    INTLIST *cur_num = NULL;
    int cur_num_len = 0;
    while ((c = getc(fp)) != EOF){
        if(c != '\n' && c != ' '){
            c = c - 48;
            if(cur_num == NULL){
                cur_num = init_intlist(c);
            } else {
                list_append(cur_num, &c);
            }
            cur_num_len++;
        } else if(c == ' ' || c == '\n'){
            // we reached a space, meaning we finished
            // reading a contiguous block of digits
            // now we need to figure out what we actually read...
            int num = 0;
            INTLIST *ptr;
            ptr = cur_num;
            while(cur_num_len != 0){
                cur_num_len--;
                num += pow(10, cur_num_len) * ptr->datum;
                ptr = ptr->next;
            }    

            if(lists[ctr] == NULL){
                // init new list
                lists[ctr] = init_intlist(num);
            } else {
                // append to existing
                list_append(lists[ctr], &num);
            }

            // clear cur_num to read the next one
            cur_num_len = 0;
            list_delete(cur_num);
            cur_num = NULL;
        }

        if(c == '\n') {
            // newline reached - increment to fill in next list
            ctr++;
        }
    }    

    return ctr;
}

導致段錯誤的init_intlists調用因此開始:

    FILE *fp = (FILE *)malloc(sizeof(FILE));
    FILE *base_vector_fp = (FILE *)malloc(sizeof(FILE));

    parse_args(argc, argv, fp, base_vector_fp);

    if(fp == NULL || base_vector_fp == NULL){
        fprintf(stderr, "Critical error, could not load input files\n");
        return 1;
    }

    INTLIST *lines[MAX_LINES] = {};
    INTLIST *base_vectors[MAX_LINES] = {};

    int lines_read = init_intlists(fp, lines);

parse_args看起來像:

FILE *load_file(char *filename) {
    FILE *fp;

    fp = fopen(filename, "r");

    if(fp == NULL){
        fprintf(stderr, "File %s does not seem to exist.\n", filename);
        return NULL;
    }

    // XXX Does this memory leak?
    // fp is never fclose()'d
    return fp;
}

void parse_args(int argc, char *argv[], FILE *fp, FILE *base_vector_fp) {
    char *prog = argv[0];
    if (argc != 3){
        fprintf(stderr, "Wrong number of arguments supplied.\nUse: %s <data_filename>     <base_vector_filename>\n", prog);
        free(fp);
        free(base_vector_fp);
        fp = NULL;
        base_vector_fp = NULL;
        exit(1);
    }

    char *filename = argv[1];
    *fp = *load_file(filename);

    char *base_vector_filename = argv[2];
    *base_vector_fp = *load_file(base_vector_filename);
}

所以,當我嘗試在我的Mac上調用它時,它工作的很好,它讀取文件時,它應該能夠對它進行操作,並為我的任務得到正確的答案。

但是,當我嘗試在Linux上運行它時,它會在init_intlists子例程中嘗試getc時發生段init_intlists

我已經證實,我提供的輸入文件存在並且是世界可讀的(umask 755)。 我已經嘗試過絕對路徑和相對路徑。 我也嘗試了幾個不同的輸入文件。

我已經嘗試在Linux服務器上使用gcc 4.2gcc 3.4 ,並且都會產生一個二進制可執行文件,這會導致任何給定的輸入文件出現段錯誤。

以下是兩個不同版本的gcc之間的版本信息:

Mac OS X:

[email protected] ~> gcc -v
Using built-in specs.
Target: i686-apple-darwin9
Configured with: /var/tmp/gcc/gcc-5465~16/src/configure --disable-checking -enable-werror --prefix=/usr --mandir=/share/man --enable-languages=c,objc,c++,obj-c++ --program-transform-name=/^[cg][^.-]*$/s/$/-4.0/ --with-gxx-include-dir=/include/c++/4.0.0 --with-slibdir=/usr/lib --build=i686-apple-darwin9 --with-arch=apple --with-tune=generic --host=i686-apple-darwin9 --target=i686-apple-darwin9
Thread model: posix
gcc version 4.0.1 (Apple Inc. build 5465)

Linux的:

[email protected]:~/assignment_1$ gcc -v
Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --enable-languages=c,c++,fortran,objc,obj-c++,treelang --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --with-gxx-include-dir=/usr/include/c++/4.2 --program-suffix=-4.2 --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-mpfr --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.2.4 (Ubuntu 4.2.4-1ubuntu4)

我在OS X和Linux上使用相同的Makefile調用編譯器。 gcc的最終調用看起來像這樣:

gcc  -Wall -g  -c src/common_file_io.c src/main.c src/intlist.c
gcc  -Wall -g  common_file_io.o main.o intlist.o -lreadline -lm  -o bin/myprogram 

有任何想法嗎? 我的教授和我的教授完全一樣。


你不應該分配你自己的FILE對象,它們通常是由libc管理的不透明的對象。 不要free()他們,這是由fclose(3) 。 雖然從理論上講,你可以分配一個,做一個結構分配,然後讓它工作,但最好不要去和圖書館作鬥爭,而是像其他人一樣通過參考。 庫可能會或可能不會保持不在FILE結構中的狀態,並且在整個結構中查看或取消引用是一種非常糟糕的風格,執行者可能會認為您從不這樣做。

如果你想返回一個FILE *你可以使用它作為一個返回指針的值,或者使用雙重間接指針: FILE *fp; f(&fp); FILE *fp; f(&fp);

嗯,我只注意到C99實際上在7.19.13中指定了這個:

6用於控制流的FILE對象的地址可能很重要; 一個FILE對象的副本不需要代替原來的東西。

有了這個,他們正在註意到一個FILE *可能真的只是一個魔法餅乾。


你不應該把fopen()的結果復製到一個FILE像中,實際上,你根本就不應該malloc一個FILE對象。 你應該總是使用fopen()來分配FILE控制對象。

FILEFILE不透明的,它實際上包含了很多對於凡人而言隱藏的東西。 這個實現可以自由地放入各種各樣的東西,比如指向其他控制結構的指針等等。