2009-10-27 8 views
2

J'ai un code ANSI C que j'ai développé sur mon Mac, mais quand j'ai essayé de l'exécuter sur les serveurs Linux de notre école, j'ai un erreur de segmentation.ANSI C getc provoque une erreur de segmentation sur Linux mais pas sur OS X

La ligne spécifique qui me cause un problème est getc à partir d'un pointeur de fichier.

Le fichier existe.

est la méthode ici en 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; 
} 

L'appel à init_intlists qui provoque l'erreur de segmentation commence ainsi:

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

et parse_args ressemble:

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

Ainsi, lorsque J'essaie d'invoquer ceci sur mon Mac, ça marche parfaitement bien et ça lit e file comme il se doit et je suis capable de travailler dessus et d'obtenir les bonnes réponses pour mon devoir.

Cependant, lorsque j'essaie de l'exécuter sous Linux, j'obtiens une erreur de segmentation quand elle essaie de getc dans le sous-programme init_intlists.

J'ai vérifié que les fichiers que je fournis pour l'entrée existent et sont lisibles dans le monde entier (umask 755). J'ai essayé avec des chemins absolus et relatifs. J'ai aussi essayé plusieurs fichiers d'entrée différents.

J'ai essayé d'utiliser gcc 4.2 et gcc 3.4 sur le serveur Linux et tous deux produisent un exécutable binaire qui provoquera un segfault avec des fichiers d'entrée donnés.

Voici les informations de version entre les deux versions différentes de gcc:

Mac OS X:

[email protected] ~> 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:

[email protected]:~/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) 

J'invoque le compilateur en utilisant le même Makefile sur OS X et Linux. L'invocation de fin de gcc vents ressemble à ceci:

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 

Toutes les idées? Je suis complètement perdu, tout comme mon professeur.

+0

Conseil amical: Essayez de réduire le texte global, c'est un peu trop à parcourir et vous obtiendrez peut-être moins de réponses. – csl

+0

Pourquoi êtes-vous 'malloc'ing mémoire pour' FILE * 's? – pmg

Répondre

2

Les autres réponses sont correctes - traitez un FILE * comme une poignée opaque que vous copiez, n'essayez pas de copier son contenu. Plus précisément, vous pouvez fixer votre code comme suit:

Retirez l'appel à malloc lorsque vous initialisez fp et base_vector_fp:

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

passer un pointeur sur ces pointeurs pour parse_args, afin qu'il puisse mettre à jour les valeurs de pointeur:

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

et changer parse_args de mettre à jour les FILE * objets dans l'appelant, plutôt que d'essayer de travailler avec le FILE objets:

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

Dur, certaines raisons pointeurs-à-pointeurs ne me venaient pas facilement à l'esprit comme une solution à ce problème. Je savais que je ne devais pas coller manuellement les choses dans le contenu de l'objet FILE, mais j'avais du mal à trouver l'idiome correct. – ashgromnies

11

Vous n'êtes pas censé allouer vos propres objets FILE, ce sont généralement des objets opaques gérés par libc. Ne pas les free() non plus, cela est fait par fclose(3). Bien que théoriquement vous puissiez en attribuer un et faire une assignation de structure et que cela fonctionne, il vaudrait mieux ne pas combattre la bibliothèque et simplement faire circuler la référence comme tout le monde le fait. La bibliothèque peut ou non conserver l'état qui n'est pas dans la structure FILE, et jeter un coup d'œil à l'intérieur ou déréférencer la structure entière est un style suffisamment mauvais pour que les implémenteurs puissent réellement supposer que vous ne le faites jamais.

Si vous souhaitez renvoyer un FILE *, vous pouvez l'utiliser comme pointeur de retour comme dans un cas ou utiliser un pointeur indirect double: FILE *fp; f(&fp);.

Hmm, je viens de remarquer que C99 précise en fait cela dans 19/07/13:

6 L'adresse de l'objet fichier utilisé pour contrôler un flux peut être importante; une copie d'un objet FICHIER n'a pas besoin de servir à la place de l'original .

Avec ceci, ils font remarquer qu'un FILE * peut vraiment être juste un cookie magique.

+0

Pour être plus précis, vous ne travaillez jamais avec 'FILE' - vous ne travaillez qu'avec' FILE * ', et vous obtenez uniquement des valeurs valides pour un appel de' fopen', 'freopen' ou' tmpfile'. –

+1

Etrange que son professeur n'a pas remarqué? –

+1

Bonne réponse! Notez que pour plus de clarté à l'OP, vous pouvez spécifiquement noter que le malloc'ing douteux des pointeurs de dossier pourrait être la cause du segfault. – csl

0

Vous ne devez pas copier le résultat de fopen() dans un objet FILE, et en fait, vous ne devriez pas malloc un objet FILE du tout. Vous devez toujours utiliser fopen() pour allouer l'objet de contrôle FILE.

L'objet FILE est opaque et contient en réalité beaucoup de choses cachées aux simples mortels. L'implémentation est libre d'y mettre toutes sortes de choses, comme des pointeurs vers d'autres structures de contrôle, etc.

Questions connexes