2011-05-17 11 views
9

He estado trabajando en una tarea de la C en la universidad, y he intentado dar sentido a un error que parece tener con mi código. Básicamente, parece que algo anda mal con mis punteros (y/o asignación de memoria).Buscando desesperadamente la respuesta a mi problema con el puntero

Esta asignación se trata principalmente de listas vinculadas, por lo que las estructuras contienen punteros al siguiente elemento de la lista. Obviamente, para recorrer la lista hasta que encuentre que el elemento actual tiene un puntero NULL al siguiente elemento (y luego lo cambio para ser el puntero al elemento 'nuevo' que quiero agregar.

El problema que tengo sin embargo, es por alguna razón mi código parece estar completamente atornillado con mis punteros de memoria, porque de alguna manera se confunden. Parecen bien por el momento, pero muy pronto van a ser basura.

Esto es lo que mis relojes en el depurador de XCode son mostrándome:

My screenshot of the debugger

El primer círculo me muestra los valores como el primer elemento en la lista, que por lo que puedo decir se establece inicialmente correctamente, y debe ser "C0001 \ 0". El segundo círculo muestra el puntero current->nextCategory que debe ser NULL (0x0) pero en su lugar muestra esa extraña dirección de memoria (¡mire su tamaño!). Supongo que estos problemas están relacionados, pero como soy nuevo en C, no sé cómo ni por qué.

En cualquiera de los casos, cuando compruebo current->nextCategory != NULL en mi declaración de tiempo, se lanza EXC_BAD_ACCESS:

EXE_BAD_ACCESS error

He pasado las últimas horas tirando de mi pelo que robaba no puede averiguar qué diablos está pasando a mi programa. ¿Estoy haciendo algo mal con mis punteros o usando malloc() incorrectamente?

Aquí es la parte pertinente de mi programa:

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

definiciones de cabecera:

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

Respuesta

10

A mi me parece que no estás asignación de memoria correctamente.

category_p = malloc(sizeof(CategoryTypePtr)); 

Esto solo asigna suficiente memoria para almacenar una sola dirección, no una estructura de categoría completa. Pruebe algo como:

category_p = malloc(sizeof(CategoryType)); 
+4

O.M.G. * facepalms * – Ash

+0

Otra razón por la que no deberías usar 'typedef' cada vez que quieras usar un tipo. ;) – Mehrdad

1

En el siguiente código

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

y

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

Usted ha asignado la memoria primero, vinculado en la lista, y esa misma dirección que ha desasignado . Yo pienso que éste es el problema.

Y también se han hecho:

item_p = malloc(sizeof(ItemTypePtr)); 

y

category_p = malloc(sizeof(CategoryTypePtr)); 

la ItemTypePtr y CategoryTypePtr son punteros, así que lo que se puede asignar es sólo el tamaño del puntero, y la memoria no puede acomodar el datos completos de la estructura. Que tiene que hacer

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

También en otros lugares es necesario cambiar la ItemTypePtr a ItemType según sea necesario. Diré no a typedef los punteros como lo has hecho. Esto puede traer dificultades para leer el código. Si tiene expresiones de puntero de función complejas, entonces typedef está bien; en mi opinión.

3

El problema es estas líneas:

category_p = malloc(sizeof(CategoryTypePtr)); 

    item_p = malloc(sizeof(ItemTypePtr)); 

Estas líneas, como está escrito, sólo se asigna suficiente memoria para almacenar un puntero, no la estructura que desea apuntar.

Probar:

category_p = malloc(sizeof(CategoryType)); 

    item_p = malloc(sizeof(ItemType)); 

Además, sus funciones de empuje son excesivamente complicado. No es necesario copiar los nodos de la lista antes de agregarlos a la lista. Basta con asignar la dirección en el puntero al nodo nuevo al puntero ->next... en la cola actual:

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++; 
} 

Entonces no quieren que los free(item_p) y free(category_p) llamadas en la rutina principal porque la memoria que ha asignado es ahora está siendo referenciado por la lista. Tendrá que liberar esta memoria cuando se deshaga de la lista.

1

Para depurar este tipo de problemas, solo puedo sugerir el uso de valgrind: le proporcionará una ayuda muy valiosa en desbordamiento de búfer, escritura sin límite, pérdida de memoria. Está instalado en el paquete de desarrollo.

1

Tiene varios problemas. Además de la asignación de memoria de forma incorrecta que está haciendo

*new = *category; 

en su función pushCategory esperando para copiar automagicamente el contenido interno de la estructura category: que simplemente no funciona. Deberá asignar un nuevo objeto CategoryType y luego copiar cada elemento individual de manera apropiada. Algo como esto:

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++; 
} 

Usted tendrá que hacer lo mismo para pushItem.

+0

En realidad no estaba haciendo eso inicialmente, ya que no es necesario, pero lo hice porque pensé que me faltaban los indicadores. Pero estás en lo correcto, y eliminaré esta acción innecesaria. – Ash

Cuestiones relacionadas