不能重新分配双指针动态内存
cannot realloc a double pointer dynamic memory
我试图在另一个 method/function 中增加一个双指针缓冲区。但是,分配的缓冲区的大小不会改变。这是我试过的代码。
#include <stdio.h>
#include <stdlib.h>
void change_buffer_size(char ** buffer)
{
*buffer = realloc(*buffer, sizeof(char) * 2);
}
int main()
{
char **buffer = malloc(sizeof(char) * 1);
*buffer[0] = 'H';
change_buffer_size(buffer);
*buffer[1] = 'i';
printf("%s\n", *buffer);
return 0;
}
我收到 Segmentation fault (core dumped)
错误。
char **buffer = malloc(sizeof(char) * 1);
这是错误的。您现在有一个 char**
,所以它指向的是一个 char*
,但它的空间只够 char
。你也只分配了一层双指针。
*buffer[0] = 'H';
由于上述问题,此行导致段错误。它正在尝试写入未定义位置的内存。
解决此问题的最佳方法是正常分配第一层,必要时使用 &
,第二层仅使用 malloc
。
此外,%s
直到看到空字节才会停止写入,因此您需要分配并写入其中一个。以下是解决所有问题的方法:
#include <stdio.h>
#include <stdlib.h>
void change_buffer_size(char ** buffer)
{
*buffer = realloc(*buffer, sizeof(char) * 3);
}
int main()
{
char *buffer = malloc(sizeof(char) * 1);
buffer[0] = 'H';
change_buffer_size(&buffer);
buffer[1] = 'i';
buffer[2] = '[=12=]';
printf("%s\n", buffer);
return 0;
}
@JosephSible 您已经有了一个很好的答案,但是关于 realloc
的使用还有一点需要说明。我在原始问题下的评论中提到了这一点。使用 realloc
时,您必须始终 realloc
使用 临时 指针来捕获 realloc
的 return。
为什么?当(不是如果)realloc
失败时,它 returns NULL
。如果您将 return 直接分配给您尝试为其重新分配的指针,则会覆盖指向现有内存块的指针,并且 NULL
会丢失您对原始块的引用,从而造成内存泄漏。例如,您不想:
*buffer = realloc (*buffer, 2); /* sizeof(char) is always 1 */
相反,使用临时指针并在将重新分配的块分配给原始指针之前验证重新分配是否成功,例如
void * tmp = realloc (*buffer, 2);
if (!tmp) { /* validate EVERY allocation & reallocation */
perror ("realloc-*buffer");
exit (EXIT_FAILURE); /* or handle as desired, e.g return NULL, etc.. */
}
*buffer = tmp; /* now assign the reallocated block to your pointer */
对您的原作的一些评论 post。回想一下,在 C 中,字符串必须以 nul-terminating 字符结尾。您不能简单地分配 buffer[0] = 'H';
并将 buffer
视为字符串。 nul-termianting 字符('[=25=]'
,或简称 0
)必须跟在后面,因此在调用 printf("%s\n", *buffer);
[ 之前需要 buffer[1] = 0;
=38=]
避免在代码中使用 幻数。您的 change_buffer_size()
函数使用幻数 2
将重新分配硬编码为大小。 (不是很有用)。相反,至少将所需的大小作为参数传递,以便您的函数可重用,例如
char *change_buffer_size (char **buffer, size_t nchar)
{
void *tmp = realloc (*buffer, nchar * sizeof **buffer);
if (!tmp) { /* validate EVERY allocation/reallocation */
perror ("change_buffer_size()-realloc");
return NULL;
}
return *buffer = tmp; /* assign new block to *buffer, return */
}
(注意: 将 return 类型更改为 char*
允许您通过 [=98= 指示函数的 success/failure ] 以及在成功时直接访问重新分配的内存块)
现在您想将缓冲区重新分配为 2 个字符,只需将 2
作为 nchar
传递,等等。结合一个重新分配字符并将其添加到缓冲区的简短示例一次(同时确保它始终 nul-terminated)可能类似于以下内容:
#include <stdio.h>
#include <stdlib.h>
char *change_buffer_size (char **buffer, size_t nchar)
{
void *tmp = realloc (*buffer, nchar * sizeof **buffer);
if (!tmp) { /* validate EVERY allocation/reallocation */
perror ("change_buffer_size()-realloc");
return NULL;
}
return *buffer = tmp; /* assign new block to *buffer, return */
}
int main (void) {
size_t nchar = 1; /* character counter */
char *str = "hello world!", *buffer = NULL;
for (int i = 0; str[i]; i++) {
if (!change_buffer_size(&buffer, nchar + 1)) /* alloc nchar + 1 */
return 1;
buffer[nchar-1] = str[i]; /* copy char from str to buffer */
buffer[nchar++] = 0; /* nul-terminate buffer */
printf ("buffer: '%s'\n", buffer); /* print current buffer contents */
}
free (buffer); /* don't forget to free what you allocate */
}
(注意:不要忘记free()
你分配的内存。是的,这里它会在程序退出时被释放,但要早点养成良好的习惯 - - 你不会总是在 main()
)
工作
示例Use/Output
$ ./bin/realloccharbuf
buffer: 'h'
buffer: 'he'
buffer: 'hel'
buffer: 'hell'
buffer: 'hello'
buffer: 'hello '
buffer: 'hello w'
buffer: 'hello wo'
buffer: 'hello wor'
buffer: 'hello worl'
buffer: 'hello world'
buffer: 'hello world!'
内存Use/Error检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址 因此,(2) 当不再需要它时可以释放。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。
对于Linux valgrind
是正常的选择。每个平台都有类似的内存检查器。它们都很简单易用,只需运行你的程序就可以了。
$ valgrind ./bin/realloccharbuf
==19740== Memcheck, a memory error detector
==19740== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==19740== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==19740== Command: ./bin/realloccharbuf
==19740==
buffer: 'h'
buffer: 'he'
buffer: 'hel'
buffer: 'hell'
buffer: 'hello'
buffer: 'hello '
buffer: 'hello w'
buffer: 'hello wo'
buffer: 'hello wor'
buffer: 'hello worl'
buffer: 'hello world'
buffer: 'hello world!'
==19740==
==19740== HEAP SUMMARY:
==19740== in use at exit: 0 bytes in 0 blocks
==19740== total heap usage: 13 allocs, 13 frees, 1,114 bytes allocated
==19740==
==19740== All heap blocks were freed -- no leaks are possible
==19740==
==19740== For counts of detected and suppressed errors, rerun with: -v
==19740== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认您已释放所有分配的内存并且没有内存错误。
检查一下,如果您有任何问题,请告诉我。
我试图在另一个 method/function 中增加一个双指针缓冲区。但是,分配的缓冲区的大小不会改变。这是我试过的代码。
#include <stdio.h>
#include <stdlib.h>
void change_buffer_size(char ** buffer)
{
*buffer = realloc(*buffer, sizeof(char) * 2);
}
int main()
{
char **buffer = malloc(sizeof(char) * 1);
*buffer[0] = 'H';
change_buffer_size(buffer);
*buffer[1] = 'i';
printf("%s\n", *buffer);
return 0;
}
我收到 Segmentation fault (core dumped)
错误。
char **buffer = malloc(sizeof(char) * 1);
这是错误的。您现在有一个 char**
,所以它指向的是一个 char*
,但它的空间只够 char
。你也只分配了一层双指针。
*buffer[0] = 'H';
由于上述问题,此行导致段错误。它正在尝试写入未定义位置的内存。
解决此问题的最佳方法是正常分配第一层,必要时使用 &
,第二层仅使用 malloc
。
此外,%s
直到看到空字节才会停止写入,因此您需要分配并写入其中一个。以下是解决所有问题的方法:
#include <stdio.h>
#include <stdlib.h>
void change_buffer_size(char ** buffer)
{
*buffer = realloc(*buffer, sizeof(char) * 3);
}
int main()
{
char *buffer = malloc(sizeof(char) * 1);
buffer[0] = 'H';
change_buffer_size(&buffer);
buffer[1] = 'i';
buffer[2] = '[=12=]';
printf("%s\n", buffer);
return 0;
}
@JosephSible 您已经有了一个很好的答案,但是关于 realloc
的使用还有一点需要说明。我在原始问题下的评论中提到了这一点。使用 realloc
时,您必须始终 realloc
使用 临时 指针来捕获 realloc
的 return。
为什么?当(不是如果)realloc
失败时,它 returns NULL
。如果您将 return 直接分配给您尝试为其重新分配的指针,则会覆盖指向现有内存块的指针,并且 NULL
会丢失您对原始块的引用,从而造成内存泄漏。例如,您不想:
*buffer = realloc (*buffer, 2); /* sizeof(char) is always 1 */
相反,使用临时指针并在将重新分配的块分配给原始指针之前验证重新分配是否成功,例如
void * tmp = realloc (*buffer, 2);
if (!tmp) { /* validate EVERY allocation & reallocation */
perror ("realloc-*buffer");
exit (EXIT_FAILURE); /* or handle as desired, e.g return NULL, etc.. */
}
*buffer = tmp; /* now assign the reallocated block to your pointer */
对您的原作的一些评论 post。回想一下,在 C 中,字符串必须以 nul-terminating 字符结尾。您不能简单地分配 buffer[0] = 'H';
并将 buffer
视为字符串。 nul-termianting 字符('[=25=]'
,或简称 0
)必须跟在后面,因此在调用 printf("%s\n", *buffer);
[ 之前需要 buffer[1] = 0;
=38=]
避免在代码中使用 幻数。您的 change_buffer_size()
函数使用幻数 2
将重新分配硬编码为大小。 (不是很有用)。相反,至少将所需的大小作为参数传递,以便您的函数可重用,例如
char *change_buffer_size (char **buffer, size_t nchar)
{
void *tmp = realloc (*buffer, nchar * sizeof **buffer);
if (!tmp) { /* validate EVERY allocation/reallocation */
perror ("change_buffer_size()-realloc");
return NULL;
}
return *buffer = tmp; /* assign new block to *buffer, return */
}
(注意: 将 return 类型更改为 char*
允许您通过 [=98= 指示函数的 success/failure ] 以及在成功时直接访问重新分配的内存块)
现在您想将缓冲区重新分配为 2 个字符,只需将 2
作为 nchar
传递,等等。结合一个重新分配字符并将其添加到缓冲区的简短示例一次(同时确保它始终 nul-terminated)可能类似于以下内容:
#include <stdio.h>
#include <stdlib.h>
char *change_buffer_size (char **buffer, size_t nchar)
{
void *tmp = realloc (*buffer, nchar * sizeof **buffer);
if (!tmp) { /* validate EVERY allocation/reallocation */
perror ("change_buffer_size()-realloc");
return NULL;
}
return *buffer = tmp; /* assign new block to *buffer, return */
}
int main (void) {
size_t nchar = 1; /* character counter */
char *str = "hello world!", *buffer = NULL;
for (int i = 0; str[i]; i++) {
if (!change_buffer_size(&buffer, nchar + 1)) /* alloc nchar + 1 */
return 1;
buffer[nchar-1] = str[i]; /* copy char from str to buffer */
buffer[nchar++] = 0; /* nul-terminate buffer */
printf ("buffer: '%s'\n", buffer); /* print current buffer contents */
}
free (buffer); /* don't forget to free what you allocate */
}
(注意:不要忘记free()
你分配的内存。是的,这里它会在程序退出时被释放,但要早点养成良好的习惯 - - 你不会总是在 main()
)
示例Use/Output
$ ./bin/realloccharbuf
buffer: 'h'
buffer: 'he'
buffer: 'hel'
buffer: 'hell'
buffer: 'hello'
buffer: 'hello '
buffer: 'hello w'
buffer: 'hello wo'
buffer: 'hello wor'
buffer: 'hello worl'
buffer: 'hello world'
buffer: 'hello world!'
内存Use/Error检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址 因此,(2) 当不再需要它时可以释放。
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。
对于Linux valgrind
是正常的选择。每个平台都有类似的内存检查器。它们都很简单易用,只需运行你的程序就可以了。
$ valgrind ./bin/realloccharbuf
==19740== Memcheck, a memory error detector
==19740== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==19740== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==19740== Command: ./bin/realloccharbuf
==19740==
buffer: 'h'
buffer: 'he'
buffer: 'hel'
buffer: 'hell'
buffer: 'hello'
buffer: 'hello '
buffer: 'hello w'
buffer: 'hello wo'
buffer: 'hello wor'
buffer: 'hello worl'
buffer: 'hello world'
buffer: 'hello world!'
==19740==
==19740== HEAP SUMMARY:
==19740== in use at exit: 0 bytes in 0 blocks
==19740== total heap usage: 13 allocs, 13 frees, 1,114 bytes allocated
==19740==
==19740== All heap blocks were freed -- no leaks are possible
==19740==
==19740== For counts of detected and suppressed errors, rerun with: -v
==19740== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认您已释放所有分配的内存并且没有内存错误。
检查一下,如果您有任何问题,请告诉我。