使用结构更新指针进行重新分配时出现问题[重复]

Posted

技术标签:

【中文标题】使用结构更新指针进行重新分配时出现问题[重复]【英文标题】:Problems updating pointer doing realloc using a structure [duplicate] 【发布时间】:2019-11-11 09:03:33 【问题描述】:

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

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

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

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

【问题讨论】:

void addElement(structure* userArray, const int tag) 在局部变量 userArray 上有效,但在全局变量 userArray 上无效,正如您所料。 (后者在函数内部被前者所掩盖。)因此,每当realloc() 返回一个新地址时,一旦您离开函数,它就会丢失,而全局userArray 保持不变。您可以从addElement() 中删除参数userArray。但是,我建议return userArray(将返回类型更改为structure*)并将addElement() 的结果再次分配给全局。 @scheff。为什么要在评论中回答? @alk 我正要写一个答案... 除了@Scheff的出色回答之外,我认为考虑到函数addElement管理结构structure的不同分配的能力,最好管理元素计数器( userArrayLength) 以另一种方式。实际上,您可能只管理一个数组,因为只使用一个变量 (userArrayLength) 来管理计数器。一个简单的方法应该是将计数器指针传递给addElementlistElement 函数。 @AnttiHaapala 我没想到会出现这样的重复,因为这个问题实际上与realloc() 的关系不大,但更多的是与范围和食相有关。尊重。 ;-) 【参考方案1】:

恕我直言,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.。

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

    摆脱全局变量的影响:
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 对我来说似乎是最一致的解决方法。

【讨论】:

除了您的出色回答之外,我认为,考虑到函数addElement 的能力(如果将使用方法2或3)来管理结构的不同分配(structure),它是以其他方式更好地管理元素计数器 (userArrayLength)。实际上,代码可能只管理一个数组,因为只使用一个变量 (userArrayLength) 管理计数器。一种简单的方法是将计数器指针传递给addElement 并将计数器传递给listElement 函数。 @SirJoBlack 我完全同意。恐怕答案会很长。 (我还不擅长简短的回答。)所以,我对你的评论投了赞成票…… @Scheff:非常感谢您长时间周到的回答。我选择了选项 3,问题得到解决。再次感谢。

以上是关于使用结构更新指针进行重新分配时出现问题[重复]的主要内容,如果未能解决你的问题,请参考以下文章

在结构中分配内存时出现不可预测的行为

为啥在尝试使用指针访问结构时出现此分段错误?

重命名和重新创建文件时出现奇怪的时间戳重复

访问分配的内存时出现访问冲突错误[重复]

返回指针时出现分段错误[重复]

分配时出现分段错误[重复]