我一直在从事大学C作业,并且一直试图理解我似乎对我的代码所犯的错误。基本上,我的指针(和/或内存分配)似乎有问题。
此赋值主要是关于链表,因此结构包含指向列表中下一个元素的指针。显然,遍历列表直到我发现当前元素有一个指向下一个元素的NULL指针(然后我将其更改为指向我要添加的'new'元素的指针。
我遇到的问题是,由于某些原因,我的代码似乎完全搞砸了我的内存指针,因为它们会以某种方式变得混乱。它们似乎很好,但很快就会变成垃圾。
以下是我在XCode调试器中的手表向我展示的内容:
第一个圆圈显示了值作为列表中的第一个元素,据我所知,它最初设置正确,应为“C0001 \ 0”。第二个圆圈显示current->nextCategory
指针,该指针应该为NULL(0x0),而是显示奇怪的内存地址(查看它的大小!)。我认为这些问题是相关的,但由于我是C的新手,我不知道如何或为什么。
在任何一种情况下,当我在while语句中检查current->nextCategory != NULL
时,它会抛出EXC_BAD_ACCESS:
我花了几个小时把头发拉出来,因为我无法弄清楚程序到底发生了什么。我的指针是做错了什么,还是使用malloc()
不正确?
以下是我的计划的相关部分:
/****************************************************************************
* Initialises the system to a safe empty state.
****************************************************************************/
void systemInit(GJCType* menu)
{
if (menu == NULL) {
fprintf(stderr, "can't initialize system with a null menu pointer.\n");
exit(EXIT_FAILURE);
}
menu->headCategory = NULL;
menu->numCategories = 0;
}
/****************************************************************************
* Loads all data into the system.
****************************************************************************/
int loadData(GJCType* menu, char* menuFile, char* submenuFile)
{
FILE *fp;
size_t len;
char *line;
char *buffer;
CategoryTypePtr category_p;
ItemTypePtr item_p;
char *catId;
if (menu == NULL)
return FALSE;
fp = fopen(menuFile, "r");
if(fp == NULL) {
fprintf(stderr, "can't open %s\n", menuFile);
return FALSE;
}
buffer = malloc(MAX_BUFFER_SIZE);
len = MAX_BUFFER_SIZE;
catId = malloc(ID_LEN + 1);
while((buffer = fgetln(fp, &len))) {
line = strtok(buffer, "\n\0");
category_p = malloc(sizeof(CategoryTypePtr));
if (!tokenizeCategory(line, category_p)) {
fprintf(stderr, "can't tokenize category:> %s\n", line);
free(category_p);
category_p = NULL;
free(buffer);
free(catId);
return FALSE;
}
pushCategory(menu, category_p);
free(category_p);
category_p = NULL;
}
fp = fopen(submenuFile, "r");
if(fp == NULL) {
fprintf(stderr, "can't open %s\n", submenuFile);
return FALSE;
}
while((buffer = fgetln(fp, &len))) {
line = strtok(buffer, "\n\0");
item_p = malloc(sizeof(ItemTypePtr));
if (!tokenizeItem(line, item_p, catId)) {
fprintf(stderr, "can't tokenize item:> %s\n", line);
free(item_p);
item_p = NULL;
free(buffer);
free(catId);
return FALSE;
}
category_p = findCategory(menu, catId);
pushItem(category_p, item_p);
free(item_p);
item_p = NULL;
}
free(buffer);
free(catId);
return TRUE;
}
void pushItem(CategoryTypePtr category, ItemTypePtr item)
{
ItemTypePtr current;
ItemTypePtr new;
if ((new = malloc(sizeof(ItemTypePtr))) == NULL) {
fprintf(stderr, "can't malloc enough memory for new item pointer.\n");
exit(EXIT_FAILURE);
}
*new = *item;
if (category->headItem == NULL) {
category->headItem = new;
} else {
current = category->headItem;
while (current->nextItem != NULL) {
current = current->nextItem;
}
current->nextItem = new;
}
category->numItems++;
}
void pushCategory(GJCType* menu, CategoryTypePtr category)
{
CategoryTypePtr current;
CategoryTypePtr new;
if ((new = malloc(sizeof(CategoryTypePtr))) == NULL) {
fprintf(stderr, "can't malloc enough memory for new category pointer.\n");
exit(EXIT_FAILURE);
}
*new = *category;
if (menu->headCategory == NULL) {
menu->headCategory = new;
} else {
current = menu->headCategory;
while (current->nextCategory != NULL) {
current = current->nextCategory;
}
current->nextCategory = new;
}
menu->numCategories++;
}
CategoryTypePtr findCategory(GJCType* menu, char* id)
{
CategoryTypePtr current;
current = menu->headCategory;
while (current != NULL) {
if (!strcmp(current->categoryID, id))
return current;
current = current->nextCategory;
}
return NULL;
}
/* This function takes the character delimited string and converts it into
* a category structure at the location of the category pointer supplied.
*/
int tokenizeCategory(char *data, CategoryTypePtr category)
{
char* buffer;
if (category == NULL || strlen(data) < 1)
return FALSE;
buffer = malloc(MAX_BUFFER_SIZE);
strcpy(buffer, data);
strcpy(category->categoryID, strtok(buffer, "|\n"));
category->drinkType = *(strtok(NULL, "|\n"));
strcpy(category->categoryName, strtok(NULL, "|\n"));
strcpy(category->categoryDescription, strtok(NULL, "|\n"));
category->numItems = 0;
category->nextCategory = NULL;
category->headItem = NULL;
free(buffer);
return TRUE;
}
/* This function takes the character delimited string and converts it into
* an item structure at the location of the item pointer supplied.
*/
int tokenizeItem(char *data, ItemTypePtr item, char* categoryId)
{
char* buffer;
int i;
if (item == NULL || strlen(data) < 1)
return FALSE;
buffer = malloc(MAX_BUFFER_SIZE);
strcpy(buffer, data);
strcpy(item->itemID, strtok(buffer, "|\n"));
strcpy(categoryId, strtok(NULL, "|\n"));
strcat(categoryId, "\0");
strcpy(item->itemName, strtok(NULL, "|\n"));
for (i = 0; i < NUM_PRICES; i++)
sscanf(strtok(NULL, "|\n"),"%d.%d",&(item->prices[i].dollars),&(item->prices[i].cents));
strcpy(item->itemDescription, strtok(NULL, "|\n"));
item->nextItem = NULL;
free(buffer);
return TRUE;
}
标题定义:
/* System-wide constants. */
#define ID_LEN 5
#define MIN_NAME_LEN 1
#define MAX_NAME_LEN 25
#define MIN_DESC_LEN 1
#define MAX_DESC_LEN 250
#define NUM_PRICES 3
#define HOT 'H'
#define COLD 'C'
#define FALSE 0
#define TRUE 1
#define MAX_BUFFER_SIZE 1024
typedef struct category* CategoryTypePtr;
typedef struct item* ItemTypePtr;
/* Structure definitions. */
typedef struct price
{
unsigned dollars;
unsigned cents;
} PriceType;
typedef struct item
{
char itemID[ID_LEN + 1];
char itemName[MAX_NAME_LEN + 1];
PriceType prices[NUM_PRICES];
char itemDescription[MAX_DESC_LEN + 1];
ItemTypePtr nextItem;
} ItemType;
typedef struct category
{
char categoryID[ID_LEN + 1];
char categoryName[MAX_NAME_LEN + 1];
char drinkType; /* (H)ot or (C)old. */
char categoryDescription[MAX_DESC_LEN + 1];
CategoryTypePtr nextCategory;
ItemTypePtr headItem;
unsigned numItems;
} CategoryType;
typedef struct gjc
{
CategoryTypePtr headCategory;
unsigned numCategories;
} GJCType;
答案 0 :(得分:10)
在我看来,你并没有正确分配内存。
category_p = malloc(sizeof(CategoryTypePtr));
这只分配足够的内存来存储单个地址,而不是整个类别结构。尝试类似:
category_p = malloc(sizeof(CategoryType));
答案 1 :(得分:3)
问题在于以下几点:
category_p = malloc(sizeof(CategoryTypePtr));
item_p = malloc(sizeof(ItemTypePtr));
这些行,如编写的那样,只分配足够的内存来存储指针,而不是你想要指向的结构。
尝试:
category_p = malloc(sizeof(CategoryType));
item_p = malloc(sizeof(ItemType));
此外,您的推送功能过于复杂。在将列表节点添加到列表之前,无需复制列表节点。只需将指向新节点的地址分配给当前尾部的->next...
指针:
void pushCategory(GJCType* menu, CategoryTypePtr category)
{
CategoryTypePtr current;
// no need to allocate space just for a pointer
if (menu->headCategory == NULL) {
menu->headCategory = category;
} else {
current = menu->headCategory;
while (current->nextCategory != NULL) {
current = current->nextCategory;
}
current->nextCategory = category;
}
menu->numCategories++;
}
然后您不希望主例程中的free(item_p)
和free(category_p)
调用因为您已分配的内存现在被列表引用。处理清单时,您需要释放此内存。
答案 2 :(得分:1)
在以下代码中
category_p = malloc(sizeof(CategoryTypePtr));
.
.
.
pushCategory(menu, category_p);
free(category_p);
category_p = NULL;
和
item_p = malloc(sizeof(ItemTypePtr));
.
.
.
pushItem(category_p, item_p);
free(item_p);
item_p = NULL;
您已先分配内存,将其链接到列表中,以及您已解除分配的地址。我认为这是问题所在。
你也做到了:
item_p = malloc(sizeof(ItemTypePtr));
和
category_p = malloc(sizeof(CategoryTypePtr));
ItemTypePtr
和CategoryTypePtr
是指针,所以你分配的只是指针的大小,内存不能容纳结构的整个数据。你需要做
item_p = malloc(sizeof(ItemType));
category_p = malloc(sizeof(CategoryType));
同样在其他位置,您需要根据需要将ItemTypePtr
更改为ItemType
。我不会像你所做的那样告诉typedef
指针。这会给阅读代码带来困难。如果你有复杂的函数指针表达式,那么typedef
就可以了;在我看来。
答案 3 :(得分:1)
要调试此类问题,我只建议使用valgrind:它将为您提供有关缓冲区溢出,写入错误,内存丢失的非常有价值的帮助。它安装在开发者包中。
答案 4 :(得分:1)
您有多个问题。除了错误地分配你正在做的内存
*new = *category;
期待pushCategory
函数中的期望自动复制category
结构的内部内容:这根本不起作用。您需要分配一个新的CategoryType
对象,然后适当地复制每个单独的元素。像这样:
void pushCategory(GJCType* menu, CategoryTypePtr category)
{
CategoryTypePtr newCategory;
CategoryTypePtr current;
if ((newCategory = malloc(sizeof(CategoryType))) == NULL) {
fprintf(stderr, "can't malloc enough memory for new category pointer.\n");
exit(EXIT_FAILURE);
}
// copy individual elements here and set values properly
newCategory->headItem = NULL;
strncpy(newCategory->categoryID, category->categoryID, ID_LEN);
// copy other strings and NULL-initialize other pointers
if (menu->headCategory == NULL) {
menu->headCategory = new;
} else {
current = menu->headCategory;
while (current->nextCategory != NULL) {
current = current->nextCategory;
}
current->nextCategory = newCategory;
}
menu->numCategories++;
}
您需要为pushItem
执行相同的操作。