首页 > 解决方案 > 使用结构更新指针进行重新分配时出现问题

问题描述

我正在尝试创建一个结构的自扩展数组。我在这里这里这里看到了问题,但答案似乎不适用于我的情况。

使用字符串数组使用这种技术时,我很幸运,但是使用结构不起作用。下面是代码:

//  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。

更新: 最初我有两个问题。一个更新指针,另一个获取垃圾数据。我决定将两者分成两个不同的问题。

标签: cvisual-studio

解决方案


恕我直言,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()可能会回来

  1. 给定地址(第一个参数)
  2. 新地址
  3. 一个NULL指针。

内部使用的堆管理realloc()可能会识别出块后增长的内存仍然是空闲的。因此,块可以就地生长。也可能是堆管理提供了比实际请求更大的块。(这可能是一种防止堆内存碎片的策略。)这就是为什么尽管请求的内存比以前更多,但仍可能返回相同地址的原因。

如果以上都不适用,则分配新块,将旧内容复制到新地址,并释放旧块。(否则,它将成为内存泄漏。)

如果realloc()上述任何一项都失败,则返回NULL.

回想一下上面的内容,情况更糟:每当realloc()返回不同的地址时,旧地址处的内存就会被释放。因此,全局userArray(未更新)变得悬空(指向释放的内存)。访问悬空指针是未定义的行为 → 适用于任何奇怪的影响,包括崩溃(访问冲突)。

关于第三个:

userArray = (structure*)realloc(userArray, userArrayAvailable * sizeof(structure));

可能会出现问题。

(我个人对此的想法是:当超过内存时,这意味着通常情况非常糟糕。进程通常必须尽快终止。这将在大多数平台上发生,当NULL访问指针时忽略丑陋的事实系统错误消息可能会吓到用户并迫使她/他拨打支持电话。请注意我重复使用“通常”...)

OP 已经在诊断输出中观察到第 1 点和第 2 点。

有多种选项可以解决此问题:

  1. 摆脱全局变量 eclipsing:
void addElement(const int tag)
{
  /* unmodified code will now access the global userArray */
}
  1. 改用指向指针的指针:
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)"
   */
}
  1. 返回(可能更改)userArray作为结果:
structure* addElement(structure *userArray, const int tag)
{
  /* unmodified code */
  return userArray;
}

现在必须调用:

userArray = addElement(userArray, 13);

将(可能更改的)指针重新分配给全局变量。


我(个人)总体上更喜欢设计3。但是,考虑到addElement()访问各种其他全局变量以及选项 1 在我看来是最一致的修复。


推荐阅读