【问题标题】:ANSI C getc causes segfault on Linux but not OS XANSI C getc 在 Linux 但不是 OS X 上导致段错误
【发布时间】:2009-10-27 22:17:27
【问题描述】:

我有一些在我的 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 时出现段错误。

我已验证我为输入提供的文件存在并且是世界可读的 (umask 755)。我尝试过使用绝对路径和相对路径。我也尝试了几种不同的输入文件。

我曾尝试在 Linux 服务器上使用 gcc 4.2gcc 3.4 并生成二进制可执行文件,这将导致任何给定输入文件出现段错误。

这里是两个不同版本的gcc之间的版本信息:

Mac OS X:

me@dinosaurhunter ~> 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:

me@janus:~/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 

有什么想法吗?我和我的教授一样完全不知所措。

【问题讨论】:

  • 友情提示:尽量减少整体文字,略读有点过多,可能会得到更少的答案。
  • 你为什么要 mallocing 内存 FILE *s?

标签: c file-io malloc segmentation-fault readline


【解决方案1】:

您不应该分配自己的 FILE 对象,它们通常是由 libc 管理的不透明对象。也不要free() 他们,这是由fclose(3) 完成的。虽然理论上你可以分配一个并进行结构分配并让它工作,但最好不要与图书馆争吵,而像其他人一样传递参考。库可能会或可能不会保留不在 FILE 结构中的状态,并且在内部窥视或取消引用整个结构是非常糟糕的风格,实现者实际上可能会认为您从不这样做。

如果您想返回 FILE *,您可以像在一种情况下那样将其用作返回指针值,也可以使用双间接指针:FILE *fp; f(&amp;fp);

嗯,我刚刚注意到 C99 实际上在 7.19.13 中指定了这一点:

6 使用的FILE对象的地址 控制流可能是 重大; FILE 对象的副本 不需要代替 原创。

因此,他们通知FILE * 可能真的只是一个神奇的cookie。

【讨论】:

  • 更具体地说,您从不使用FILE - 您只使用FILE*,并且您只能通过调用fopenfreopen 之一获得有效值, 或tmpfile.
  • 好答案!请注意,为了澄清 OP,您可能会特别指出文件指针的可疑 malloc'ing 可能是段错误的原因。
  • “虽然理论上你可以分配一个并进行结构分配并让它工作” - 我不确定这是否可以保证是可行的。指针是否保证是指向FILE* 的有效指针(而不仅仅是库来回转换的句柄)?即使它是一个有效的指针,是否可以保证FILE 是一个完全定义的类型(而不仅仅是typedef struct FILE FILE)?
  • @PM:C99 似乎没有回答这些问题,但我当然同意你的看法;除了 OO 风格的操作之外,使用参考做任何事情都是个坏主意。
【解决方案2】:

其他答案是正确的 - 将 FILE * 视为您复制的不透明句柄,不要尝试复制其内容。具体来说,您可以按如下方式修复您的代码:

在初始化fpbase_vector_fp 时删除对malloc 的调用:

FILE *fp = NULL;
FILE *base_vector_fp = NULL;

将指向这些指针的指针传递给parse_args,以便它可以更新指针值:

parse_args(argc, argv, &fp, &base_vector_fp);

并更改parse_args 以更新调用者中的FILE * 对象,而不是尝试使用FILE 对象:

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);
        exit(1);
    }

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

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

【讨论】:

  • Dur,出于某种原因,我并没有想到将指针指向指针作为解决此问题的方法。我知道我不应该手动将内容粘贴到 FILE 对象的内容中,但在找出正确的习语时遇到了麻烦。
【解决方案3】:

你不应该将fopen()的结果复制到FILEobject,事实上,你根本不应该mallocaFILEobject。您应该始终使用fopen()来分配FILE控制对象。

FILEobject 是不透明的,它确实包含许多对凡人隐藏的东西。实现中可以随意放各种东西,比如指向其他控制结构的指针等。

【讨论】:

    猜你喜欢
    • 2011-02-05
    • 2015-10-05
    • 1970-01-01
    • 1970-01-01
    • 2016-03-22
    • 1970-01-01
    • 1970-01-01
    • 2014-05-08
    • 1970-01-01
    相关资源
    最近更新 更多