Отчаянно ища ответ на мою проблему с указателем - PullRequest
9 голосов
/ 17 мая 2011

Я работал над заданием колледжа C и пытался разобраться с ошибкой, которую я, похоже, имею в своем коде.По сути, кажется, что что-то не так с моими указателями (и / или распределением памяти).

Это назначение в основном относится к связанным спискам, поэтому структуры содержат указатели на следующий элемент в списке.Очевидно, чтобы просмотреть список, пока я не обнаружу, что текущий элемент имеет нулевой указатель на следующий элемент (а затем я изменяю его на указатель на «новый» элемент, который я хочу добавить.

ПроблемаУ меня есть, хотя, по какой-то причине мой код, кажется, полностью искажает мои указатели памяти, потому что они каким-то образом искажаются. Они на мгновение кажутся нормальными, но очень скоро становятся мусором.

Вот что мои часыв отладчике XCode мне показывают:

My screenshot of the debugger

Первый кружок показывает мне значения в качестве первого элемента в списке, которые, насколько я могу судить, изначально установлены правильно, идолжно быть «C0001 \ 0». Второй кружок показывает указатель current->nextCategory, который должен быть НЕДЕЙСТВИТЕЛЕН (0x0), но вместо этого показывает этот странный адрес памяти (посмотрите на его размер!). Я предполагаю, что эти проблемы связаны, нопоскольку я новичок в C, я не знаю, как и почему.

В любом случае, когда я проверяю current->nextCategory != NULL в своем операторе while, он выдает 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 ]

10 голосов
/ 17 мая 2011

Мне кажется, вы неправильно распределяете память.

category_p = malloc(sizeof(CategoryTypePtr));

При этом выделяется только достаточно памяти для хранения одного адреса, а не всей структуры категории. Попробуйте что-то вроде:

category_p = malloc(sizeof(CategoryType));
3 голосов
/ 17 мая 2011

Проблема заключается в следующих строках:

  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) в основной подпрограммепотому что память, которую вы выделили, теперь ссылается на список.Вам нужно будет освободить эту память, когда вы избавитесь от списка.

1 голос
/ 17 мая 2011

У вас есть несколько вопросов. Помимо неправильного распределения памяти вы делаете

*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.

1 голос
/ 17 мая 2011

Для устранения проблем такого рода я могу только предложить использовать valgrind : он предоставит вам очень ценную помощь по переполнению буфера, записи без ограничений, потере памяти.Он установлен в пакете developper.

1 голос
/ 17 мая 2011

В следующем коде

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 ing it okay; по моему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...