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

时间:2023-01-05 07:18:34

I have been working on a college C assignment, and have been trying to make sense of a bug I seem to be having with my code. Basically, it seems like something is wrong with my pointers (and or memory allocation).

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

This assignment is primarily about linked lists, so the structs contain pointers to the next element in the list. Obviously, to traverse the list until I find that the current element has a NULL pointer to the next element (and then I change that to be the pointer to the 'new' element I want to add.

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

The problem I have though, is for some reason my code seems to be completely screwing with my memory pointers, because they're getting garbled somehow. They seem fine for moment but very soon go rubbish.

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

Here is what my watches in XCode's debugger are showing me:

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

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

The first circle shows me the values as the first element in the list, which as far as I can tell are initially set correctly, and should be "C0001\0". The second circle shows the current->nextCategory pointer which should be NULL (0x0) but instead show that weird memory address (look at the size of it!). I assume that these problems are related, but as I am new to C, I don't know how or why.

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

In either case, when I check current->nextCategory != NULL in my while statement, it throws EXC_BAD_ACCESS:

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

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

I've spent the past few hours pulling my hair out becasue I cannot work out what the heck is happening to my program. Am I doing something wrong with my pointers, or using malloc() improperly?

我花了几个小时把头发拉了出来,因为我无法弄清楚我的计划到底发生了什么。我的指针做错了什么,或者使用malloc()不正确?

Here is the relevant part of my program:

这是我的计划的相关部分:

/****************************************************************************
* 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;
}

Header definitions:

/* 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 个解决方案

#1


10  

It looks to me like you're not allocating memory properly.

它看起来像你没有正确分配内存。

category_p = malloc(sizeof(CategoryTypePtr));

This only allocates enough memory to store a single address, not an entire category structure. Try something like:

这仅分配足够的内存来存储单个地址,而不是整个类别结构。尝试以下方法:

category_p = malloc(sizeof(CategoryType));

#2


3  

The problem is these lines:

问题是这些问题:

  category_p = malloc(sizeof(CategoryTypePtr));

  item_p = malloc(sizeof(ItemTypePtr));

These lines, as written, only allocate enough memory to store a pointer, not the structure you want to point to.

这些行(如写入的那样)仅分配足够的内存来存储指针,而不是您想要指向的结构。

Try:

  category_p = malloc(sizeof(CategoryType));

  item_p = malloc(sizeof(ItemType));

Also, your push functions are overly complicated. There's no need to copy the list nodes before you add them to the list. Just assign the address in the pointer to the new node to the ->next... pointer in the current tail:

此外,您的推送功能过于复杂。在将列表节点添加到列表之前,无需复制列表节点。只需将指向新节点的地址分配给当前尾部的 - > 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++;
}

Then you don't want the free(item_p) and free(category_p) calls in the main routine because the memory you've allocated is now being referenced by the list. You will need to free this memory when you dispose of the list.

然后你不希望主例程中的free(item_p)和free(category_p)调用,因为你已分配的内存现在被列表引用。处理列表时,您需要释放此内存。

#3


1  

To debug this kind of issues, I can only suggest to use valgrind : it will provide you very valuable help on buffer overflow, out of bound writing, memory loss. It's installed in the developper package.

为了调试这类问题,我只建议使用valgrind:它将为您提供有关缓冲区溢出,写入错误,内存丢失的非常有价值的帮助。它安装在开发包中。

#4


1  

You have multiple issues. Besides incorrectly allocating memory you are doing

你有很多问题。除了错误分配你正在做的内存

*new = *category;

in your pushCategory function expecting to automagically copy the internal contents of the category structure: that simply doesn't work. You'll need to allocate a new CategoryType object and then copy each individual element appropriately. Something like this:

期望在pushCategory函数中自动复制类别结构的内部内容:这根本不起作用。您需要分配一个新的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++;
}

You will need to do the same thing for pushItem.

你需要为pushItem做同样的事情。

#5


1  

In the following code

在以下代码中

category_p = malloc(sizeof(CategoryTypePtr));
.
.
.
pushCategory(menu, category_p);
free(category_p);
category_p = NULL;

and

item_p = malloc(sizeof(ItemTypePtr));
.
.
.
pushItem(category_p, item_p);
free(item_p);
item_p = NULL;

You have allocated the memory first, linked it in the list, and that very address you have deallocated. I think this is the problem.

您已先分配内存,将其链接到列表中,以及已取消分配的地址。我认为这是问题所在。

And also you have done:

而你也做到了:

item_p = malloc(sizeof(ItemTypePtr));

and

category_p = malloc(sizeof(CategoryTypePtr));

the ItemTypePtr and CategoryTypePtr are pointers, so what you allocate is only the size of the pointer, and the memory cannot accomodate the entire data of the structure. You need to do

ItemTypePtr和CategoryTypePtr是指针,因此您分配的只是指针的大小,并且内存不能容纳结构的整个数据。你需要这样做

item_p = malloc(sizeof(ItemType));
category_p = malloc(sizeof(CategoryType));

Also in other locations you need to change the ItemTypePtr to ItemType as needed. I will tell not to typedef the pointers as you have done. This can bring difficulty in reading the code. If you have complex function pointer expressions then typedef ing it okay; in my opinion.

此外,在其他位置,您需要根据需要将ItemTypePtr更改为ItemType。我会告诉你不要像你那样输入指定指针。这会给阅读代码带来困难。如果你有复杂的函数指针表达式,那么输入它就可以了;我的想法是。

#1


10  

It looks to me like you're not allocating memory properly.

它看起来像你没有正确分配内存。

category_p = malloc(sizeof(CategoryTypePtr));

This only allocates enough memory to store a single address, not an entire category structure. Try something like:

这仅分配足够的内存来存储单个地址,而不是整个类别结构。尝试以下方法:

category_p = malloc(sizeof(CategoryType));

#2


3  

The problem is these lines:

问题是这些问题:

  category_p = malloc(sizeof(CategoryTypePtr));

  item_p = malloc(sizeof(ItemTypePtr));

These lines, as written, only allocate enough memory to store a pointer, not the structure you want to point to.

这些行(如写入的那样)仅分配足够的内存来存储指针,而不是您想要指向的结构。

Try:

  category_p = malloc(sizeof(CategoryType));

  item_p = malloc(sizeof(ItemType));

Also, your push functions are overly complicated. There's no need to copy the list nodes before you add them to the list. Just assign the address in the pointer to the new node to the ->next... pointer in the current tail:

此外,您的推送功能过于复杂。在将列表节点添加到列表之前,无需复制列表节点。只需将指向新节点的地址分配给当前尾部的 - > 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++;
}

Then you don't want the free(item_p) and free(category_p) calls in the main routine because the memory you've allocated is now being referenced by the list. You will need to free this memory when you dispose of the list.

然后你不希望主例程中的free(item_p)和free(category_p)调用,因为你已分配的内存现在被列表引用。处理列表时,您需要释放此内存。

#3


1  

To debug this kind of issues, I can only suggest to use valgrind : it will provide you very valuable help on buffer overflow, out of bound writing, memory loss. It's installed in the developper package.

为了调试这类问题,我只建议使用valgrind:它将为您提供有关缓冲区溢出,写入错误,内存丢失的非常有价值的帮助。它安装在开发包中。

#4


1  

You have multiple issues. Besides incorrectly allocating memory you are doing

你有很多问题。除了错误分配你正在做的内存

*new = *category;

in your pushCategory function expecting to automagically copy the internal contents of the category structure: that simply doesn't work. You'll need to allocate a new CategoryType object and then copy each individual element appropriately. Something like this:

期望在pushCategory函数中自动复制类别结构的内部内容:这根本不起作用。您需要分配一个新的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++;
}

You will need to do the same thing for pushItem.

你需要为pushItem做同样的事情。

#5


1  

In the following code

在以下代码中

category_p = malloc(sizeof(CategoryTypePtr));
.
.
.
pushCategory(menu, category_p);
free(category_p);
category_p = NULL;

and

item_p = malloc(sizeof(ItemTypePtr));
.
.
.
pushItem(category_p, item_p);
free(item_p);
item_p = NULL;

You have allocated the memory first, linked it in the list, and that very address you have deallocated. I think this is the problem.

您已先分配内存,将其链接到列表中,以及已取消分配的地址。我认为这是问题所在。

And also you have done:

而你也做到了:

item_p = malloc(sizeof(ItemTypePtr));

and

category_p = malloc(sizeof(CategoryTypePtr));

the ItemTypePtr and CategoryTypePtr are pointers, so what you allocate is only the size of the pointer, and the memory cannot accomodate the entire data of the structure. You need to do

ItemTypePtr和CategoryTypePtr是指针,因此您分配的只是指针的大小,并且内存不能容纳结构的整个数据。你需要这样做

item_p = malloc(sizeof(ItemType));
category_p = malloc(sizeof(CategoryType));

Also in other locations you need to change the ItemTypePtr to ItemType as needed. I will tell not to typedef the pointers as you have done. This can bring difficulty in reading the code. If you have complex function pointer expressions then typedef ing it okay; in my opinion.

此外,在其他位置,您需要根据需要将ItemTypePtr更改为ItemType。我会告诉你不要像你那样输入指定指针。这会给阅读代码带来困难。如果你有复杂的函数指针表达式,那么输入它就可以了;我的想法是。