这两个声明之间有什么区别吗?
int x[10];
VS
int* x = new int[10];
我认为前一个声明(如后一个声明)是一个指针声明,两个变量都可以被视为相同。这是否意味着他们本质上是一样的?
答案 0 :(得分:72)
#include<iostream>
int y[10];
void doSomething()
{
int x[10];
int *z = new int[10];
//Do something interesting
delete []z;
}
int main()
{
doSomething();
}
int x[10];
- 在堆栈上创建一个大小为10的整数数组
- 您不必显式删除此内存,因为它会在堆栈展开时消失
- 其范围仅限于函数doSomething()
int y[10];
- 在BSS /数据段上创建一个大小为10的整数数组
- 您不必明确删除此内存
- 由于它已声明为global
,因此可以全局访问。
int *z = new int[10];
- 在堆上分配一个大小为10的整数的动态数组,并将此内存的地址返回到z
。
- 使用后必须明确删除此动态内存。使用 强>
delete[] z;
答案 1 :(得分:8)
之间唯一相似的东西
int x[10];
和
int* x = new int[10];
是否可以在需要int*
的某些上下文中使用:
int* b = x; // Either form of x will work
void foo(int* p) {}
foo(x); // Either form will work
但是,它们不能用于预期int*
的所有上下文中。具体地,
delete [] x; // UB for the first case, necessary for the second case.
其他答案中已解释了一些核心差异。其他核心差异是:
差异1
sizeof(x) == sizeof(int)*10 // First case
sizeof(x) == sizeof(int*) // Second case.
差异2
第一种情况中&x
的类型为int (*)[10]
第二种情况中&x
的类型为int**
差异3
给定功能
void foo(int (&arr)[10]) { }
您可以使用第一个x
而不是第二个x
来调用它。
foo(x); // OK for first case, not OK for second case.
答案 2 :(得分:6)
第一个是大小为int
的{{1}}数组。说它在堆栈上创建是错误的。因为标准不保证。它的实现定义。其存储持续时间可以是静态或自动,具体取决于10
是全局变量还是 local 变量
在第二个中,您创建一个类型为x
的指针。不一定是在堆上创建的,标准没有这么说。分配的内存跨越int*
个字节。为此,您必须通过编写以下内容来释放内存:
10 * sizeof(int)
在这种情况下,指针delete [] x;
的内存被动态分配并动态解除分配,因此这些对象被认为具有动态存储持续时间。
答案 3 :(得分:6)
根据标准,我们应该区分三种不同类型的数组声明:
int x[10];
void method() {
int y[10];
int *z = new int[10];
delete z;
}
第一个声明int x[10]
使用静态存储持续时间,由cppreference定义为:“在程序开始时分配对象的存储空间,并在程序结束。只存在一个对象实例。在命名空间范围内声明的所有对象(包括全局命名空间)都有这个存储持续时间,加上用static或extern声明的那些。“
第二个int y[10]
使用自动存储持续时间,由cppreference定义为:“对象在封闭代码块的开头分配,并在以下处取消分配所有本地对象都有这个存储持续时间,除了声明为static,extern或thread_local的那些。“
第三个int *z = new int[10]
通常被称为动态内存分配,实际上是一个两步序列:
正如其他评论已经提到的,这些类型的声明有其微妙的差异,但最常见的是:
在大多数现代操作系统上:
动态分配的内存应由程序员明确delete
,而静态和自动存储变量由“环境”负责
静态和自动存储变量仅限于特定范围,而动态分配的内存没有边界,这意味着,在一个模块中声明的变量可以传递给在同一地址空间中运行的任何其他模块< / p>
使用new[]
分配数组时,大小可以为0
(正如@R Sahu已经指出的那样)&x
和&z
的类型不同:
&x
是int (*)[10]
&z
是int **
答案 4 :(得分:3)
声明完全不同。
第一种情况,
int x[10];
将x
声明为10
整数数组,而第二种情况,
int* x = new int[10];
将x
声明为指向int
的指针 - 一个值等于int
地址的变量,并将该指针初始化为新表达式的结果({{1}动态分配十个整数的数组。
尽管存在差异,但两者可以以类似的方式使用;
new int [10]
,其中x[i]
是i
和0
之间的整数值),可用于设置或检索各个数组的值上面的语法; 9
相当于x + i
&x[i]
与i
之间的0
包含。[是的,有可能获得“一个接一个”的地址]; 10
和*(x+i)
是等价的,x[i]
和i
之间的0
[取消引用“一个过去的结尾”指针会给出未定义的行为]。但是,也存在一些关键差异,例如;
9
运营商的结果。 sizeof
在两种情况下给出了不同的值。
sizeof(x)
。 sizeof(x) == sizeof(int)*10
提供了一个实现定义的balue,但sizeof(int)
将始终给出数组中元素的数量(即sizeof(x)/sizeof(*x)
,其值为std::size_t
。)10
- 这是一个实现定义的值。 sizeof(x) == sizeof(int *)
的值实际上极不可能产生sizeof(x)/sizeof(*x)
的值。这意味着该技术不能用于获得元素数量。<强>寿命强>
在第一种情况下,10
的生命周期取决于声明发生的范围。如果声明发生在文件范围内(即在编译单元中,在任何功能块之外),那么x
具有静态存储持续时间(因此只要程序正在运行就存在)。如果声明发生在块中,x
- 及其所有元素 - 在块结束时不再存在。例如
x
在第二种情况下,只有指针{
int x[10];
} // x and all its elements cease to exist here
的生命周期取决于范围。动态分配的内存(x
的结果)永远不会被释放。这意味着new x[10]
的生命周期和它引用的(动态分配的)数组的生命周期是解耦的,这给我们带来了第三个差异......
分配结果无法重新分配数组,指针可以(除非适当x
限定)。
考虑
的背景const
在第一种情况下,两个分配都将导致编译器诊断 - 分配无效。在第二种情况下,分配是有效的,并使 // x as previously defined in one or the other form
int y[10];
int z;
x = y;
x = &z;
分别指向x
的(第一个元素)地址和y
的地址。除非z
的值在重新分配之前存储在另一个指针中,否则新表达式(x
)分配的内存将被泄露 - 程序不再可以访问它,但也不会释放它。 / p>
答案 5 :(得分:0)
第一种情况:基于它是非静态局部变量还是静态/全局变量,在堆栈/数据段上创建x
。 x
的地址不可修改。
第二种情况:'x'是指向堆上创建的一般的数组的指针(免费存储)。您也可以更改x
指向其他内容。此外,您需要使用delete[] x;
答案 6 :(得分:0)
它们是相同的,因为两个x都指向10个整数数组中的第一个内存地址,但是非常不同
int x[10]
在静态随机存取存储器中声明存储器,并且 关键字&#39; new&#39;使用堆动态创建它们,与在c中使用malloc动态创建数组大致相同。
不仅如此,(我相信,还没有测试过这个理论),有机会:
int* x = new int[10];
可能会失败,并且取决于编译器,可能会返回错误或空指针。如果c ++编译器符合ANSI / ISO标准,那么它支持&#34; no-throw&#34; new的形式,如果分配失败则返回null,而不是抛出异常。
另一个不同之处在于&#39; new&#39;运算符可以重载。
然而,我不确定的是,是否有一个(在c ++中)创建了一个空终止数组。我知道在c中,在我至少使用的编译器中,如果你希望能够迭代它们而不过度扩展边界,你必须确保始终将\ 0附加到任何字符串或数组。只需要我的$ 0.02价值。 :)
答案 7 :(得分:0)
如果要动态调整数组大小,例如:
void doSomething(int size)
{
int x[size]; // does not compile
int *z = new int[size];
//Do something interesting ...
doMore(z, size);
}
然后x将无法在C ++中编译,因此您必须使用z。好消息是,在大多数情况下,您现在可以使用z,就好像它是静态分配的那样,例如:
void doMore(int anArray[], int size)
{
// ...
}
将z作为参数,将指针视为数组。