我有一些我在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上运行它时,我在getc
子例程中尝试init_intlists
时会遇到段错误。
我已经确认我提供的输入文件存在并且是世界可读的(umask 755)。我尝试过绝对路径和相对路径。我也尝试了几种不同的输入文件。
我尝试在Linux服务器上使用gcc 4.2
和gcc 3.4
,并且都生成了一个二进制可执行文件,它会导致任何给定输入文件的段错误。
以下是两个不同版本的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)
我在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
有什么想法吗?我和教授一样,完全失去了。
答案 0 :(得分:10)
您不应该分配自己的FILE
个对象,它们通常是由libc管理的不透明对象。不要free()
他们,fclose(3)
完成。虽然从理论上讲你可以分配一个并进行结构分配并使其工作,但最好不要对抗图书馆而只是像其他人一样传递参考文献。库可能会或可能不会保留不在FILE结构中的状态,并且在内部窥视或取消引用整个结构是足够糟糕的样式,实现者实际上可能认为你永远不会这样做。
如果要返回FILE *
,可以像在一种情况下一样使用它作为返回指针值,或者使用双间接指针:FILE *fp; f(&fp);
。
嗯,我刚注意到C99实际上在 7.19.13 中指定了这个:
6使用的FILE对象的地址 控制流可能是 重大; FILE对象的副本 不需要代替 原始
有了这个,他们发出通知,FILE *
可能真的只是一个神奇的饼干。
答案 1 :(得分:2)
其他答案是正确的 - 将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);
}
答案 2 :(得分:0)
您不应将fopen()
的结果复制到FILE
对象中,事实上,您根本不应该malloc
FILE
个对象。您应该始终使用fopen()
来分配FILE
控件对象。
FILE
对象是不透明的,实际上它包含了许多对凡人来说隐藏的东西。实现可以自由地放入各种各样的东西,比如指向其他控制结构的指针等等。