【问题标题】:How to use realloc to shorten a string array size如何使用 realloc 来缩短字符串数组的大小
【发布时间】:2020-05-03 20:01:41
【问题描述】:

我尝试取某个字符串,将字符串设置为某个大小,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;
}

【问题讨论】:

  • 你有什么问题?
  • @Meir Biton 从这个名字开始 = (char*)malloc(amount * sizeof(char));代码无效。

标签: c malloc realloc


【解决方案1】:

names 是一个指向 char 的指针数组,所以在

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 的结果,您会假设/希望有足够的内存,但这可能是错误的,在这种情况下这些函数返回 NULL,检查这种情况会更安全。这意味着realloc 首先保存在辅助char* 中以不丢失names[i] 的当前值,如果realloc 返回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] 中,您可能需要将其删除。在这种情况下,您必须在打印名称时进行调整,以便在名称之间引入分隔符、空格或换行符。

另一种阅读方式是:

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

49 允许限制写入数组中的字符数(我删除了 1 以便为空字符留出空格),并且前面的空格允许绕过输入开头的空格(这里 空格 表示“”,但也表示制表符、换行符等)。使用这种方式,名称可以包含空格,而"%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);所以你不释放其他数组

【讨论】:

  • 从技术上讲,realloc() 可以返回 NULL,即使在缩小块时也是如此。 names[i] = realloc(names[i], strlen(names[i]) + 1); 在这种情况下会导致内存泄漏。将返回值存储到指针并测试NULL 是大多数realloc() 使用的首选模式。
  • 当我尝试重新分配时,它会触发一个断点。照你说的做了
  • @MeirBiton 您还需要修复第一个错误的malloc,而不仅仅是realloc
  • 旁白:“names 是一个指向 char 的指针数组” --> char **names 是一个指向 char 的指针。 names 不是数组。
  • "另一种读取方式但不获取换行符是:scanf(" 49%[^\n]", names[i]);" --> 当输入行只有 "\n" 时,这会产生麻烦。
【解决方案2】:

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

分配错误

//                                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")] = '\0'; // 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")] = '\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);

【讨论】:

    【解决方案3】:
    #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);
    }
    

    【讨论】:

    • 建议void *tmp = realloc (str, 25); if (!void) { /* handle error */ }; str = tmp; 这样可以避免realloc返回NULL失败时丢失原始指针。
    • 为了简单起见,我避免了这种情况,但没关系。
    • 当然,不,在退出的情况下无论如何 - 它并没有真正的区别。但在一般情况下展示适当的做法总是很好的,以涵盖 OP 将您的解决方案放入函数中。
    猜你喜欢
    • 2020-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-08
    • 2012-02-07
    • 2014-11-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多