根据您在阅读水果名称时希望如何提供存储(内存)来保存水果名称,您有 2 个选项 (2) 使用鲜为人知的 'm' 字段修饰符(在较旧的实现上是 'a',所以如果您正在使用windoze,请阅读文档以做出决定(或者同时尝试两者,看看哪个有效)。
您的代码的直接问题是fopen 是使用"w" 文件模式调用的。仅当 fruits.txt 已经显示 作为 "w" 模式不会创建不存在的文件时,这才有效。正确的mode 是"w+"(或"a" 或"a+",其中任何一个都会创建一个不存在的文件)。只需将您的模式更改为"w+",即可将水果信息写入新创建的fruits.txt。
有关在某些 MS 编译器上使用 VLA(可变长度数组)的问题可能会出现问题。您只需要查看您的版本和更改日志或文档(或者只是尝试阅读错误或警告。更糟糕的情况,您可以使用 pointer-to-pointer-to-type,或者一个足够大的静态数组。鉴于您使用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 * 的pointer-to-pointer -- 这是 地址分配/文件的指针,例如&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'.\n\n", 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] : %s\n", 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标记行。花点时间消化代码并为自己回答您必须查找的两个编译器问题(1)VLA 对fruits[count] 的支持和(2)您的编译器是否使用m 或a 来分配。