提问人:Windroz 提问时间:9/18/2023 最后编辑:Vlad from MoscowWindroz 更新时间:9/20/2023 访问量:65
Realloc 给出错误:_CrtIsValidHeapPointer(block)
Realloc gives error: _CrtIsValidHeapPointer(block)
问:
我有这个学校作业,我们得到了一些代码来修改。我遇到的部分是截断动态分配的字符串。我们正在编写一个函数,其中我们接受指向字符串的指针和字符串的新长度(作为 int)。
void dstring_truncate(DString* destination, unsigned int truncatedLength)
{
assert(destination != NULL);
assert(*destination != NULL);
assert(truncatedLength > 0);
DString temp = (DString)malloc(strlen(*destination) * sizeof(char));
strcpy(temp, *destination);
printf("\n%s test dstring truncate\n", temp);
temp[truncatedLength] = '\0';
temp = (DString)realloc(temp, truncatedLength + 1);
*destination[truncatedLength] = '\0';
*destination = (DString)realloc(*destination, truncatedLength + 1);
printf("%s test dstring truncate after realloc\n", *destination);
}
对函数的调用看起来像是 DString 是我们正在使用的 ADTdstring_truncate(&str1, 10);
typedef char* DString;
此外,程序中的第一个函数初始化字符串DString dstring_initialize(const char* str);
我假设我不理解指针,因为上面代码中的“Temp”按预期工作,但“*destination”没有。
我尝试使用“destination”、“*destination”、“**destination”,我已经重新观看了给出的材料和一些在线 youtube/文档,但我就是无法解决这个问题。
另外,这就是我编写初始化代码的方式,也许这里有什么问题?
DString dstring_initialize(const char* str)
{
assert(str != NULL);
DString arr = (DString)malloc((strlen(str) + 1) * sizeof(char));
if (arr == NULL) {
printf("Memory allocation Failed");
return NULL;
}
strcpy(arr, str);
if (strcmp(str, arr) == 0)
{
return arr; // Ersatt denna rad. Den ar just nu med for att programmet ska kompilera
}
}
答:
当您超出了以前分配的内存的范围时,该错误是典型的。
就像你这样做的时候一样
strcpy(temp, *destination);
不为 null 终止符分配空间。
请记住,这不计算 null 终止符。strlen
快速修复:
DString temp = malloc(strlen(*destination) + 1);
添加我的评论中提到的一些部分,我会推荐这样的功能:
DString dstring_truncate(DString* destination, size_t truncated_length)
{
if (destination == NULL || *destination == NULL)
{
// No source/destination
return NULL;
}
size_t original_length = strlen(*destination);
if (truncated_length == original_length)
{
// No operation
}
else if (truncated_length > original_length)
{
// Try to make it longer? That's not truncation. No operation
}
else
{
// Truncate by reallocation
// Note that this assumes the original buffer was allocated dynamically as well
DString temp = realloc(*destination, truncated_length + 1);
if (temp == NULL)
{
// Allocation failed, keep the old buffer but return as an error
return NULL;
}
// Set the null-terminator
temp[truncated_length] = '\0';
// And finally reassign the destination
*destination = temp;
}
// Return the (possibly new) string
return *destination;
}
此行中存在运算符优先级错误:
*destination[truncatedLength] = '\0';
它应该是:
(*destination)[truncatedLength] = '\0';
解释:后缀运算符(如数组下标运算符)比一元运算符(如一元(间接)运算符)绑定得更紧密,所以实际上与 or 或相同,这不是您想要的。这将被视为指向数组的第一个元素,并将该数组的第 th 个元素设置为 。将被视为空指针常量,因此它将数组的第 th 个元素设置为空指针。不幸的是,它实际上指向的是单个,而不是数组的第一个元素,导致在 时出现未定义的行为。在两边加上括号意味着一元运算符应用于 而不是应用于 。 等价于 或 ,这是您想要的。(与 .)[
]
*
*destination[truncatedLength] = '\0';
*(destination[truncatedLength]) = '\0';
*(*(destination + truncatedLength)) = '\0';
**(destination + truncatedLength) = '\0'
destination
DString
truncatedLength
'\0'
'\0'
truncatedLength
DString
destination
DString
DString
truncatedLength > 0
*destination
*
destination
destination[truncatedLength]
(*destination)[truncatedLength = '\0';
*((*destination) + truncatedLength) = '\0';
*(*destination + truncatedLength) = '\0';
**(destination + truncatedLength)
*(*destination + truncatedLength)
该函数假定 .如果要在调试期间断言条件为 true,可以添加调用:truncatedLength <= strlen(*destination)
assert
assert(truncatedLength <= strlen(*destination));
(请注意,如果定义了宏,则调用将不起作用。assert
NDEBUG
或者,在以下情况下,可以将函数编写为不执行任何操作:truncatedLength >= strlen(*destination)
if (truncatedLength >= strlen(*destination))
{
return;
}
(*destination)[truncatedLength] = '\0';
允许将字符串截断为零长度似乎是合理的,但现有的不允许这样做。assert(truncatedLength > 0);
调用 可能会返回 。在缩小分配块的大小时,这不太可能发生,但在技术上是可行的。在这种情况下,原始块仍然有效,并且由于它足够大,因此可以保留它。仅当调用成功时,以下代码才会替换:realloc()
NULL
*destination
realloc()
DString tempdest = realloc(*destination, truncatedLength + 1);
if (tempdest != NULL)
{
*destination = tempdest;
}
请注意,不需要强制转换 返回的值,或者因为 会自动将右侧的值转换为 左侧的指针对象的类型。void *
malloc()
calloc()
realloc()
=
void *
=
在使用 of 作为比例因子时存在一些不一致之处,因此上述代码可以更改为:sizeof(char)
DString tempdest = realloc(*destination, (truncatedLength + 1) * sizeof(char));
if (tempdest != NULL)
{
*destination = tempdest;
}
请注意,根据定义,它是 1,因此可以删除所有这些因素。(对于适用于 的版本,需要保留这些因素。sizeof(char)
* sizeof(char)
wchar_t
* sizeof(wchar_t)
变量的实验也存在问题。在这些行中:temp
DString temp = (DString)malloc(strlen(*destination) * sizeof(char));
strcpy(temp, *destination);
第一行没有为 null 终止符分配足够的空间,因此第二行写入了分配的内存的末尾。指向的内存永远不会被释放,所以这也是一个内存泄漏错误,但我理解这只是一个实验。temp
把所有这些放在一起并删除实验,我们最终可能会得到这样的东西:temp
void dstring_truncate(DString* destination, unsigned int truncatedLength)
{
assert(destination != NULL);
assert(*destination != NULL);
if (truncatedLength >= strlen(*destination))
{
return;
}
DString tempdest = realloc(*destination, truncatedLength + 1);
if (tempdest != NULL)
{
*destination = tempdest;
}
(*destination)[truncatedLength] = '\0';
}
在 中,并非所有通过函数的路径都返回一个值,但由于函数末尾的条件始终为 true,因此无论如何都不会到达该语句后面的代码。没有必要在最后语句中调用 。只需在通话后返回:dstring_initialize()
if
if
if
strcmp
arr
strcpy()
DString dstring_initialize(const char* str)
{
assert(str != NULL);
DString arr = malloc(strlen(str) + 1);
if (arr == NULL) {
printf("Memory allocation Failed");
return NULL;
}
strcpy(arr, str);
return arr;
}
在函数中按原样使用 varable 是没有意义的,因为至少它会产生内存泄漏。我认为您包含带有变量 temporary 的代码仅用于测试目的。temp
temp
然而,这部分代码在此语句中包含一个错误
DString temp = (DString)malloc(strlen(*destination) * sizeof(char));
您没有为创建的字符串的终止零字符保留内存。因此,这句话'\0'
strcpy(temp, *destination);
覆盖超出已导致未定义行为的已分配动态内存范围的内存。
本声明
temp[truncatedLength] = '\0';
在逻辑上也是不正确的,因为您没有检查的值是否确实不大于源字符串的长度。在重新分配字符串之前,您需要检查 的值是否小于源字符串的 currect 长度。truncatedLength
truncatedLength
顺便说一句,变量(函数参数)应该有 type 而不是 . 是函数的返回类型,也是在标准内存分配函数中指定大小的参数类型。truncatedLength
size_t
unsigned int
size_t
strlen
在此声明中
*destination[truncatedLength] = '\0';
使用无效的运算符顺序。相反,你需要写
( *destination )[truncatedLength] = '\0';
同样,您需要首先检查它是否不大于源字符串的长度。truncatedLength
该函数可以返回一个 null 指针。在这种情况下,您将发生内存泄漏,因为指针的值将在此语句中被覆盖realloc
*destination
*destination = (DString)realloc(*destination, truncatedLength + 1);
您需要使用一个中间变量,函数的返回值将被分配给该中间变量。realloc
并且您应该向函数的调用者报告重新分配是否成功。因此,最好将函数的返回类型声明为 而不是 。int
void
下面是一个演示程序,展示了如何更新函数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
typedef char *DString;
DString dstring_initialize( const char *str )
{
assert( str != NULL );
DString arr = malloc( strlen( str ) + 1 );
if ( arr == NULL)
{
puts( "Memory allocation Failed" );
}
else
{
strcpy( arr, str );
}
return arr;
}
int dstring_truncate( DString *destination, size_t truncatedLength )
{
assert( destination != NULL );
assert( *destination != NULL );
assert( truncatedLength > 0 );
size_t n = strlen( *destination );
int success = truncatedLength < n;
if ( success )
{
DString temp = realloc( *destination, truncatedLength + 1 );
success = temp != NULL;
if (success)
{
*destination = temp;
( *destination )[truncatedLength] = '\0';
}
}
return success;
}
int main( void )
{
const char *s = "Hello Wordl!";
DString ds = dstring_initialize( s );
if ( ds != NULL )
{
int success = dstring_truncate( &ds, strchr( ds, ' ' ) - ds );
if (success)
{
printf( "Now after truncation the string is \"%s\"\n", ds );
}
free( ds );
}
}
程序输出为
Now after truncation the string is "Hello"
实际上,该函数不应发出任何消息。它是函数的调用者根据被调用函数的返回值来决定是否发出消息。因此,该函数应如下所示dstring_initialize
DString dstring_initialize( const char *str )
{
assert( str != NULL );
DString arr = malloc( strlen( str ) + 1 );
if ( arr != NULL)
{
strcpy( arr, str );
}
return arr;
}
评论
malloc
或realloc
或任何其他返回 .此外,不要重新分配回传递到的指针。如果失败,它将返回一个空指针,但保留旧内存。如果重新分配回同一指针,则会丢失原始内存。而且我没有看到您的代码进行任何错误检查或验证(如果它们相等,则无需执行任何操作)。void *
realloc
realloc
truncatedLength <= strlen(*destination)
strcmp
dstring_initialize
strcpy
strcmp
return