Pregunta

Tengo un código ANSI C que desarrollé en mi Mac, pero cuando traté de ejecutarlo en los servidores Linux de nuestra escuela me da un defecto.

La línea específica que me está causando problemas es un getc de un puntero de archivo.

El archivo existe.

Aquí está el método en cuestión:

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

La llamada a init_intlists que causa la segfault se inicia así:

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

y parse_args se ve así:

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

Entonces, cuando intento invocar esto en mi Mac, funciona perfectamente y lee el archivo como debería y puedo operarlo y obtener las respuestas correctas para mi tarea.

Sin embargo, cuando intento ejecutarlo en Linux, obtengo un defecto cuando intenta getc en la subrutina init_intlists .

He verificado que los archivos que proporciono para la entrada existen y son legibles en todo el mundo (umask 755). He intentado con rutas absolutas y relativas. También he probado varios archivos de entrada diferentes.

He intentado usar gcc 4.2 y gcc 3.4 en el servidor Linux y ambos producen un ejecutable binario que causará una falla por defecto con cualquier archivo de entrada dado.

Aquí está la información de la versión entre las dos versiones diferentes de 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)

Invoco el compilador usando el mismo Makefile tanto en OS X como en Linux. La invocación final de gcc termina así:

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 

¿Alguna idea? Estoy completamente perdido, al igual que mi profesor.

¿Fue útil?

Solución

Las otras respuestas son correctas: trate un FILE * como un asa opaca que copie, no intente copiar su contenido. Específicamente, puede corregir su código de la siguiente manera:

Elimine la llamada a malloc cuando inicialice fp y base_vector_fp :

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

Pase un puntero a estos punteros a parse_args , para que pueda actualizar los valores del puntero:

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

Y cambie parse_args para actualizar los objetos FILE * en la persona que llama, en lugar de intentar trabajar con los objetos 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);
}

Otros consejos

Se supone que no debe asignar sus propios objetos FILE , generalmente son objetos opacos administrados por libc. No los free () tampoco, eso lo hace fclose (3) . Aunque en teoría podría asignar uno y hacer una asignación de estructura y hacer que funcione, sería mejor no luchar contra la biblioteca y simplemente pasar la referencia como lo hacen todos los demás. La biblioteca puede o no mantener un estado que no está en la estructura de ARCHIVO, y mirar dentro o desreferenciar la estructura completa es un estilo suficientemente malo que los implementadores pueden asumir que nunca lo haces.

Si desea devolver un FILE * , puede usarlo como un valor de puntero de retorno como lo hizo en un caso o usar un puntero doble indirecto: FILE * fp; f (& amp; fp); .

Hmm, acabo de notar que C99 realmente especifica esto en 7.19.13 :

  

6 La dirección del objeto FILE utilizado   para controlar una corriente puede ser   significativo; una copia de un objeto FILE   no necesita servir en lugar del   original.

Con esto, están notando que un FILE * puede ser realmente una galleta mágica.

No debe copiar el resultado de fopen () en un objeto FILE , y de hecho, no debe malloc a FILE en absoluto. Siempre debe usar fopen () para asignar el objeto de control FILE .

El objeto FILE es opaco, y en verdad contiene mucho que está oculto a los simples mortales. La implementación es gratuita para incluir todo tipo de cosas, como punteros a otras estructuras de control, etc.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top