拼命寻找我的指针问题的答案

时间:2011-05-17 04:52:05

标签: c memory pointers linked-list

我一直在从事大学C作业,并且一直试图理解我似乎对我的代码所犯的错误。基本上,我的指针(和/或内存分配)似乎有问题。

此赋值主要是关于链表,因此结构包含指向列表中下一个元素的指针。显然,遍历列表直到我发现当前元素有一个指向下一个元素的NULL指针(然后我将其更改为指向我要添加的'new'元素的指针。

我遇到的问题是,由于某些原因,我的代码似乎完全搞砸了我的内存指针,因为它们会以某种方式变得混乱。它们似乎很好,但很快就会变成垃圾。

以下是我在XCode调试器中的手表向我展示的内容:

My screenshot of the debugger

第一个圆圈显示了值作为列表中的第一个元素,据我所知,它最初设置正确,应为“C0001 \ 0”。第二个圆圈显示current->nextCategory指针,该指针应该为NULL(0x0),而是显示奇怪的内存地址(查看它的大小!)。我认为这些问题是相关的,但由于我是C的新手,我不知道如何或为什么。

在任何一种情况下,当我在while语句中检查current->nextCategory != NULL时,它会抛出EXC_BAD_ACCESS:

EXE_BAD_ACCESS error

我花了几个小时把头发拉出来,因为我无法弄清楚程序到底发生了什么。我的指针是做错了什么,还是使用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;

5 个答案:

答案 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));

ItemTypePtrCategoryTypePtr是指针,所以你分配的只是指针的大小,内存不能容纳结构的整个数据。你需要做

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执行相同的操作。