使用 new [重复] 实例化的结构上的未初始化内存警告

Posted

技术标签:

【中文标题】使用 new [重复] 实例化的结构上的未初始化内存警告【英文标题】:Uninitialized memory warning on structs instantiated with new [duplicate] 【发布时间】:2021-07-24 04:09:24 【问题描述】:

所以我有一个节点结构

struct Node

    int x = 0;
;

我赚了 20 个Node*s。我的理解是Node** 是一个指向数组开头的指针,该数组包含指向Nodes 的指针。

constexpr int mazeSize = 20;
Node** testMaze = new Node * [mazeSize];

在此之后,当我尝试对其进行任何操作时,我开始收到警告和错误。例子:

testMaze[0]->position.x == 0; //->Using uninitialized memory `*testMaze` and 

我从这个错误中了解到:*testMaze 正在取消引用指针数组,这意味着它引用了该数组中的第一个 Node 对象。如果是这种情况,我将如何初始化它?如果我只是这样创建Node*

Node* node = new Node;
node->x = 0; 

它工作得很好,不需要初始化它,那么为什么不按照我的方式做呢?我也不明白如何初始化一个结构。

另一个例子:

testMaze[0]->x == testMaze[1]->x //->Runtime error: Access violation reading error
testMaze[0]->x = 0; //->Runtime error: Access violation writing error

如何解决这些问题?谢谢。

【问题讨论】:

new Node * [mazeSize]; 提供了一个指针数组。您没有显示任何将这些指针指向任何有效位置的代码,并且警告消息表明您没有编写此类代码。指针会指向,所以解决方法是创建几个Nodes 并开始指向。 Note* 不是Node。您有指针但没有 Node 指向。您可能打算使用new Node[mazesize] 来实际创建Node 对象,但您应该只使用std::vector<Node> @user4581301 啊,我想我现在明白了。我以为new Node * [mazeSize]; 会给我指向这些指针创建和指向的节点的指针,但它实际上只生成指针,我必须自己创建节点并让这些指针指向这些节点。是这样吗? 时髦。乐意效劳。现在看看您是否可以使用@FrançoisAndrieux 谈论的内容并杀死所有(可见)指针。指针几乎总是会减慢代码的速度,因为计算机不能只获取下一位数据,它必须使用指针来跟踪它然后加载它。但是,如果所有数据都在一条直线上,则计算机只是开始在后台加载内容,并且第二项可能已经准备好在您需要时使用,如果没有在加载第一项的同一读取中抓取。 不相关的咆哮:计算机很聪明,因为它们可以非常快地完成数量惊人的愚蠢事情。很多时候,让计算机变得愚蠢和快速所获得的收益比让计算机变得更智能所获得的收益更多。对于短期数据,O(N) 线性搜索比任何花哨的 O(log(N)) 搜索都要快得多,因为 CPU 可以直接运行 nope.nope.nope.nope.nope... 而没有就正确的路径或下一步要加载的数据做出任何决定。 【参考方案1】:

问题:

    constexpr int mazeSize = 20;
    Node** testMaze = new Node *[mazeSize]; //this makes 20 pointers, but the pointers dont point to anything
    testMaze[0]->position.x == 0; //undefined, as testMaze's pointers do not point to anything

这是因为你创建了一个新的node,而不是一个指向node的新指针。

Node * node = new Node;
node->x = 0;

至于这个:

    testMaze[0]->x == testMaze[1]->x; //This is an undefined pointer, it doesn't point to anything, and you are trying to access it, so UNDEFINED behavior
    testMaze[0]->x = 0; //This is a undefined pointer, it doesn't point to anything, and you are trying to access it, so UNDEFINED behavior

我会这样做:

Node** Make(int size) 
    Node** temp = new Node * [size];
    Node* pool = new Node[size];
    for (int i = 0; i < size; ++i) 
        temp[i] = &pool[i];
    

这使您的指针数组,使您的指针指向指向实际值的实际指针。

另外,不要忘记delete[] 你的Node**s 和Node*s,否则你会发生内存泄漏!

【讨论】:

还请注意,所涉及的手动内存管理可能会以无数种方式绊倒您。我分配了吗?是否已经存在需要处理的分配?是时候删除了吗?删除是否安全?无论如何,删除谁的工作?我需要什么样的复制逻辑?我可以简单地复制指针并共享分配,还是需要一个新的分配并复制指针处的内容?你知道大师们是如何处理这个问题的吗?几乎从不面对它。 They'll do something like this.【参考方案2】:

永远记住,指针类型是独立的、具体的类型,有自己的类型和大小。指向 T T* 的指针基本上归结为一小块内存,用于将地址存储到另一个内存区域,(希望)包含某种类型的实例 T

Node** testMaze = new Node * [mazeSize];

您正在分配Node*sizeof(Node*) 类型的maxSize 指针大小 元素数组(在现代平台上通常为4 或8 字节,具体取决于您的可执行文件是否旨在在 32 位或 64 位模式下运行)。

这些新创建的指针没有被初始化,因此指向无效地址,除非你也零初始化数组:

Node** testMaze = new Node * [mazeSize] ;
assert (testMaze[0] == nullptr); // holds true

为了获得NodemaxSize 实例,您必须创建maxSizeNode 实例:

Node** testMaze = new Node* [mazeSize];
for (std::ptrdiff_t i ; i < maxSize; ++i) 
    testMaze[i] = new Node  /* insert parameters here */ ;

鉴于您使用的是constexpr,我推断您所针对的 C++ 版本是 C++11 或更高版本。在这种情况下,您应该知道,在编写现代 C++ 时,operator newoperator new[] 几乎总是错误的选择,因为它们只返回您必须手动管理其所有权和生命周期的指针。

您绝对应该开始使用 STL 容器,例如 std::vectorstd::array,它们更易于使用,并且可以避免很多不必要的痛苦。如果你坚持使用new,至少看看std::unique_ptr,它包装了一个指针或C数组,一旦超出范围就会自动调用deletedelete[]

【讨论】:

你不想要testMaze[0] = new Node,你想要testMaze[i] = new Node。一个微妙但重要的区别。 @MarkRansom 愚蠢的我,我犯了一个经典的复制粘贴错误。已修复,谢谢! 通常当你使用std::vector时你根本不需要指针,你可以让向量的每个元素成为完整的对象。 @MarkRansom 有人可能更喜欢使用(最好是智能)指针向量而不是向量 的一个原因是当您的 T 非常 很大并且您需要对向量进行排序或通常更改其元素的顺序。交换unique_ptrs 是即时的,而频繁地memcpy-ing 大型数据结构可能会变得更慢。 这就是链表通常被杀死的地方。当然,你有 O(1) 的插入和删除,但你所做的一切,包括在列表中查找插入或删除的位置,都是 O(N),由于缓存未命中和指针追逐而变得非常缓慢。

以上是关于使用 new [重复] 实例化的结构上的未初始化内存警告的主要内容,如果未能解决你的问题,请参考以下文章

python中的内方法

valgrind 抱怨 C++ 结构上的未初始化字节

带有 Clang 10 显式模板实例化的 ~queue 的未定义引用

Java类的实例化的初始化过程

如何从类内实例化的对象访问私有变量

Java类加载及实例化的调用顺序