c - 使用结构更新指针执行 realloc 时出现问题

标签 c visual-studio

<分区>

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

我在使用字符串数组的这种技术时运气不错,但使用结构体却行不通。下面是代码:

//  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()可能会回来

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

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

如果以上都不适用,则分配一个新 block ,将旧内容复制到新地址,然后释放旧 block 。 (否则会变成内存泄漏。)

如果realloc()未能执行上述任何操作,它返回 NULL .

回想以上,更糟的是:每当realloc()返回一个不同的地址,然后旧地址的内存被释放。所以,全局 userArray (未更新)变为悬空(指向释放的内存)。访问悬空指针是未定义的行为 → 对任何奇怪的效果都有好处,包括崩溃(访问冲突)。

关于第三个:

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

可能看起来有问题。

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

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

有多种方法可以解决这个问题:

  1. 摆脱全局变量遮蔽:
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 在我看来是最一致的修复。

关于c - 使用结构更新指针执行 realloc 时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56823256/

相关文章:

c++ - 当我的项目在 Visual Studio 和 Qt Creator 中构建时,Q_ASSERT 具有不同的行为

c# - 是否有 Visual Studio 键盘快捷键来实现委托(delegate)?

c - 如何从VC8中的2个静态库中获取所有符号冲突

c# - 最新 windows 10 更新后 Visual Studio 2013 发布崩溃

c - 当文件末尾没有新行时,fgets 无法正确读取

c++ - 如何在 ubuntu 12.04 中的 C/c++ 项目中链接库

c - 我在 C 的输出中收到未知字符

c# - 为什么 Visual Studio 不认为 Obsolete ("message") 是警告?

c - 如何在ARMv6+上实现16bit立体声混音?

c - 为什么我用scanf时空格和回车不一样?