c - 使用结构更新指针进行重新分配时出现问题
问题描述
我正在尝试创建一个结构的自扩展数组。我在这里、这里和这里看到了问题,但答案似乎不适用于我的情况。
使用字符串数组使用这种技术时,我很幸运,但是使用结构不起作用。下面是代码:
// SO1.h
//
#pragma once
typedef struct {
int tag;
int type;
}structure;
void addElement(structure* Tkn_A, const int Tag);
void listArray();
这是C代码。
// SO1.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include "stdio.h"
#include "malloc.h"
#include "SO1.h"
const int ARRAY_INITIAL_SIZE = 2;
const int ARRAY_ADDITIONAL_SIZE = ARRAY_INITIAL_SIZE / 2;
structure* userArray;
size_t userArrayLength = -1;
size_t userArrayAvailable = ARRAY_INITIAL_SIZE;
int main()
{
userArray = (structure*)malloc(userArrayAvailable * sizeof(structure));
printf(" orgarrptr=%p\n", userArray);
addElement(userArray, 13);
addElement(userArray, 14);
addElement(userArray, 15);
addElement(userArray, 16);
addElement(userArray, 17);
addElement(userArray, 18);
addElement(userArray, 19);
addElement(userArray, 20);
addElement(userArray, 21);
addElement(userArray, 22);
addElement(userArray, 23);
addElement(userArray, 24);
addElement(userArray, 25);
}
void addElement(structure* userArray, const int tag)
{
userArrayLength++;
if (userArrayLength > userArrayAvailable) {
userArrayAvailable += ARRAY_ADDITIONAL_SIZE;
structure* originalUserArrayPtr = userArray;
printf(" orgarrptr=%p\n", originalUserArrayPtr);
userArray = (structure*)realloc(userArray, userArrayAvailable * sizeof(structure));
printf(" newarrptr=%p\n", userArray);
if (originalUserArrayPtr != userArray) {
printf("pointers different\n");
}
}
userArray[userArrayLength].tag = tag;
userArray[userArrayLength].type = 1;
printf("%2d %d\n\n", userArray[userArrayLength].tag,
userArray[userArrayLength].type);
listArray();
}
void listArray()
{
for (size_t i = 0; i <= userArrayLength; i++) {
printf("%2d %d\n", userArray[i].tag,
userArray[i].type);
}
}
当程序无法正常完成时,我在执行以下操作时收到以下错误realloc
:
Debug Assertion Failed!
File: minkernel\crts\ucrt\src\appcrt\heap.cpp
Line: 604
Expression _CrtIsValidHeapPointer(block)
当realloc
提出与具有新长度的原始指针不同的指针时,就会发生这种情况。但是下次再看指针时,它仍然有旧指针的值。以下是一次运行的输出:
:
:
:
13 1
14 1
-842150451 -842150451
-842150451 -842150451
-842150451 -842150451
-842150451 -842150451
-842150451 -842150451
20 1
orgarrptr=015C79E0
newarrptr=015C79E0
21 1
13 1
14 1
-842150451 -842150451
-842150451 -842150451
-842150451 -842150451
-842150451 -842150451
-842150451 -842150451
-842150451 -842150451
21 1
orgarrptr=015C79E0
newarrptr=015C5F58 <=== new pointer
pointers different
22 1
13 1
14 1
-842150451 -842150451
-842150451 -842150451
-842150451 -842150451
-842150451 -842150451
-842150451 -842150451
-842150451 -842150451
21 1
-1863261150 134281082
orgarrptr=015C79E0 <=== original pointer
exited with code 3.
有谁知道什么会导致这个问题?TIA。
很抱歉这个问题很长,但我想提供所有细节。我认为 Visual Studio 与问题没有任何关系,但不确定。我正在使用 VS Community Edition 2019, 16.1.3。
更新: 最初我有两个问题。一个更新指针,另一个获取垃圾数据。我决定将两者分成两个不同的问题。
解决方案
恕我直言,OP 还不知道变量的范围:
void addElement(structure* userArray, const int tag)
{
/* The global variable userArray is now invisible (eclipsed).
* Instead, the local parameter userArray is used.
* It has its own storage.
*/
}
因此,每当realloc()
返回一个不同的地址,然后给定它离开后就会丢失addElement()
。
realloc()
可能会回来
- 给定地址(第一个参数)
- 新地址
- 一个
NULL
指针。
内部使用的堆管理realloc()
可能会识别出块后增长的内存仍然是空闲的。因此,块可以就地生长。也可能是堆管理提供了比实际请求更大的块。(这可能是一种防止堆内存碎片的策略。)这就是为什么尽管请求的内存比以前更多,但仍可能返回相同地址的原因。
如果以上都不适用,则分配新块,将旧内容复制到新地址,并释放旧块。(否则,它将成为内存泄漏。)
如果realloc()
上述任何一项都失败,则返回NULL
.
回想一下上面的内容,情况更糟:每当realloc()
返回不同的地址时,旧地址处的内存就会被释放。因此,全局userArray
(未更新)变得悬空(指向释放的内存)。访问悬空指针是未定义的行为 → 适用于任何奇怪的影响,包括崩溃(访问冲突)。
关于第三个:
userArray = (structure*)realloc(userArray, userArrayAvailable * sizeof(structure));
可能会出现问题。
(我个人对此的想法是:当超过内存时,这意味着通常情况非常糟糕。进程通常必须尽快终止。这将在大多数平台上发生,当NULL
访问指针时忽略丑陋的事实系统错误消息可能会吓到用户并迫使她/他拨打支持电话。请注意我重复使用“通常”...)
OP 已经在诊断输出中观察到第 1 点和第 2 点。
有多种选项可以解决此问题:
- 摆脱全局变量 eclipsing:
void addElement(const int tag)
{
/* unmodified code will now access the global userArray */
}
- 改用指向指针的指针:
void addElement(structure **userArray, const int tag)
{
/* all uses of userArray in code have to get an additional indirection operator
* i.e. replace all occurrences of "userArray" by "(*userArray)"
*/
}
- 返回(可能更改)
userArray
作为结果:
structure* addElement(structure *userArray, const int tag)
{
/* unmodified code */
return userArray;
}
现在必须调用:
userArray = addElement(userArray, 13);
将(可能更改的)指针重新分配给全局变量。
我(个人)总体上更喜欢设计3。但是,考虑到addElement()
访问各种其他全局变量以及选项 1 在我看来是最一致的修复。
推荐阅读
- typescript - 使用并发承诺填充记录
- ios - 在 iOS / Swift 中添加到现有的 UINavigationController
- swiftui - SwiftUI:有条件的 onDelete
- python - 变量以字符串形式出现;TypeError:abs()的错误操作数类型:'str'
- python - 使用列表更新嵌套字典时的奇怪行为
- scala - Scala 3 中还存在 volatile 类型吗?
- sql - 在 Firebird/SQLite 数据库中为字符串创建“校验和”
- ruby-on-rails - 如何摆脱用 Prawn 生成的 PDF 中奇怪的方框字符?
- python - 运行 Python 模块 gfootball 时的错误消息
- javascript - 当滚动位置高于第一个锚链接时删除类