Как использовать reallo c для сокращения размера массива строк - PullRequest
2 голосов
/ 03 мая 2020

Я попытался взять определенную строку, установить строку для определенного размера, 50 символов, затем после того, как все строки будут введены, он будет сортировать их, а затем перевести c размеры от 50 символов до длина строки, которую написал пользователь, если сначала я дал ей 50 байтов, а кто-то ввел «привет», она изменится на необходимое количество байтов.

#include <stdio.h>

#define MAX_CHARS 50

int main(void)
{
    int i = 0, j = 0;
    char* temp = 0;
    char** names = 0;
    int amount = 0;

    // Getting number of friends from user
    printf("Enter number of friends: ");
    scanf("%d", &amount);
    getchar();

    // Allocating space for the names.
    temp = (char*)malloc(MAX_CHARS * sizeof(char));
    names = (char*)malloc(amount * sizeof(char));

    for (i = 0; i < amount; i++)
    {
        names[i] = (char*)malloc((MAX_CHARS + 1) * sizeof(char));
    }

    // Getting the names from the user
    for (i = 0; i < amount; i++)
    {
        printf("Enter name of friend %d: ", i + 1);
        fgets(names[i], MAX_CHARS - 1, stdin);
    }

    for (i = 0; i < amount; i++)
    {
        for (j = i + 1; j < amount; j++)
        {
            if (strcmp(names[j], names[i]) < 0)
            {
                strcpy(temp, names[j]);
                strcpy(names[j], names[i]);
                strcpy(names[i], temp);
            }
        }
        // Reallocating the 50 bytes space to only the space needed.
        printf("%d", strlen(names[i]));
        (*names)[i] = (char*)realloc((*names)[i], strlen(names[i]) * sizeof(char));
    }

    for (i = 0; i < amount; i++)
    {
        printf("%s", names[i]);
    }

    free(names);
    getchar();
    return 0;
}

Ответы [ 3 ]

2 голосов
/ 03 мая 2020

names - это массив указателей на char, поэтому в

names = (char*)malloc(amount * sizeof(char));

вы выделяете недостаточно, и позже поведение будет неопределенным, когда вы назначит его из него

do (приведение не имеет смысла)

names = malloc(amount * sizeof(char*));

Выполнение

(*names)[i] = (char*)realloc((*names)[i], strlen(names[i]) * sizeof(char));

недопустимо, поскольку (*names)[i] является символом, не забудьте также место для нулевого символа, заканчивающего строку, поэтому вы хотите:

names[i] = realloc(names[i], strlen(names[i]) + 1);

обратите внимание, что по определению sizeof(char) равно 1

Без проверки результата из malloc и realloc вы предполагаете / надеетесь, что памяти достаточно, но это может быть ложным, и в этом случае эти функции возвращают NULL, более безопасно проверить этот случай. Это означает, что realloc для первого сохранения во вспомогательном char*, чтобы не потерять текущее значение names[i], вы можете продолжать использовать, если realloc возвращает NULL

To

scanf("%d", &amount);

опасно, когда ввод недействителен, вы этого не знаете, и сумма не инициализируется с неопределенным поведением при его использовании, например,

if (scanf("%d", &amount) != 1)
{
   puts("invalid value");
   return -1;
}

Учитывая, как вы используете names[i], когда вы

names[i] = (char*)malloc((MAX_CHARS + 1) * sizeof(char));

выделяете 1 байт слишком много, вы можете сделать

 names[i] = malloc(MAX_CHARS);

Предупреждение выполняя:

fgets(names[i], MAX_CHARS - 1, stdin);

вероятный символ новой строки, заканчивающий ввод, сохраняется в names[i], возможно, вам нужно удалить его. В этом случае вам придется адаптироваться, когда вы печатаете имена, чтобы ввести разделитель между именами, пробелом или новой строкой.

Другой способ чтения, но без перевода новой строки:

 scanf(" 49%[^\n]", names[i]);

49 позволяет ограничить количество символов, записанных в массиве (я удалил 1, чтобы освободить место для нулевого символа), а пробелы перед позволяют пропустить пробелы в начале ввода (здесь пробелы означают '', но также и табуляцию, перевод строки и c). Используя этот способ, имя может содержать пробелы, что не имеет место в формате "%49s".

В любом случае, что бы вы ни использовали, вам нужно проверить, что ввод был выполнен, иначе вы не установите массив и когда вы будет использовать его позже, поведение будет неопределенным.

Когда вы сортируете свой массив, вы делаете:

strcpy(temp, names[j]);
strcpy(names[j], names[i]);
strcpy(names[i], temp);

, но вам не нужно копировать глубоко, просто поменяйте указатели так:

char * aux = names[j];

names[j] = names[i];
names[i] = aux;

В конце вы хотите освободить ресурсы, но вы делаете только free(names);, поэтому вы не освобождаете другие массивы

1 голос
/ 04 мая 2020

Как использовать reallo c для сокращения размера массива строк

Неправильное распределение

//                                v----------v s/b the size of a pointer 
// names = (char*)malloc(amount * sizeof(char));
names = malloc(sizeof *names * amount);
//             ^-----------^ Much easier to code right    

off- на -1 (или 2) ошибки

// fgets(names[i], MAX_CHARS - 1, stdin);
fgets(names[i], MAX_CHARS + 1, stdin);

// realloc((*names)[i], strlen(names[i]) * sizeof(char));
realloc((*names)[i], (strlen(names[i]) + 1)* sizeof(char));

, оставляя \ n на входе

fgets(names[i], MAX_CHARS - 1, stdin);
// add
names[i][strcspn(names[i], "\n")] = '\0'; // to lop off potential \n

Потенциальный UB с несоответствующим printf спецификатор

// printf("%d", strlen(names[i]));
printf("%zu", strlen(names[i]));

Невозможность освободить выделения

// add before `free(names);`
for (i=0; i<amount; i++) free(names[i]);

Неэффективная сортировка

Код поменяет имена, когда только указатели на имена нуждаются в замене. Также рассмотрим qsort()


Рекомендуемый средний код, который не включает детали сортировки. Рекомендую сортировать после того, как все имена введены.

// Allocating space for the names.

// No need to allocate, a simple array will do.
// Let us double it size to help detect and consume long-ish names
char temp[MAX_CHARS * 2];

names = malloc(sizeof *names * amount);
if (names == NULL) Handle_OutOfMemory();

// Getting the names from the user
for (i = 0; i < amount; i++) {
    printf("Enter name of friend %d: ", i + 1);
    if (fgets(temp, sizeof temp, stdin)) {
      Handle_Unexpected_Eary_EOF();
    }
    temp[strcspn(temp, "\n")] = '\0'; // lop off potential \n 
    size_t len = strlen(temp);
    if (len > MAX_CHARS) Handle_LongLine();

    names[i] = malloc(len + 1);  // only allocated what is needed
    if (names[i] == NULL) Handle_OutOfMemory();
    strcpy(name[i], temp);
}

for (i = 0; i < amount; i++) {
    printf("<%s>\n", names[i]);
}

// Sort by your own code or take time to learn `qsort()`
qsort(names, amount, sizeof names[0], TBD_compare_function);

for (i = 0; i < amount; i++) {
    printf("<%s>\n", names[i]);
    free(names[i]);
}
free(names);
0 голосов
/ 03 мая 2020
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#define SIZE 50
#define NEWSIZE 25

int main(void)
{
        char *str = malloc(SIZE);

        /* now there are 25 bytes
         * allocated for str, unless
         * an error occurs
         */
        void *tmp = realloc(str, NEWSIZE);
        if (!tmp) {
                perror("ERROR");
                exit(EXIT_FAILURE);
        }
        str = tmp;
        exit(EXIT_SUCCESS);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...