在尝试将我的程序中的信息保存到.txt文件(或任何文件中)时,我遇到了一些问题。我一直在查看我的代码几次,我似乎无法找到问题。最初我认为可能存在某种形式的内存泄漏(虽然我不知道内存泄漏的后果所以我无法确定)。
我想澄清这是一项学校作业,毕竟我在这里学习,所以不要太容易给我答案!
任务是我们的最后一次,也是我们最大的任务。我们正在创建一个带结构的购物清单。当我尝试使用struct成员将信息保存到.txt文件(如果需要,稍后将它们加载到程序中)时,问题就出现了。我知道这些代码看起来很可怕而且很伤心,但请耐心等待。
这是我的“保存”功能。这是非常基本的,非常可怕。
void saveList(struct GList *grocery)
{
char file[20];
FILE *fp;
printf("What do you want to save the list as? (Don't include file extension): ");
scanf("%s", file);
fp = fopen(strcat(file, ".txt"), "w");
for (int i=0; i<grocery->Items; i++)
{
printf("%s %f %s\n", grocery->list[i].name, grocery->list[i].amount, grocery->list[i].unit);
fprintf(fp, "%s %f %s\n", grocery->list[i].name, grocery->list[i].amount, grocery->list[i].unit);
}
fclose(fp);
}
这是我在程序中输入的内容(添加项目时):
Name of the item: Avocado
Unit: kg
Amount: 10
这是保存到我的.txt文件的内容(它没有显示,但第一行总是包含一些奇怪的符号)。
10.000000 kg
milk 10.000000 litres
同样的问题一直在发生;第一个项目名称(Avocado eg)显示为一些奇怪的符号。
这是我的完整代码,问题可能就在这里。
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
struct Grocery {
char name[20];
char unit[20];
float amount;
};
struct GList {
size_t Items;
struct Grocery *list;
};
int addGrocery();
void printList();
void hQuit();
void inputError();
void removeItem();
void changeItem();
void saveList();
int main()
{
struct GList glist;
glist.Items = 1;
size_t menuChoice = 0;
char cont = 'y';
if((glist.list = malloc(sizeof(glist.list))) == NULL)
return ENOMEM;
puts("Welcome to your Grocery List Manager");
do
{
printf("\n- - - - - - - - - - -\n[1] Add an item\n[2] Print grocery list\n[3] Remove a grocery\n[4] Edit a grocery\n[5] Save your list\n[6] Load a list\n[7] Quit\n\nPlease choose action: ");
if(!scanf("%u", &menuChoice))
return EIO;
putchar('\n');
switch(menuChoice)
{
case 1:
addGrocery(&glist);
break;
case 2:
printList(&glist);
break;
case 3:
removeItem(&glist);
break;
case 4:
changeItem(&glist);
break;
case 5:
saveList(&glist);
break;
case 6:
//Load shopping list
break;
case 7:
hQuit(&glist);
break;
default:
inputError();
break;
}
} while (cont == 'y');
//free(grocery);
return 0;
}
int addGrocery(struct GList *grocery)
{
printf("Name of the grocery: ");
if(!scanf("%s", grocery->list[grocery->Items].name))
return EIO;
printf("Unit: ");
if(!scanf("%s", grocery->list[grocery->Items].unit))
return EIO;
printf("Amount: ");
if(!scanf("%f", &grocery->list[grocery->Items].amount))
return EIO;
printf("You have added %f %s of %s into your list!\n\n", grocery->list[grocery->Items].amount, grocery->list[grocery->Items].unit, grocery->list[grocery->Items].name);
(grocery->Items)++;
grocery->list = realloc(grocery->list, grocery->Items * sizeof(grocery->list));
if(grocery->list == NULL)
return ENOMEM;
return 1;
}
void printList(struct GList *grocery)
{
if ((grocery->Items - 1) > 0)
printf("You have added %d item(s) into your list!\n", grocery->Items - 1);
else
printf("You have no items in your list!\n");
for (int i=1; i<grocery->Items; i++)
{
printf("[%d] %-10s %.1f %s\n", i, grocery->list[i].name, grocery->list[i].amount, grocery->list[i].unit);
}
putchar('\n');
}
void removeItem(struct GList *grocery)
{
size_t index = 0;
printf("Which item would you wish to remove from the list? ");
scanf("%u", &index);
printf("\nYou have removed %s from your grocery list!", grocery->list[index].name);
for (int i=(int)index; i < grocery->Items; i++)
grocery->list[i] = grocery->list[i+1];
(grocery->Items)--;
}
void changeItem(struct GList *grocery)
{
size_t index = 0;
printf("Which item would you like to edit the amount of? ");
scanf("%d", &index);
printf("\nCurrent amount: %.1f %s\nEnter new amount: ", grocery->list[index].amount, grocery->list[index].unit);
scanf("%f", &grocery->list[index].amount);
printf("\nYou changed the amount to %.1f!\n", grocery->list[index].amount);
}
void hQuit(struct GList *grocery)
{
puts("*-*-* Thank you for using the Grocery List! *-*-*");
free(grocery->list);
exit(0);
}
void inputError(struct GList *grocery)
{
puts("No such option. Please try again!\n");
}
void saveList(struct GList *grocery)
{
char file[20];
FILE *fp;
printf("What do you want to save the list as? (Don't include file extension): ");
scanf("%s", file);
fp = fopen(strcat(file, ".txt"), "w");
for (int i=0; i<grocery->Items; i++)
{
printf("%s %f %s\n", grocery->list[i].name, grocery->list[i].amount, grocery->list[i].unit);
fprintf(fp, "%s %f %s\n", grocery->list[i].name, grocery->list[i].amount, grocery->list[i].unit);
}
fclose(fp);
}
如果我的代码在某些地方看起来非常特殊(为什么你有一个void inputError()?)这是因为我们的老师为我们的作业设定了一些非常奇怪的规则。
请随意打破我的代码。
答案 0 :(得分:4)
问问自己,&#34; C是否使用基于0或基于1的数组索引&#34;?
当你调用addGrocery时,你传递了glist的地址,
addGrocery(&glist);
第一次调用addGrocery时glist的第一个/初始值是多少?在添加第一个项目之前,此列表包含多少项?这是&#34;列表&#34;还是&#34;数组&#34;?
以下是主要功能的前几行(回答该问题),
int main()
{
struct GList glist;
glist.Items = 1;
if((glist.list = malloc(sizeof(glist.list))) == NULL)
return ENOMEM;
考虑定义一个函数(构造函数)来创建初始(空)列表。以及一个向列表中添加元素的功能。
您的addGrocery功能会混合输入数据并将数据添加到列表中。考虑一个仅收集输入的函数,然后调用函数将数据添加到列表中。
int addGrocery(struct GList *grocery)
{
printf("Name of the grocery: ");
//what is the value of grocery-Items the first time this is called?
if(!scanf("%s", grocery->list[grocery->Items].name))
return EIO;
//Consider something that creates a grocery list item (does malloc)
//then appends that list item to the list
//then this check would not be needed (well, it would change)
if(grocery->list == NULL)
return ENOMEM;
提示:您是否正在添加第一个列表元素?
但是还有一个更大的问题。您是使用数组还是列表来存储struct Grocery项目?您将列表声明为指针,并在main中初始化它。您是否分配了一些包含多个项目的数组,或者您想要一个项目列表? struct Grocery类型没有指针,所以你可能不想要一个&#34;列表&#34;,而是一个&#34;数组&#34; (命名很重要)。
struct GList {
size_t Items;
struct Grocery *list;
};
因为你的addGrocery函数使用数组索引,假设你想要一个Grocery项目数组,但是你创建了多少个?你在说哪一个?
(这些问题应该指向正确的方向)
答案 1 :(得分:2)
首先,我确信你的老师会多次告诉你不要使用魔术数字:
char file[PATH_MAX];
为了您的程序未来的计算合理性,您可能希望避免溢出此缓冲区:
if (snprintf(NULL, 0, "%s.txt", file) >= PATH_MAX - 1) {
fputs("Filename too long!", stderr);
exit(EXIT_FAILURE);
}
if (scanf("%s", grocery->list[grocery->Items].name) == 1)
在您阅读the scanf
manual(这是我们作为软件开发人员的工作的一部分)之前,您不会错误地使用scanf
。事实上,从粗略的一瞥可能看起来你做错了什么也不是很明显。
事实上,作为软件开发人员,我们不仅必须仔细阅读其他人编写的手册,错误消息,代码(这可能不会很好地反映质量差的评论)。
检查scanf
是否返回0是确定是否读取0个元素的好方法,但不是确定是否发生EOF
或其他文件访问错误的好方法。
你能解释为什么我(正确)与1比较吗?如果您想使用与stdin
的单一比较从scanf
读取两个值,您应该将返回值与?
void *temp = realloc(grocery->list, grocery->Items * sizeof *grocery->list);
if (temp == NULL)
return ENOMEM;
grocery->list = temp;
你不会知道你正在使用realloc
incorre ...阅读the realloc
manual yada ... yada ......等等等等
我在这里做了另一个修改:你错过了一个星号! D'oh!看看你是否能发现它:)
事实上,作为软件开发人员,我们不仅必须仔细阅读其他人编写的手册,错误消息,代码(这可能不会很好地反映质量差的评论)。
因此,我们必须从手册中做一些推论,以确定在覆盖它之前realloc
何时可能不free
旧指针(从而导致内存泄漏)。
你能解决为什么我使用temp
orary变量(你应该如此)?
同样,你错过了另一个星号:
if((glist.list = malloc(sizeof glist.list[0])) == NULL)
抱歉,我忍不住让它变得更加明显......你应该记下这些模式:
pointer = malloc(count * sizeof *pointer); // -- the `malloc` pattern; pointer could be NULL, so needs checking
void *temp = realloc(pointer, count * sizeof *pointer); // -- the `realloc` pattern; temp could be NULL (in which case you need to deal with `pointer`)
请记住这两种模式,你不应该再发现自己犯这些错误。
虽然我们讨论的是列表主题,但空列表包含0个项目,对吗?
glist.Items = 0;
P.S。你听说过valgrind
吗?...