fscanf() 代码并不总是会导致内存崩溃,但有时
fscanf() code does not always crash memory, but sometimes
=========================
代码(文件名为test.c)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(int argc, char * argv[] ) {
int i=0, num=atoi(argv[1]);
char **arr=(char**)malloc(num*sizeof(char*));
FILE *fp = fopen("test.txt","r");
if( arr == NULL) { printf("out of memory\n"); exit(1); }
if( fp == NULL) { printf("cannot open the file \n"); exit(1); }
for(i=0; i< num; i++) fscanf(fp,"%s", arr+i ); // HERE
printf("%s\n", arr+num-1 );
fclose(fp);
free(arr);
return 0;
}
========
test.txt
watermelon
grape
strawberries
orange
peach
banana
mango
cherry
pineapple
apple
blueberry
raspberry
pear
melon
greengrapes
tangerine
kiwifruit
pomegranate
plum
nectarine
========
问题
当我在下面执行了几次
test 1
test 2
...
...
test 7
test 8
它经常压碎类似 "core dump" 的东西,但按我的预期工作。
然而,当我输入高于 9 时,它永远不会崩溃...
test 9
test 10
...
是什么导致此代码崩溃?
fscanf
正在尝试将数据写入您尚未分配的 *arr[i]
。你只分配了 arr[i]
(你也没有初始化)。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(int argc, const char * argv[])
{
int i = 0, num = atoi(argv[1]);
char** arr = (char**)malloc(num * sizeof(char*));
for (int j = 0; j < num; j++) {
arr[j] = (char*)malloc(100 * sizeof(char));
}
FILE* fp = fopen("test.txt", "r");
if (arr == NULL) {
printf("out of memory\n");
exit(1);
}
if (fp == NULL) {
printf("cannot open the file\n");
exit(1);
}
for (; i < num; i++) {
fscanf(fp, "%s", arr[i]);
printf("%s\n", arr[i]);
}
for (int k = 0; k < num; k++) {
free(arr[i]);
}
free(arr);
return 0;
}
对于 fscanf(fp, "%s", arr[i]);
,您需要为每个 arr[i]
.
分配内存
问题是,您只为指针分配了 space,而没有为字符串本身分配。如果您正在为 POSIX.1-2008 兼容平台或使用足够新的 glibc(例如最近的 Linux , 可能还有 MinGW):
您可以使用 a
说明符进行 %s
转换(在 scanf
man page 阅读更多内容),这会导致 scanf
为字符串分配内存(调用者是负责在 free()
上调用),所以对于你的代码:
// No need to cast return value of malloc or calloc.
// Optional: switched to calloc, which has overhead of zeroing memory,
// but also makes immediate segfault more likely on some bugs.
char **arr = calloc(num, sizeof(char*));
//...
for(i=0; i < num; i++) {
int status = fscanf(fp,"%ms", arr[i] );
assert(status==1); // really simple error checking added
}
如前所述,完成后,您应该释放分配的内存:
for(i=0; i < num; i++) {
free(arr[i]);
}
free(arr);
好处是,你不必担心缓冲区溢出(如果文件大于可用虚拟内存,那么你就会遇到麻烦,除非你在 a
说明符中添加限制,请阅读手册页以获取详细信息...)并且您不会因为为较短的字符串分配太多 space 而浪费任何内存。缺点是 a
说明符不是由 C 标准定义的,所以它会降低你的代码到 GNU 和 POSIX 平台的可移植性,并可能导致你的老师拒绝它(如果它是课程作业),以防万一。
=========================
代码(文件名为test.c)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(int argc, char * argv[] ) {
int i=0, num=atoi(argv[1]);
char **arr=(char**)malloc(num*sizeof(char*));
FILE *fp = fopen("test.txt","r");
if( arr == NULL) { printf("out of memory\n"); exit(1); }
if( fp == NULL) { printf("cannot open the file \n"); exit(1); }
for(i=0; i< num; i++) fscanf(fp,"%s", arr+i ); // HERE
printf("%s\n", arr+num-1 );
fclose(fp);
free(arr);
return 0;
}
========
test.txt
watermelon
grape
strawberries
orange
peach
banana
mango
cherry
pineapple
apple
blueberry
raspberry
pear
melon
greengrapes
tangerine
kiwifruit
pomegranate
plum
nectarine
========
问题
当我在下面执行了几次
test 1
test 2
...
...
test 7
test 8
它经常压碎类似 "core dump" 的东西,但按我的预期工作。
然而,当我输入高于 9 时,它永远不会崩溃...
test 9
test 10
...
是什么导致此代码崩溃?
fscanf
正在尝试将数据写入您尚未分配的 *arr[i]
。你只分配了 arr[i]
(你也没有初始化)。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(int argc, const char * argv[])
{
int i = 0, num = atoi(argv[1]);
char** arr = (char**)malloc(num * sizeof(char*));
for (int j = 0; j < num; j++) {
arr[j] = (char*)malloc(100 * sizeof(char));
}
FILE* fp = fopen("test.txt", "r");
if (arr == NULL) {
printf("out of memory\n");
exit(1);
}
if (fp == NULL) {
printf("cannot open the file\n");
exit(1);
}
for (; i < num; i++) {
fscanf(fp, "%s", arr[i]);
printf("%s\n", arr[i]);
}
for (int k = 0; k < num; k++) {
free(arr[i]);
}
free(arr);
return 0;
}
对于 fscanf(fp, "%s", arr[i]);
,您需要为每个 arr[i]
.
问题是,您只为指针分配了 space,而没有为字符串本身分配。如果您正在为 POSIX.1-2008 兼容平台或使用足够新的 glibc(例如最近的 Linux , 可能还有 MinGW):
您可以使用 a
说明符进行 %s
转换(在 scanf
man page 阅读更多内容),这会导致 scanf
为字符串分配内存(调用者是负责在 free()
上调用),所以对于你的代码:
// No need to cast return value of malloc or calloc.
// Optional: switched to calloc, which has overhead of zeroing memory,
// but also makes immediate segfault more likely on some bugs.
char **arr = calloc(num, sizeof(char*));
//...
for(i=0; i < num; i++) {
int status = fscanf(fp,"%ms", arr[i] );
assert(status==1); // really simple error checking added
}
如前所述,完成后,您应该释放分配的内存:
for(i=0; i < num; i++) {
free(arr[i]);
}
free(arr);
好处是,你不必担心缓冲区溢出(如果文件大于可用虚拟内存,那么你就会遇到麻烦,除非你在 a
说明符中添加限制,请阅读手册页以获取详细信息...)并且您不会因为为较短的字符串分配太多 space 而浪费任何内存。缺点是 a
说明符不是由 C 标准定义的,所以它会降低你的代码到 GNU 和 POSIX 平台的可移植性,并可能导致你的老师拒绝它(如果它是课程作业),以防万一。