ANSI C getcによりLinuxでセグメンテーション違反が発生しますが、OS Xでは発生しません
-
06-07-2019 - |
質問
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.2
および gcc 3.4
を使用してみましたが、両方とも、指定された入力ファイルでセグメンテーション違反を引き起こすバイナリ実行可能ファイルを生成します。
gccの2つの異なるバージョン間のバージョン情報は次のとおりです。
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
アイデアはありますか?教授もそうですが、私は完全に迷っています。
解決
他の答えは正しいです- FILE *
を不透明なハンドルとして扱い、コピーして、その内容をコピーしようとしないでください。具体的には、次のようにコードを修正できます。
fp
および base_vector_fp
を初期化するときの malloc
への呼び出しを削除します:
FILE *fp = NULL;
FILE *base_vector_fp = NULL;
これらのポインターへのポインターを 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);
}
他のヒント
独自の FILE
オブジェクトを割り当てることは想定されていません。これらは通常、libcによって管理される不透明なオブジェクトです。それらも free()
しないでください。これは fclose(3)
によって行われます。理論的には1つを割り当てて構造を割り当てて機能させることはできますが、ライブラリと戦わず、他の人と同じように参照を渡すだけの方が良いでしょう。ライブラリはFILE構造体にない状態を保持する場合と保持しない場合があり、構造体全体を覗き見したり、構造体全体を間接参照したりすることは、実装者が実際にそれを行わないと想定するほど十分に悪いスタイルです。
FILE *
を返す場合は、ある場合と同じようにそれをリターンポインタ値として使用するか、二重間接ポインタを使用できます。 FILE * fp; f(&amp; fp);
。
うーん、C99は実際に 7.19.13 でこれを指定していることに気付きました:
6使用されるFILEオブジェクトのアドレス ストリームを制御するには 重要な; FILEオブジェクトのコピー の代わりに役立つ必要はありません オリジナル。
これにより、彼らは FILE *
が本当に魔法のcookieであるかもしれないという通知を提供しています。
fopen()
の結果を FILE
オブジェクトにコピーしないでください。実際、 malloc
a FILE
オブジェクト。常に fopen()
を使用して、 FILE
コントロールオブジェクトを割り当てる必要があります。
FILE
オブジェクトは不透明であり、実際には単なる人間に隠されているものが多く含まれています。実装では、他の制御構造へのポインターなど、あらゆる種類のものを自由に配置できます。