【发布时间】:2018-06-20 06:10:54
【问题描述】:
我在尝试将信息从我的程序保存到 .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
同样的问题总是出现;第一个项目名称(例如鳄梨)显示为一些奇怪的符号。
这是我的完整代码,问题可能出在此处。
#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()?),那是因为我们的老师为我们的作业设置了一些非常奇怪的规则。
请随意抨击我的代码。
【问题讨论】:
-
第一个明显的问题:glist.list 显然是一个指向杂货对象的指针数组,但您只是为一个对象分配内存。第二: addGrocery 假设存在一个杂货项目,但你第一次调用它时显然没有创建任何杂货项目,所以你的 scanf 正在进入随机内存。事实上,我没有看到你在任何地方为任何杂货分配内存。
-
@LeeDanielCrocker 我们的老师:创建一个指针,您将为其分配内存(作为数组工作)。当您向列表中添加新项目时,您将沿途扩展所需的内存 (realloc())。我不明白 addGrocery() 如何假设内存中已经有一个项目?我错过了什么吗?
-
一如既往,
T*p=[m/c/re]alloc(sizeof T*)是错误的。 -
指针列表是你需要内存的一件事。但是您还需要为它们指向的每个结构提供内存。另一种方法是将 GList.list 声明为零长度数组而不是指针,然后在整个结构单元中分配。
-
@LeeDanielCrocker 所以其中一项任务不是使用数组,而是将指针与 malloc() 和 realloc() 一起使用。我认为 malloc(n * sizeof(list)) 与 list[n] 基本相同,只是它是动态内存。