ANSI C getc 在 Linux 上导致段错误,但在 OS X 上不会

发布于 2024-08-09 01:30:04 字数 5082 浏览 1 评论 0原文

我有一些在 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 

有什么想法吗?我完全不知所措,我的教授也是如此。

I have some ANSI C code that I developed on my Mac, but when I tried running it on our school's Linux servers I get a segfault.

The specific line that is causing me trouble is a getc from a file pointer.

The file does exist.

Here is the method in question:

// 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;
}

The call to init_intlists that causes the segfault starts thusly:

    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);

and parse_args looks like:

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);
}

So when I try invoking this on my Mac, it works perfectly fine and it reads the file in like it should and I'm able to operate on it and get the correct answers for my assignment.

However, when I try running it on Linux, I get a segfault when it tries to getc in the init_intlists subroutine.

I have verified that the files I supply for input exist and are world-readable(umask 755). I have tried with both absolute and relative paths. I have tried several different input files as well.

I have tried using gcc 4.2 and gcc 3.4 on the Linux server and both produce a binary executable that will cause a segfault with any given input files.

Here is the version information between the two different versions of 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)

I invoke the compiler using the same Makefile on both OS X and Linux. The end invocation of gcc winds up looking like this:

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 

Any ideas? I am at a complete loss, as is my professor.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

猛虎独行 2024-08-16 01:30:04

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

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

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

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

通过这个,他们注意到 FILE * 可能真的只是一个神奇的饼干。

You aren't supposed to allocate your own FILE objects, they are generally opaque objects managed by libc. Don't free() them either, that's done by fclose(3). Although theoretically you could allocate one and do a structure assign and have it work, it would be better not to fight the library and just pass around the reference like everyone else does. The library may or may not keep state that isn't in the FILE structure, and peeking inside or dereferencing the entire struct is sufficiently bad style that the implementors may actually assume you never do it.

If you want to return a FILE * you can either use it as a return pointer value as you did in one case or use a double-indirect pointer: FILE *fp; f(&fp);.

Hmm, I just noticed that C99 actually specifies this in 7.19.13:

6 The address of the FILE object used
to control a stream may be
significant; a copy of a FILE object
need not serve in place of the
original.

With this they are serving notice that a FILE * may really just be a magic cookie.

你げ笑在眉眼 2024-08-16 01:30:04

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

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

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

将指向这些指针的指针传递给 < code>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);
}

The other answers are correct - treat a FILE * as an opaque handle that you copy around, don't try to copy its contents. Specifically you can fix your code as follows:

Remove the call to malloc when you initialise fp and base_vector_fp:

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

Pass a pointer to these pointers to parse_args, so that it can update the pointer values:

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

And change parse_args to update the FILE * objects in the caller, rather than try and work with the FILE objects:

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);
}
意中人 2024-08-16 01:30:04

您不应该将fopen()的结果复制到FILE对象中,事实上,您不应该mallocaFILE< /code>根本就是对象。您应该始终使用fopen()来分配FILE控制对象。

FILE对象是不透明的,而且它确实包含了很多对于普通人来说是隐藏的东西。该实现可以自由地将各种东西放入其中,例如指向其他控制结构的指针等。

You should not copy the result offopen()into aFILEobject, and in fact, you should notmallocaFILEobject at all. You should always usefopen()to allocate theFILEcontrol object.

TheFILEobject is opaque, and verily it contains much that is hidden to mere mortals. The implementation is free to put all sorts of things in it, such as pointers to other control structures, etc.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文