ANSI C getc يسبب segfault على لينكس ولكن ليس OS X
-
06-07-2019 - |
سؤال
ولدي بعض رمز ANSI C أنني وضعت على بلدي ماك، ولكن عندما حاولت تشغيله على خوادم لينكس مدرستنا أحصل على segfault.
وخط معين الذي يسبب لي مشكلة هو 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
الذي يسبب segfault تبدأ هكذا:
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);
}
وهكذا عندما أحاول استدعاء هذا على بلدي ماك، وأنها تعمل على ما يرام تماما، ويقرأ الملف في كما ينبغي وأنا قادرة على العمل على ذلك والحصول على الإجابات الصحيحة لمهمتي.
ولكن عندما أحاول تشغيله على لينكس، وأحصل على segfault عندما كان يحاول getc
في روتين init_intlists
.
ولقد التحقق من أن الملفات I تزويد لإدخال موجودة ويمكن قراءتها في العالم (umask 755). لقد حاولت مع كل مسارات المطلقة والنسبية. لقد حاولت عدة ملفات الإدخال المختلفة كذلك.
ولقد حاولت استخدام gcc 4.2
وgcc 3.4
على خادم لينكس وكلاهما إنتاج قابل للتنفيذ ثنائي من شأنها أن تتسبب في segfault مع أي ملفات مدخلات معينة.
وهنا هي معلومات إصدار بين اثنين من إصدارات مختلفة من دول مجلس التعاون الخليجي:
وماك 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)
ولينكس:
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)
وأنا استدعاء مترجم باستخدام نفس Makefile
على حد سواء OS X و Linux. احتجاج نهاية 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 *
كما مقبض مبهمة الذي قمت بنسخ حولها، لا تحاول نسخ محتوياته. على وجه التحديد يمكنك إصلاح التعليمات البرمجية كما يلي:
وإزالة الدعوة إلى malloc
عند التهيئة fp
وbase_vector_fp
:
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)
. وعلى الرغم من الناحية النظرية يمكن تخصيص واحد والقيام تعيين هيكل ويكون ذلك العمل، سيكون من الأفضل عدم محاربة مكتبة ويمر حول مرجعية مثل أي شخص آخر لا. المكتبة قد أو قد لا تبقي على حالة غير موجود في بنية FILE، وتطل على داخل أو dereferencing البنية بكاملها هو نمط سيئة بما فيه الكفاية أن المنفذين قد تحمل في الواقع كنت لم تفعل ذلك.
إذا كنت ترغب في إرجاع FILE *
يمكنك إما استخدامه بمثابة قيمة الإرجاع مؤشر كما فعلت في حالة واحدة أو استخدام مزدوج غير مباشرة المؤشر: FILE *fp; f(&fp);
وهم، أنا فقط لاحظت أن C99 يحدد هذا الواقع في على 7.19.13 : ل
<اقتباس فقرة>و6 عنوان الكائن FILE المستخدمة للسيطرة على تيار قد يكون كبير؛ نسخة من الكائن FILE ليس من الضروري أن تكون في مكان الأصلي.
اقتباس فقرة> ومع هذا أنهم يخدمون اشعار بأن FILE *
قد حقا أن يكون مجرد الكعكة السحرية.
ويجب أن لا نسخ نتيجة offopen()
into aFILE
object، وفي الواقع، يجب أن notmalloc
aFILE
object على الإطلاق. يجب عليك دائما usefopen()
to تخصيص theFILE
control الكائن.
وTheFILE
object غير مبهمة، والحق أنه يحتوي على الكثير مما لا يخفى على البشر الفانين. تنفيذ حر في وضع جميع أنواع الأشياء فيه، مثل مؤشرات إلى هياكل المراقبة الأخرى، وما إلى ذلك.