指向变量的指针是否与指向一个元素或零个元素的数组的指针相同

Is pointer to variable the same as a pointer to an array of one element, or of zero elements?

本文关键字:元素 指针 零个 数组 是否 变量 一个      更新时间:2023-10-16

大小为 0 的数组 对零长度数组有很好的解释,当然是值得和相关的。我没有看到它将零长度与单元素数组和指针到变量进行比较。

当我之前问(c++ 删除等同于删除[1]吗?我没有很好地表达自己。我的问题似乎是相同的,或者包含在关于新的、新的[]、删除和删除[]的更一般的答案中。有些人明白我问的只是单一元素。评论中的所有答案似乎都是正确和一致的。

有一个问题看起来和这个问题一样。但是这个身体是关于同时使用C++和Java的。在这里,我们只谈论C++。

检查我的理解

我将提出成对的提议的等效语句。这些语句是指向单个变量的指针的声明或实例化,后跟指向一个元素数组的指针。然后我会说明为什么我认为它们是等价的。

这些对等价吗?

int one = 1;
// Sample 1. Same in the sense of pointing to an address somewhere
// whose contents equal one. Also same in the sense of not being able to 
// change to point to a different address:
int * const p_int = &one;
int a_int[1] = {1};
// Sample 2.
int * p_int1 = new int;
int * a_int1 = new int[1];
// Sample 3.
delete p_int1;
delete[] a_int1;
// Sample 4. If Sample 3 is an equivalent pair, then (given Sample 2) 
// we can write
delete[] p_int1;
delete a_int1;

当然,样本 4 是一种不好的做法。

我在想:"删除"将调用对象的析构函数。 delete[] 将为数组的每个元素调用析构函数,然后调用数组的析构函数。 示例 2 中的新内容将 malloc(可以这么说)变量。 new[] 将 malloc 一个元素的数组,然后 malloc 一个元素。然后,该元素将设置为等于 1。所以,我想这就是为什么当我有一个甚至一个元素的数组时,我需要调用 delete[] 而不是删除。我明白吗?

如果我理解,然后调用 delete 而不是 delete[] 来释放一个元素的数组,那么我肯定会有内存泄漏。内存泄漏是将要发生的特定"坏事"。

但是,这个呢:

int * a_int0 = new int[0];
delete a_int0;

这会导致内存泄漏吗?

我请纠正我对术语和其他任何事情的滥用。

示例 1:

int const * p_int = &one;
int a_int[1] = {1};

,这些不等价。指针与数组不同。它们不等价的原因与1std::vector<int>{1}不同的原因相同:一个元素的范围与一个元素不是一回事。

示例 2:

int * p_int1 = new int;
int * a_int1 = new int[1];

这些是等价的。您必须以不同的方式delete它们,但除此之外,您使用p_int1a_int1的方式是相同的。您可以将其中任何一个视为一个范围(分别以 p_int1+1a_int1+1 结尾)。

示例 3:

delete p_int1;
delete[] a_int1;

我认为这些是等效的,因为两者都正确地释放了两个变量的各自内存。

示例 4:

delete[] p_int1;
delete a_int1;

我认为这些是等价的,因为两者都正确释放了两个变量的各自内存。

int const * p_int = &one;
int a_int[1] = {1};

它们不等价,第一个是指向另一个变量的指针,另一个仅仅是大小为 1 的数组,立即使用值 1 进行初始化。

理解这一点:指针本身就是实体,与它们所指向的内容不同。也就是说,你的p_int的内存地址与变量 1 的内存地址完全不同。但是,现在存储在p_int中的是变量 1 的内存地址

// Sample 2.
int * p_int1 = new int;
int * a_int1 = new int[1];

不过,在这里,它们在内存分配方面实际上是一回事。在这两种情况下,您都在堆上创建一个 int(new 表示堆空间),并且两个指针都会立即分配这些堆分配的整数的地址。后者不应该在实践中使用,即使它在技术上没有做任何完全错误的事情,但对于人类来说阅读是令人困惑的,因为它传达了一个对象数组的概念,而实际上只有一个对象,即没有发生阵列。

// Sample 3.
delete p_int1;
delete[] a_int1;

就个人而言,我从未使用过"数组"删除,但是是的,您所说的是正确的想法:delete[] 本质上意味着"对数组中的每个元素调用 delete",而常规删除意味着"删除指针指向的一个对象"。

// Sample 4. If Sample 3 is an equivalent pair, then (given Sample 2) 
// we can write
delete[] p_int1;
delete a_int1;

根据标准,数组上的删除是未定义的,这意味着我们真的不知道会发生什么。一些编译器可能足够聪明,可以看到你的意思是常规删除,反之亦然,或者他们可能没有,在任何情况下,这是有风险的行为,正如你所说,这是不好的做法。

编辑:我错过了你的最后一点。总之,我不知道。

new int[0];

基本上意味着"为 int 类型的 0 个对象分配空间"。这当然意味着 0 * 4,即 0 字节。至于内存泄漏,看起来很直观是否定的,因为没有分配内存,所以如果指针超出范围,堆上什么都没有。我不能给出比这更详细的答案。我的猜测是,这是一个未定义行为的例子,根本不会产生任何后果。

但是,执行此操作时会发生内存泄漏:

void foo()
{
    int* ptr = new int;
}

请注意,函数返回时不会调用任何删除。指针 ptr 本身会自动释放,因为它位于堆栈(IE 自动内存)上,而 int 本身则不是。由于堆内存不会自动解除分配,因此该小 int 将不再可寻址,因为您的堆栈中不再有指向它的指针。基本上,只要进程运行,这 4 个字节的堆内存就会作系统标记为"正在使用",因此不会再次分配它。

编辑2:我需要提高我的阅读理解能力,没有注意到你确实删除了变量。不过变化不大:当你记得删除时,你永远不会有内存泄漏,当你忘记在堆分配的对象(即new)上调用删除时,就会出现内存泄漏,然后再从范围返回指向堆内存的指针所在的范围。我的例子是我能想到的最简单的例子。

这是一个

仅语法的答案。我使用以下代码在调试器中检查了这一点:

// Equivalence Test 1.
*p_int = 2;
p_int[0] = 3;
*a_int = 2;
a_int[0] = 3;

因为我可以将声明作为数组或指向变量的指针来访问和操作,所以我认为声明在语法上是等效的,至少是近似的。

我必须道歉(1)我没有想清楚定义我的术语。如果没有我的定义,很难谈论任何事情。我应该意识到并声明我正在考虑语法以及典型的编译器可能会做什么。 (2)我应该更早地考虑签入调试器。

我认为前面的答案在语义上是正确的。当然,好的编程实践会在数组是指向变量的指针的含义等时声明数组。

感谢对我的问题给予的关注。我希望你能接受我的缓慢,弄清楚我想说什么和问什么。

我认为可以在调试器中以类似的方式检查其他声明,以查看它们在语法上是否等效。

编译器生成相同的程序集将显示语法等效性。但是,如果生成的程序集不同,则需要研究该程序集以查看每个程序集是否执行相同的操作。