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

时间:2016-03-08 15:44:34

标签: c++ arrays pointers memory-leaks

Array with size 0对零长度数组有很好的解释,肯定是有价值和相关的。我没有看到它将零长度与单元素数组和指针变量进行比较。

当我之前询问时(Is c++ delete equivalent to delete[1]?)我没有表达自己的意思。我的问题似乎相同或包含在关于new,new [],delete和delete []的更一般的答案中。有些人明白我只询问一个元素。评论中的所有答案似乎都是正确和一致的。

有一个问题看起来和这个问题一样。但是正文是关于一起使用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中的new将是malloc(可以这么说)变量。 new []会malloc一个元素的数组,然后malloc一个元素。然后,将一个元素设置为等于1.因此,我想那个为什么我需要调用delete []而不是删除当我有一个偶数数组时一个要素。我理解吗?

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

然而,这是怎么回事:

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

会导致内存泄漏吗?

我邀请更正我滥用术语和其他任何内容。

3 个答案:

答案 0 :(得分:3)

样本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;

我认为这些是等同的, in 正确地解除了两个变量的相应内存。

答案 1 :(得分:0)

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

它们不是等价的,第一个是指向另一个变量的指针,另一个是一个大小为1的数组,直接用值1初始化。

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

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

但是,就内存分配而言,它们实际上是相同的。在这两种情况下,您都在堆上创建一个int(new表示堆空间),并且两个指针都会立即分配这些堆分配的int的地址。后者不应该在实践中使用,尽管它在技术上没有做任何完全错误的事情,因为它传达了存在对象数组的概念,但是当它存在时,会让人感到困惑。实际上只有一个对象,即没有发生阵列。

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

就个人而言,我从未使用过“数组”删除,但是,你所说的是正确的想法: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];

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

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

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

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

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

答案 2 :(得分:0)

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

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

*a_int = 2;
a_int[0] = 3;

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

我要道歉 (1)我认为没有明确界定我的条款。如果不定义我的条款,很难谈论任何事情。我应该已经意识到并且声明我在思考语法以及典型的编译器可能会做什么。 (2)我应该考虑更早地检入调试器。

我认为以前的答案在语义上是正确的。当然,当数组是指向变量的指针等等时,良好的编程习惯会声明一个数组。

我感谢对我的问题的关注。而且我希望你能接受我的慢慢来弄清楚我想说什么并问。

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

编译器生成相同的程序集将显示句法等效性。但是如果生成的组件不同,那么需要研究组件以确定每个组件是否做同样的事情。