声明int的数组
这两个声明有什么区别?
int x[10];
与
int* x = new int[10];
我想前面的声明(就像后面的声明)是一个指针声明,两个variables都可以被视为相同的。 这是否意味着它们本质上是一样的?
#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];
– 在堆栈上创build一个大小为10的整数数组。
– 你不必显式地删除这个内存,因为它随着堆栈展开而消失。
– 它的范围仅限于函数doSomething()
int y[10];
– 在BSS /数据段上创build一个大小为10的整数数组。
– 您不必显式删除此内存。
– 由于它是global
它可以在全球范围内使用。
int *z = new int[10];
– 在堆上分配一个大小为10的整数的dynamic数组,并将该内存的地址返回给z
。
– 你必须在使用它之后明确地删除这个dynamic内存。 使用:
delete[] z;
之间唯一的相似之处
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
types是int (*)[10]
在第二种情况下, &x
types是int**
区别3
给定function
void foo(int (&arr)[10]) { }
你可以使用第一个x
而不是第二个x
来调用它。
foo(x); // OK for first case, not OK for second case.
第一个是大小为10
的int
数组。 说它在堆栈上创build是错误的。 因为标准不能保证。 其实现定义。 它的存储时间可以是静态的也可以是自动的,取决于x
是全局variables还是局部variables。
在第二个,你创build一个int*
types的指针。 标准不一定在堆上创build,标准不会这么说。 分配的内存跨越10 * sizeof(int)
个字节。 为此,你必须自己释放记忆,写下:
delete [] x;
在这种情况下,指针x
的内存被dynamic地分配,并被dynamic地解除分配,所以这样的对象被称为具有dynamic存储持续时间 。
根据标准,我们应该实际区分三种不同types的数组声明:
int x[10]; void method() { int y[10]; int *z = new int[10]; delete z; }
第一个声明int x[10]
使用由cppreference定义的静态存储持续时间:“对象的存储在程序开始时被分配,当程序结束时被释放,只存在对象的一个实例。在名称空间范围(包括全局名称空间)有这个存储持续时间,加上那些声明与静态或外部“。
第二个函数int y[10]
使用自动存储持续时间,由cppreference定义为:“对象分配在封闭代码块的开始位置,并在结束时释放,所有本地对象都有这个存储持续时间,静态,外部或thread_local“。
第三个int *z = new int[10]
通常被称为dynamic内存分配,实际上是一个两步的序列:
- 首先调用operator new,它使用标准库的默认分配方法或用户定义的实现(因为new可以在运行时被覆盖)dynamic地分配内存。 分配的内存将足以适应分配的N个元素,加上为给定分配保留元数据所需的额外内存(以便稍后可以成功释放)。
- 其次,如果第一步成功,我们继续初始化或构造数组中的每个对象。
正如其他评论已经提到的那样,这些types的声明有微妙的差别,但最常见的是:
-
在大多数现代操作系统上:
- 自动存储通常在堆栈上分配,这通常是使用LIFO机制的线程专用的预分配内存空间
- 静态存储使用预先分配的可执行文件内存空间( 更具体地说,.BSS和.DATA段,取决于variables是否初始化为零 )
- dynamic内存使用堆内存进行分配,并受制于系统的RAMpipe理系统和寻呼等其他机制。
-
dynamic分配的内存应由程序员明确
delete
,而静态和自动存储variables由“环境” -
静态和自动存储variables被限制在一个特定的范围内,而dynamic分配的内存没有边界,这意味着在一个模块中声明的variables可以传递到在相同地址空间中运行的任何其他模块
-
使用
new[]
分配数组时,大小可以是0 -
(正如@R Sahu所指出的那样)
&x
和&z
的types是不同的:-
&x
是int (*)[10]
-
&z
是int **
-
声明是完全不同的。
第一种情况,
int x[10];
将x
声明为10
整数的数组,而第二种情况,
int* x = new int[10];
声明x
为指向int
的指针 – 一个值等于int
地址的variables,并初始化指向新expression式( new int [10]
)的结果的指针,该expression式dynamic地分配十个整数的数组。
不pipe有什么不同,两者可以用相似的方式使用;
- 可以使用数组语法(例如,
x[i]
,其中i
是0
和9
之间的整数值)来设置或检索上述语法中的各个数组的值; - 指针运算可以用来获得数组元素的地址(例如
x + i
相当于&x[i]
对于i
在0
和10
之间(包括0
和10
(是的,可以获得“one of the end”地址]。 - 指针取消引用和数组访问是等价的。 即
*(x+i)
和x[i]
是等价的,因为i
介于0
和9
之间[解除引用“一个超过结尾”指针给出未定义的行为]。
但是,也有一些关键的差异,
运算符sizeof
结果 。 sizeof(x)
在两种情况下给出不同的值。
- 在第一种情况下
sizeof(x) == sizeof(int)*10
。sizeof(int)
给出了一个实现定义的balue,但sizeof(x)/sizeof(*x)
将始终给出数组中元素的数量(即一个值为10
的std::size_t
)。 - 在第二个,
sizeof(x) == sizeof(int *)
– 这是一个实现定义的值。sizeof(x)/sizeof(*x)
值实际上不太可能产生10
的值。 这意味着这种技术不能用来获取元素的数量。
终身 。
-
在第一种情况下,
x
的生存期取决于声明发生的范围。 如果声明出现在文件范围(即在编译单元中,在任何function块之外),则x
具有静态存储持续时间(只要程序正在运行就存在)。 如果声明出现在块中,则块及其所有元素在块结束时不再存在。 例如{ int x[10]; } // x and all its elements cease to exist here
-
在第二种情况下,只有指针
x
的生命周期取决于范围。 dynamic分配的内存(new x[10]
的结果)永远不会被释放。 这意味着x
的生命周期和它所引用的(dynamic分配的)数组的生命周期是分离的,这就带来了第三个差异…..
赋值的结果一个数组不能被重新分配,一个指针可以(除非适当的const
限定)。
考虑一下上下文
// x as previously defined in one or the other form int y[10]; int z; x = y; x = &z;
在第一种情况下,这两个分配都会导致编译器诊断 – 分配无效。 在第二种情况下,赋值是有效的,并使得x
分别指向y
(的第一个元素)的地址和z
的地址。 除非x
的值在重新分配之前存储在另一个指针中,否则由新expression式( new int [10]
)分配的内存将被泄漏 – 程序将不能再访问它,但也不会被释放。
第一种情况:根据是非静态局部variables还是静态/全局variables,在堆栈/数据段上创buildx
。 x
地址是不可修改的。
第二种情况:“x”是一个指向通常在堆上创build的数组的指针(自由存储)。 你也可以改变x
指向别的东西。 而且,你需要注意通过使用delete[] x;
来释放它delete[] x;
它们是相同的,就像这两个x指向10个整数数组中的第一个存储地址一样,然而在这个数组中非常不同
int x[10]
在静态随机存取内存中声明内存,关键字'new'使用堆dynamic创build它们,与在c中使用mallocdynamic创build数组大致相同。
不仅如此,而且(我相信,没有检验过这个理论)有一个机会:
int* x = new int[10];
可能会失败,并根据编译器,可能会返回一个错误或一个空指针。 如果c ++编译器符合ANSI / ISO标准,那么它支持新的“不抛出”forms,如果分配失败,则返回null,而不抛出exception。
另一个区别是“新”操作符可能被重载。
然而,我不确定的是,如果任何一个(在c + +中)创build一个null终止数组。 我知道在c中,在我至less使用的编译器中,如果您希望能够在不超出边界的情况下迭代它们,则必须确保总是将\ 0附加到任何string或数组。
只是我的0.02美元的价值。 🙂