ANSI C getc segfault on Linux but not OS X

I have ANSI C code that I developed on my Mac, but when I tried to run it on our school Linux servers I got a segfault.

The specific line that is causing me problems is getc

from the file pointer.

The file exists.

This method is:

// 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 init_intlists

causing the segfault starts like this:

    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 this:

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 to use this on my Mac, it works fine and it reads the file as it should and I can work with it and get the correct answers for my assignment.

However, when I try to run it on Linux, I get a segfault when it tries getc

in a subroutine init_intlists

.

I have verified that the files I supply for input exist and are readable in the world (umask 755). I've tried with both absolute and relative paths. I have also tried several different input files.

I tried using gcc 4.2

it gcc 3.4

on a Linux server as well, and both of them execute a binary executable that will segfault with any input files.

Here is the version information between two different gcc versions:

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 am calling the compiler using the same Makefile

for both OS X and Linux. The end call gcc

ends as follows:

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.

+2


source to share


3 answers


The other answers are correct - treat FILE *

as an opaque handle that you are copying, do not try to copy its contents. In particular, you can fix your code like this:

Remove the call malloc

on initialization 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 pointer values:



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

      

And change parse_args

to update the objects FILE *

in the caller, rather than trying to work with the objects 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);
}

      

+2


source


You don't have to allocate your own objects FILE

, they are usually opaque objects managed by libc. Don't do free()

them, it's done with fclose(3)

. While in theory you could single out one and make a struct assignment and make it work, it would be best not to struggle with the library and just navigate to the link like everyone else does. The library may or may not contain state that is not in the FILE structure, and looking inside or dereferencing the entire structure is bad enough that developers might actually assume you never do it.

If you want to return FILE *

, you can use it as the return value of the pointer, as in one case, or use a bidirectional pointer: FILE *fp; f(&fp);

.

Hmm, I just noticed that C99 actually points this to 7.19.13 :



6 The address of the FILE to use for flow control can be significant; a copy of the FILE object should not serve as a substitute for the original.

In doing so, they note that it FILE *

can be just a magic cookie.

+11


source


You shouldn't copy the result fopen()

to an object FILE

, and in fact you shouldn't be malloc

an FILE

object at all. You should always use fopen()

to allocate a control object FILE

.

The object is FILE

opaque, and indeed it contains much that is hidden from mere mortals. The implementation is free to add all sorts of things to it, such as pointers to other control structures, etc.

0


source







All Articles