Динамическое выделение памяти из ранее определенной длины строки в структурный массив

#c

#c

Вопрос:

Мне нужно создать таблицу различных продуктов и их питания в зависимости от их веса и калорий, т. Е. Яблоко 4 унции 50 калорий. Массив объявляется автоматически, и первые 2 элемента явно инициализируются. Затем я должен перебирать оставшиеся продукты, которые должны быть введены пользователем, исходя из числа, которое я ввел в макрос LUNCH_QTY. В цикле мне нужно сохранить входные данные во временный символьный буфер и сохранить вес и калории непосредственно в структуре. Затем определите точную длину имени продукта и динамически выделите точный объем памяти, определенный на предыдущем шаге, и сохраните указатель на него в элементе name структуры. Я НЕ МОГУ использовать calloc или realloc. Поэтому я использовал malloc. Если динамическое выделение завершается неудачно, мне нужно вывести ошибку, используя stderr. И если это сработает, мне нужно скопировать имя продукта в динамически выделяемую память с помощью memcpy. После завершения цикла for я создаю таблицу и освобождаю динамически выделяемую память.

Моя проблема в том, что когда я выполняю цикл, я зависаю в операторе «if» на последней итерации. Кажется, что он все еще выполняет цикл, но затем он выбивает меня из отладчика. Может быть, у меня проблемы с указателями, и я получаю доступ к недоступной памяти? Idk, но любая помощь будет высоко оценена. Я новичок в C, поэтому любая информация была бы отличной. Заранее спасибо. Ниже приведен код.

PS. таблица неполная, и я не знаю, будут ли еще работать распределения таблиц, потому что я еще не смог зайти так далеко, также я еще не освободил память, и я не знаю, так ли это сделано.

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LUNCH_QTY 4
#define INIT_ELEM 2
#define BUFF_LENGTH 128

struct Food
{
    char *name;              //"Name" attribute of food
    int weight, calories;    //"Weight" and "Calories" attributes of food
}lunches[LUNCH_QTY] = {[0] = {"Apple", 4, 100}, [1] = {"Salad", 2, 80}};

int main()
{
    for (int StructCnt = INIT_ELEM; StructCnt < LUNCH_QTY; StructCnt  )
    {
        printf("Please input a foods name, weight, and calories all space"
               " separated: ");

        char NameBuff[BUFF_LENGTH];      //Temporary Character Buffer if this was done correctly?

        scanf("%s %d %d", NameBuff, amp;lunches[StructCnt].weight,
              amp;lunches[StructCnt].calories);
        
        //Get name length for memory allocation
        int NameLength = strlen(NameBuff);

        //Find length of food name
        int NameLength = strlen(NameBuff);

        //If dynamic allocation fails output an error
        if ((lunches[StructCnt].name = malloc(NameLength   1)) == NULL)
        {
            fprintf(stderr,"Error - String Name Was Inputted Incorrectly");
            return(-1);
        }

        memcpy(lunches[StructCnt].name, NameBuff, sizeof(NameBuff));

    }

   


 //Print out food names, weight, and calories in order of the structure
    //and then free the memory once name is printed out
    printf("n         LUNCH MENU"
           "nFOOD         WEIGHT  CALORIES"
           "n-----------------------------n");

    int PrintCnt = 0;
    while (PrintCnt < LUNCH_QTY)
    {
        printf("%-15s M Mn", lunches[PrintCnt].name,
               lunches[PrintCnt].weight, lunches[PrintCnt].calories);
        PrintCnt  ;
    }
    //Free all dynamically allocated memory
    int FreeCnt = 0;
    while (FreeCnt < LUNCH_QTY)
    {
        free(lunches[PrintCnt].name);
        PrintCnt  ;
    }

    return 0;
}
  

Комментарии:

1. malloc(NameLength * sizeof(char)) -> malloc((NameLength 1) * sizeof(char)) . Вам нужно на один символ больше для завершения строки NUL. Это очень распространенная ошибка, которую мы видим здесь. Кстати malloc(NameLength 1) , (char*) приведение бесполезно и sizeof (char) равно 1 по определению. Однако могут быть и другие проблемы

2. Привет @Jabberwocky, я реализовал то, что вы сказали: if ((lunches[StructCnt].name = malloc(NameLength 1)) == NULL) { fprintf(stderr, «Ошибка — имя строки было введено неправильно»); break; } но я получаю ту же проблему, что и при зависании напоследняя итерация цикла for в этом операторе? есть идеи?

3. memcpy(lunches[StructCnt].name, NameBuff, sizeof(NameBuff)); должно быть `memcpy(lunches[StructCnt].name, NameBuff, NameLength 1); , however it would be better to replace the sequence from malloc through memcpy with lunches[StructCnt].name = strdup(NameBuff);` или, по крайней мере, заменить memcy на strcpy(lunches[StructCnt].name, NameBuff);

4. @mevets это помогло, спасибо вам за это. И мы вынуждены использовать memcpy в этом назначении.

5. последний вопрос для всех вас, как мне освободить память для всех имен, которые я использовал, будет ли это что-то вроде free (lunches-> names) или что-то подобное, и мне придется перебирать каждое имя, или есть одна строка, которая освобождает всю память имен, в этомструктура?

Ответ №1:

Здесь вы выделяете NameLength 1 байт:

 lunches[StructCnt].name = malloc(NameLength   1));
  

Но здесь вы копируете sizeof(NameBuff) (= 128) байт:

 memcpy(lunches[StructCnt].name, NameBuff, sizeof(NameBuff));
  

Вместо этого вы хотите это:

 memcpy(lunches[StructCnt].name, NameBuff, NameLength   1);
  

или даже лучше:

 strcpy(lunches[StructCnt].name, NameBuff);
  

Кроме того, в последнем цикле while вы пытаетесь освободить первые два элемента массива, хотя они не были выделены динамически с помощью malloc .

Вам нужно это:

 PrintCnt = INIT_ELEM;          // free elements 2 and 3 and leave 
                               // elements 0 and 1 alone
while (PrintCnt < LUNCH_QTY)
{
  free(lunches[PrintCnt].name);
  PrintCnt  ;
}
  

Также вы должны использовать for циклы, которые здесь более уместны (вы уже использовали цикл for для ввода).

 for (int count = 0; count < LUNCH_QTY; count  )
{
  printf("%-15s M Mn", lunches[count].name,
                            lunches[count].weight, lunches[count].calories);
}

//Free all dynamically allocated memory
for (int count = INIT_ELEM; count < LUNCH_QTY; count  )
{
  free(lunches[count].name);
}
  

Имейте в виду, что ваш дизайн подвержен ошибкам, это очень плохая идея иметь динамически выделяемую память, а не динамически выделяемую память в одном и том же массиве.