Domanda

Ho del codice ANSI C che ho sviluppato sul mio Mac, ma quando ho provato a eseguirlo sui server Linux della nostra scuola ho ricevuto un segfault.

La riga specifica che mi sta causando problemi è un getc da un puntatore a file.

Il file esiste.

Ecco il metodo in questione:

// 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 chiamata a init_intlists che causa il segfault inizia così:

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

e parse_args sembra:

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

Quindi, quando provo a invocare questo sul mio Mac, funziona perfettamente e legge il file come dovrebbe e sono in grado di operare su di esso e ottenere le risposte corrette per il mio incarico.

Tuttavia, quando provo a eseguirlo su Linux, ottengo un segfault quando tenta di getc nella subroutine init_intlists .

Ho verificato che i file forniti per l'input esistono e sono leggibili in tutto il mondo (umask 755). Ho provato con percorsi sia assoluti che relativi. Ho provato anche diversi file di input.

Ho provato a usare gcc 4.2 e gcc 3.4 sul server Linux ed entrambi producono un eseguibile binario che causerà un segfault con qualsiasi dato file di input.

Ecco le informazioni sulla versione tra le due diverse versioni di 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 il compilatore usando lo stesso Makefile sia su OS X che su Linux. L'invocazione finale di gcc finisce così:

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 

Qualche idea? Sono in completa perdita, come lo è il mio professore.

È stato utile?

Soluzione

Le altre risposte sono corrette: tratta un FILE * come un handle opaco che copi, non tentare di copiarne il contenuto. In particolare puoi correggere il tuo codice come segue:

Rimuovi la chiamata a malloc quando inizializzi fp e base_vector_fp :

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

Passa un puntatore a questi puntatori su parse_args , in modo che possa aggiornare i valori del puntatore:

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

E cambia parse_args per aggiornare gli oggetti FILE * nel chiamante, piuttosto che provare a lavorare con gli oggetti 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);
}

Altri suggerimenti

Non dovresti allocare i tuoi oggetti FILE , sono generalmente oggetti opachi gestiti da libc. Non free () , fatto da fclose (3) . Anche se teoricamente potresti assegnarne uno e assegnare una struttura e farlo funzionare, sarebbe meglio non combattere la libreria e passare semplicemente il riferimento come fanno tutti gli altri. La libreria può mantenere o meno uno stato che non si trova nella struttura FILE e sbirciare all'interno o dereferenziare l'intera struttura è uno stile sufficientemente cattivo che gli implementatori possono effettivamente supporre che tu non lo faccia mai.

Se si desidera restituire un FILE * , è possibile utilizzarlo come valore del puntatore di ritorno come in un caso oppure utilizzare un puntatore doppio indiretto: FILE * fp; f (& amp; fp);.

Hmm, ho appena notato che C99 lo specifica in 7.19.13 :

  

6 L'indirizzo dell'oggetto FILE utilizzato   per controllare un flusso può essere   significativa; una copia di un oggetto FILE   non è necessario servire al posto del   originale.

Con questo stanno notando che un FILE * potrebbe davvero essere solo un biscotto magico.

Non dovresti copiare il risultato di fopen () in un oggetto FILE , e in effetti non dovresti malloc a FILE per niente. Dovresti sempre usare fopen () per allocare l'oggetto di controllo FILE .

L'oggetto FILE è opaco e in verità contiene molto che è nascosto ai semplici mortali. L'implementazione è libera di inserire ogni sorta di cose, come puntatori ad altre strutture di controllo, ecc.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top