"fruits.txt"是一个文本文件,它以一个数字(比如n)开头,后跟n个水果的名称。我想将这n个名称存储到一个字符串数组中,但在尝试声明该数组时,我收到了"Segmentation fault(core dumped)error"
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp;
int count;
fp = fopen("fruits.txt", "r");
if(fp == NULL)
{
printf("Can't open file!!");
exit(0);
}
fscanf(fp, "%d", &count);
printf("%dn", count);
char *fruits[count]; // This line is giving Segmentation fault.
fclose(fp);
return 0;
}
根据您希望如何在读取水果名称时提供存储(内存)来保存水果名称,您有两个选项(2)使用鲜为人知的'm'
字段修饰符(在旧的实现中是'a'
,所以如果您在windoze上,请阅读文档以做出决定(或者同时尝试两者,看看哪一个有效)。
代码的直接问题是使用"w"
文件模式调用fopen
。只有当fruits.txt
已经出现时,这才会起作用,因为如果"w"
模式不存在,它将不会创建文件。正确的模式是"w+"
(或"a"
或"a+"
,其中任何一个也将创建一个不存在的文件)。只需将模式更改为"w+"
,即可将水果信息写入新创建的fruits.txt
。
在某些MS编译器上使用VLA(可变长度数组)的问题可能会带来问题。你只需要查看你的版本、变更日志或文档(或者只是尝试阅读错误或警告。更糟糕的情况是,你可以使用指针到类型的指针,或者足够大的静态数组。考虑到你使用fscanf
,这种方法在下面继续
char *fruits[count]; /* here you delare an array of pointers to type char */
/* utilizing a variable length array. some MS compiler */
/* version do not handle VLA's, VS13/VS15 should be ok */
for (i = 0; i < count; i++) /* read & allocate for each array element */
if (fscanf (fp, " %ms", &fruits[i]) != 1) { /* m allocates, a for ms */
fprintf (stderr, "error: read/allocation for fruits[%d] failed.n", i);
exit (EXIT_FAILURE);
}
(注意" %ms"
上面使用的格式字符串,它有一个警告,即接受分配块的指针必须是指向char *
的指针,这是要分配/文件的指针的<em]地址,例如&fruits[i]
)>
代码的其余部分应该非常熟悉,只是现在代码中的所有输入点和其他关键点都已通过检查任何函数提供的return
进行了验证,以确保在该点之前不存在任何错误情况,也不存在由所讨论的操作引起的错误情况。这是您对代码操作有信心的唯一方法。养成这个习惯。
综合起来,你可以得出以下结论。
#include <stdio.h>
#include <stdlib.h>
int main (void) {
int count, i;
FILE *fp;
if (!(fp = fopen ("fruits.txt", "w+"))) { /* "w+" required to create file */
fprintf (stderr, "error: file open failed 'fruits.txt'.n");
exit (EXIT_FAILURE);
}
fputs ("4 Apple Banana mango berry", fp); /* write string to fruits.txt */
fclose(fp);
if (!(fp = fopen ("fruits.txt", "r"))) { /* validate file open for reading */
fprintf (stderr, "error: file open failed 'fruits.txt'.n");
exit (EXIT_FAILURE);
}
if (fscanf (fp, " %d", &count) != 1) { /* read all fruir from fruits.txt */
fprintf (stderr, "error: in read of value from 'fruits.txt'.n");
exit (EXIT_FAILURE);
}
printf ("n Quantity read from 'fruits.txt' is '%d'.nn", count);
char *fruits[count]; /* here you delare an array of pointers to type char */
/* utilizing a variable length array. some MS compiler */
/* version do not handle VLA's, VS13/VS15 should be ok */
for (i = 0; i < count; i++) /* read & allocate for each array element */
if (fscanf (fp, " %ms", &fruits[i]) != 1) { /* m allocates, a for ms */
fprintf (stderr, "error: read/allocation for fruits[%d] failed.n", i);
exit (EXIT_FAILURE);
}
fclose(fp); /* close file for the last time */
for (i = 0; i < count; i++) /* output array */
printf (" fruit[%d] : %sn", i, fruits[i]);
for (i = 0; i < count; i++) /* free allocated memory */
free (fruits[i]);
return 0;
}
代码(在abc.c
中)和放置在bin/abc
中的可执行文件的基本编译字符串可以是:
$ gcc -Wall -Wextra -o bin/abc abc.c -std=gnu11
示例使用/输出
$ ./bin/abc
Quantity read from 'fruits.txt' is '4'.
fruit[0] : Apple
fruit[1] : Banana
fruit[2] : mango
fruit[3] : berry
在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您都有2个责任:(1)始终为内存块保留一个指向起始地址的指针,因此,(2)当不再需要时,它可以被释放。
您必须使用内存错误检查程序来确保您没有在分配的内存块之外写入,没有试图读取或基于未初始化的值进行跳转,最后确认您已经释放了所有分配的内存。对于Linux,valgrind
是正常的选择。
$ valgrind ./bin/abc
==30980== Memcheck, a memory error detector
==30980== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==30980== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==30980== Command: ./bin/abc
==30980==
Quantity read from 'fruits.txt' is '4'.
fruit[0] : Apple
fruit[1] : Banana
fruit[2] : mango
fruit[3] : berry
==30980==
==30980== HEAP SUMMARY:
==30980== in use at exit: 0 bytes in 0 blocks
==30980== total heap usage: 10 allocs, 10 frees, 1,561 bytes allocated
==30980==
==30980== All heap blocks were freed -- no leaks are possible
==30980==
==30980== For counts of detected and suppressed errors, rerun with: -v
==30980== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
始终确认所有堆块都已释放--不可能发生泄漏,同样重要的错误摘要:0个上下文中有0个错误。(尽管注意:一些操作系统没有提供足够的内存排除文件(将系统和操作系统内存排除在使用中的文件),这将导致valgrind
报告一些内存尚未释放(尽管您已经完成了工作并释放了分配并在您控制下的所有块)。
对于一个简单的问题来说,这是一个很大的问题,我们甚至没有讨论读取文件的首选方式,即用fgets
或POSIX getline
一次读取整行,然后解析读取行中的单个水果,或者用strtok
标记行。花点时间消化cocde,并自己回答您必须查找的两个编译器问题:(1)VLA对fruits[count]
的支持;(2)编译器使用m
还是a
进行分配。
使用fscanf
时,必须始终检查返回值。只有当函数返回一个成功的值(请阅读fscanf
文档)时,程序才能继续。
我已经尝试了下面的代码,并发现可以工作。这里的区别在于,首先我创建了文件并填充了其中的内容
若我并没有在文件中填充内容,那个么我就会出现分段错误(并不是所有编译器)。因此,在您的情况下,fscanf()似乎正在读取一些垃圾邮件并返回一些大的垃圾邮件编号。正如其他人所建议的,请检查fscanf返回的内容。如果返回-1,则表示在您的文件中找不到任何内容。
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp;
int count;
int i;
fp = fopen ("fruits.txt", "w");
if(fp == NULL)
{
printf("Can't open file!!");
exit(0);
}
fputs("4 Apple Banana mango berry", fp);
fclose(fp);*/
fp = fopen("fruits.txt", "r");
if(fp == NULL)
{
printf("Can't open file!!");
exit(0);
}
fscanf(fp, "%d", &count);
printf("%dn", count);
fclose(fp);
char *fruits[count]; // This line is giving Segmentation fault.
return 0;
}
这将打印4,没有错误/故障。