提问人:Virgil G. 提问时间:2/16/2023 最后编辑:Virgil G. 更新时间:2/17/2023 访问量:194
编写我自己的 realloc() 实现时出错
Error when coding my own implementation of realloc()
问:
我正在联系您,因为我需要编写函数 /(有符号和无符号)/ 。
我已经重新编码了这些函数,但它不起作用,现在我有 valgrind 错误,例如 .
你能帮我解决这个问题吗?realloc
strlen
memcpy
Conditional jump or move depends on uninitialised value(s)
以下是功能:
void *my_realloc(void *ptr, size_t size)
{
unsigned char *old_ptr = (unsigned char *)ptr;
void *new_ptr = NULL;
size_t old_size = 0;
if (!ptr) {
return malloc(size);
}
if (size == 0) {
free(ptr);
return NULL;
}
old_size = my_strlen_unsigned(old_ptr) + 1;
new_ptr = malloc(size);
if (!new_ptr) {
return NULL;
}
my_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
free(ptr);
return new_ptr;
}
void *my_memcpy(void *restrict dest, const void *restrict src, size_t n)
{
if (!dest || !src) return NULL;
unsigned char *d = dest;
const unsigned char *s = src;
size_t i = 0;
while (i < n && i < my_strlen_unsigned(s)) {
*d = *s;
d++;
s++;
i++;
}
return dest;
}
size_t my_strlen_unsigned(const unsigned char *s)
{
size_t count = 0;
if (s != NULL) {
while (*s != 0) {
count++;
s++;
}
}
return count;
}
size_t my_strlen(const char *s)
{
size_t count = 0;
if (s != NULL) {
while (*s != 0) {
count++;
s++;
}
}
return count;
}
目前,我通过以下函数测试这些函数:
char *my_str_clean(char *str)
{
char *ptr = str;
char *new_str = malloc(1);
size_t i = 0;
if (!new_str)
return (NULL);
while (*ptr) {
if (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
new_str = my_realloc(new_str, (sizeof(char) * (i + 2)));
new_str[i] = *ptr;
i++;
}
ptr++;
}
new_str[i] = '\0';
free(str);
return (new_str);
}
int main(int argc, char **argv, char **env)
{
char *test = malloc(15);
test[0] = 'l';
test[1] = 's';
test[2] = ' ';
test[3] = ' ';
test[4] = ' ';
test[5] = ' ';
test[6] = ' ';
test[7] = ' ';
test[8] = ' ';
test[9] = '-';
test[10] = 'l';
test[11] = ' ';
test[12] = '-';
test[13] = 'a';
test[14] = '\0';
char *clean = NULL;
clean = my_str_clean(test);
printf("%s\n", clean);
free(clean);
}
这是valgrid报告:
==28637== Memcheck, a memory error detector
==28637== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==28637== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==28637== Command: ./mysh
==28637==
==28637== Conditional jump or move depends on uninitialised value(s)
==28637== at 0x109BB2: my_strlen_unsigned (my_strlen_unsigned.c:14)
==28637== by 0x109B2B: my_realloc (my_realloc.c:35)
==28637== by 0x1096B5: my_str_clean (my_str_clean.c:19)
==28637== by 0x10954A: main (minishell.c:55)
==28637== Uninitialised value was created by a heap allocation
==28637== at 0x4841888: malloc (vg_replace_malloc.c:381)
==28637== by 0x109660: my_str_clean (my_str_clean.c:13)
==28637== by 0x10954A: main (minishell.c:55)
==28637==
==28637== Conditional jump or move depends on uninitialised value(s)
==28637== at 0x109BB2: my_strlen_unsigned (my_strlen_unsigned.c:14)
==28637== by 0x109ABC: my_memcpy (my_memcpy.c:16)
==28637== by 0x109B73: my_realloc (my_realloc.c:40)
==28637== by 0x1096B5: my_str_clean (my_str_clean.c:19)
==28637== by 0x10954A: main (minishell.c:55)
==28637== Uninitialised value was created by a heap allocation
==28637== at 0x4841888: malloc (vg_replace_malloc.c:381)
==28637== by 0x109660: my_str_clean (my_str_clean.c:13)
==28637== by 0x10954A: main (minishell.c:55)
==28637==
==28637== Conditional jump or move depends on uninitialised value(s)
==28637== at 0x1097A5: my_strlen (my_strlen.c:18)
==28637== by 0x10962C: my_put_str (my_put_str.c:21)
==28637== by 0x10955F: main (minishell.c:56)
==28637== Uninitialised value was created by a heap allocation
==28637== at 0x4841888: malloc (vg_replace_malloc.c:381)
==28637== by 0x109B3F: my_realloc (my_realloc.c:36)
==28637== by 0x1096B5: my_str_clean (my_str_clean.c:19)
==28637== by 0x10954A: main (minishell.c:55)
==28637==
l==28637==
==28637== HEAP SUMMARY:
==28637== in use at exit: 0 bytes in 0 blocks
==28637== total heap usage: 8 allocs, 8 frees, 43 bytes allocated
==28637==
==28637== All heap blocks were freed -- no leaks are possible
==28637==
==28637== For lists of detected and suppressed errors, rerun with: -s
==28637== ERROR SUMMARY: 18 errors from 3 contexts (suppressed: 0 from 0)
答:
你假设块中的数据是正确终止的 C 字符串,并用于计算旧大小。你不能这样做,因为你不知道块中存储了什么样的数据。唯一真正的解决方案是以某种方式记住块大小,并将其作为参数传递给您的函数。my_strlen_unsigned()
如果无法执行此操作,则可能还有其他方法可以解决此问题。例如,malloc_usable_size(
) 函数是 glibc (GNU libc) 中提供的 GNU 扩展,可用于查询现有块的大小,因此您可以使用它来代替 .但请注意,如果您使用动态链接(编译时的默认设置),这将使您的程序不可移植,并且只能在使用 glibc 的系统上运行。在这种情况下,您可能希望静态链接您的程序。my_strlen_unsigned()
假设代码中的其他函数(如 )已正确实现,则 的正确实现如下:my_memcpy()
my_realloc()
void *my_realloc(void *ptr, size_t size)
{
void *new_ptr;
size_t old_size;
if (!ptr)
return malloc(size);
if (size == 0) {
free(ptr);
return NULL;
}
new_ptr = ptr;
old_size = malloc_usable_size(ptr);
if (size != old_size) {
new_ptr = malloc(size);
if (!new_ptr)
return NULL;
my_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
free(ptr);
}
return new_ptr;
}
无效的假设:
void *my_realloc(void *ptr, size_t size)
{
unsigned char *old_ptr = (unsigned char *)ptr;
你在这里违反了规范。 不假定 将始终指向字符串。它与类型无关。realloc()
realloc()
ptr
而且演员阵容是多余的。存在从到任何其他指针类型的隐式转换。void *
未定义的行为:
old_size = my_strlen_unsigned(old_ptr) + 1;
标准或您的版本适用于字符串,即假定指针指向以 null 字节结尾的数组。s 数组不会以 null 字节结尾。因此,你的调用了未定义的行为。my_strlen_unsigned
char
int
my_realloc()
可能的修复:
您无法确定 和 重新实现 的旧大小,而至少不能重新实现您自己的 和 。但是您可以将旧尺寸作为第三个参数。(但标准只需要两个。那么这会是一个符合要求的实现吗?ptr
realloc()
malloc()
free()
realloc()
这是 glibc 对它的实现:malloc().c
这是它的 musl C 实现:malloc().c
这是我的玩具示例,它试图在某种程度上模仿该标准:realloc()
/**
* @brief The my_realloc() function shall deallocate the old object pointed to
* by ptr and return a pointer to a new object that has the size specified by new_size.
*
* @param ptr - A pointer to a block of memory to resize.
* @param old_size - The size of the block pointed to by ptr.
* @param new_size - The size to resize by.
*
* If ptr is a null pointer, my_realloc() shall be equivalent to
* malloc() for the specified new_size.
*
* If ptr does not match a pointer returned earlier by calloc(),
* malloc(), or realloc() or if the space has previously been
* deallocated by a call to free() or realloc(), the behavior is undefined.
*
* @return Upon successful completion, my_realloc() shall return a pointer to the moved allocated space.
* If size and ptr both evaluate to 0, my_realloc() shall return a
* NULL pointer with errno set to [EINVAL].
* If there is not enough available memory, my_realloc() shall return a
* NULL pointer and set errno to [ENOMEM].
*
* If my_realloc() returns a NULL pointer, the memory referenced by ptr shall not be changed.
*
* @warning my_realloc() may return NULL to indicate an error. For that reason, a different pointer variable
* must be used to hold it's return value. Otherwise, you risk overwriting the original ptr with NULL and
* losing your only reference to the original block of memory.
*/
void *my_realloc (void *ptr, size_t new_size, size_t old size)
{
if (!ptr) {
return malloc (new_size);
}
if (!new_size) {
errno = EINVAL;
return 0;
}
if (new_size <= old_size) {
return ptr;
}
/* As a last resort, allocate a new chunk and copy to it.
*/
void *new = 0;
if (new_size > old_size) {
new = malloc (new_size);
if (!new) {
return 0;
}
memcpy (new, ptr, old_size);
free (ptr);
}
return new;
}
您还可以在 K&R 的第 8 章中找到示例实现。
旁注:
char *new_str = malloc(1);
new_str = my_realloc(..);
您可能会失去对通过此处分配的原始内存的访问。如果返回 ,将被分配其结果,并且会导致程序内存泄漏。malloc()
my_realloc()
NULL
new_str
此外,返回的内存是未初始化的。代码通过调用未初始化的指针来调用未定义的行为。因此发出警告。malloc()
my_strlen()
my_realloc()
评论
my_strlen_unsigned
unsigned char *
my_strlen_unsigned
malloc
malloc
malloc_usable_size()