如何使用 realloc 来缩短字符串数组的大小

How to use realloc to shorten a string array size

我尝试获取某个字符串,将字符串设置为特定大小,即 50 个字符,然后在输入所有字符串后,它将对它们进行排序,然后将大小从 50 个字符重新分配到用户写入的字符串的长度,如果一开始我给它 50 个字节,然后有人输入 "hi" 它将更改为所需的字节数。

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

names是指向char的指针数组,所以in

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

你分配的不够多,以后你分配出去的时候行为会不确定

做(演员没用)

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

不检查 mallocrealloc 的结果你 suppose/hope 有足够的内存,但这可能是错误的,在那种情况下这些函数 returns NULL,检查这种情况更安全。这意味着 realloc 首先保存在辅助 char* 中不丢失 names[i] 的当前值你可以继续使用 if realloc returns NULL

要做

scanf("%d", &amount);

是危险的,当输入无效时你不知道并且 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] 中,您可能需要将其删除。在这种情况下,您必须在打印名称时进行调整以在名称之间引入分隔符,space 或换行符。

另一种阅读但没有换行的方法是:

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

49 允许限制写入数组的字符数(我删除了 1 让 space 用于空字符),而之前的 spaces 允许绕过 spaces 在输入的开头(这里 spaces 表示 ' ',但也表示制表符、换行符等)。使用这种方式,名称可以包含 spaces,而格式 "%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); 所以你没有释放其他数组

#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);
}

How to use realloc to shorten a string array size

分配错误

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

关闭 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")] = '[=12=]'; // to lop off potential \n

具有不匹配的 printf 说明符的潜在 UB

// 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")] = '[=15=]'; // 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);