为什么用new创建的C ++数组的行为与C风格数组不同?

时间:2014-08-01 12:58:54

标签: c++ c arrays pointers

我自己教C ++,因此编写了一些示例代码来真正理解指针和数组。

我写了这个:

int myints[] = {20, 40, 60, 80, 100}; 
// C style array? should be stored on stack? is myint's type pointer to int or an array of int? how does it differ from myotherints?

int* myotherints = new int[5]{20, 40, 60, 80, 100}; // new always returns pointer, is this a C++ style array?     
// does this pointer get created on stack while the elements themselves are created in free heap?

int j = 5; // should be stored on stack

cout << "myints: " << myints << endl; // decays to pointer, shows address array myints is stored at
cout << "*myints: " << *myints << endl; // myints decays to pointer and is dereferenced to return value stored at start of array myints
cout << "myints[0]: " << myints[0] << endl; // [] dereferences and returns value for element 0 (20)
cout << "myotherints: " << myotherints << endl; // some value?? this is totally unlike the others, why? what is this?
cout << "*myotherints: " << *myotherints << endl; // dereferences pointer myotherints to get address that holds value 20 for first element
cout << "myotherints[0]: " << myotherints[0] << endl; // [] dereferences pointer to get address that holds value 20 for first element
cout << "j: " << j << endl << endl; // 5, sure

cout << "&myints: " << &myints << endl; // array behaving as pointer, gives address of myints[0]
cout << "&myints[0]: " << &myints[0] << endl; // array behaving as pointer, gives address of myints[0]
cout << "&myotherints: " << &myotherints << endl; // address of myotherints, is this where the pointer to the array is stored?
cout << "&myotherints[0]: " << &myotherints[0] << endl; // [] dereferences the pointer that myotherints points to and returns element 0
cout << "&j: " << &j << endl; // address of j

/*
myints: 0x7fff096df830 <-- this makes sense to me, array decays to pointer to give first element address
*myints: 20            <-- this makes sense to me, dereference first element address for value
myints[0]: 20          <-- [] dereferences implicitly, returns value from pointer

myotherints: 0x2308010 <-- myotherints is a pointer to an array of ints, but its address is much lower compared to myints and j, why is that?
*myotherints: 20       <-- getting the value from above address returns 20
myotherints[0]: 20     <-- [] dereferences to address pointed to by pointer myotherints, returns value

j: 5

&myints: 0x7fff096df830      <-- same as below
&myints[0]: 0x7fff096df830   <-- same as above, meaning *myints and myints[0] are the same thing, this address

&myotherints: 0x7fff096df828 <-- how can the pointer to myotherints array be stored here when dereferencing it (*) returns 20 and...
&myotherints[0]: 0x2308010   <-- dereferencing this address with [] also returns 20, yet they are different memory locations unlike myints

&j: 0x7fff096df824 
*/

是不是说myint是一个&#34; C风格的数组&#34;而myotherints是一个&#34; C ++风格的数组&#34;?

如果我理解正确,myotherints是一个指针但myints是一个数组,大多数时候表现得像一个指针?因此,虽然你可以使用myint进行指针处理,但有时候它不像指针一样,即使用&amp;透露其地址。这意味着myints与指针的类型不同。是它的类型&#34;整数数组&#34;?

myint( thing 是myint,而不是其数组中的值)存储在哪里,如果它始终自动取消引用数组存储的位置,我该如何显示其地址?用C ++样式的新数组返回指针?

这些是否以功能不同的方式在内存中表示?

任何能够真正巩固我理解的文档提示或指示都将非常感激。谢谢!

4 个答案:

答案 0 :(得分:10)

两者都是从C继承的东西(正确的 C ++数组的东西是std::array),但你会感到困惑:

  • 第一个是C数组,即具有静态/自动存储持续时间的东西,代表一块内存。该块的大小和“位置”(地址)在编译时确定。

  • 第二个指针指向动态分配的内存块。换句话说,您使用指针将您请求的内存块的地址存储到OS。这对初学者来说很困惑,因为这个东西有时被称为动态数组。 C阵列不是一个相同意义上的数组,但实际上我们以相同的方式使用它们,但出于不同的目的。

关于C ++:

  • C数组的行为类似于内存块的指针和一些含糖(数组索引等),因此它们总是通过引用传递(因为我们拥有的是通过值传递的地址,而不是数组本身),事实上,在很多情况下它们会自动衰减成指针。这些问题使std::array成为更好的选择,因为它具有正确的值语义而且没有隐式衰减。

  • 在C ++手册内存管理应该避免中,您应该使用标准库提供的容器(最着名的std::vector)。 C ++语言提供自动,确定性和安全资源管理的功能;包括内存资源。你应该使用这些。手动内存管理仅适用于非常低级的编程和创建自己的资源管理处理程序。

答案 1 :(得分:5)

C ++从C中获得了类型衰减。

在C语言中,有几种方法可以有效地使用整个数组。你不能将它们传递给函数,从函数中返回函数,至少直接执行[]==+或几乎所有函数。

相反,阵列衰变了#39;每当你看到它的第一个元素时,看看它很有趣。 (基本上,每当你在除了被视为实际数组的少数情况下使用它之外,它会衰减成一个指针)。

因此arr[3]变为(&(first element of arr))[3]*((&(first element of arr))+3)(这些意思相同)。

当您return arr;或将其传递给函数(在C中)时,会发生类似的事情。您的cout << arr表示cout.operator<<( arr ),这只是一个功能。好吧,它也可能是operator<<( cout, arr )。在C ++中,您可以将对实际数组的引用传递给函数,但它需要一些工作而且不会在您的示例代码中发生。

如果键入&arr,则不会发生衰减,并且您将获得指向整个数组的指针。这很重要,因为指针算法,以及其他原因,以及数组数组如何以零开销工作。 (&arr)+1指向数组的末尾,无论它有多大 - ((&(arr[0]))+1)指向数组的第二个元素。

这是int arr[3]={4,5,6};的工作原理,或int arr[]={4,5,6};(同样的事情 - 第二个只为您确定数字3)。两种情况下的arr都是int[3]类型。它很容易衰减到int*,但它的类型为int[3]sizeof(arr)sizeof(int)的三倍,而不是sizeof(int*)

当你new int[3]时,你没有得到指向int[3]的指针,而是得到一个指针int[3]数组的第一个元素。从某种意义上说,它会被预先腐烂。这是指针的类型 - 数组的地址和第一个元素是相同的。但是类型信息(这是一个组合时间概念)不同!

它也存储在免费存储中,而不是自动存储(也就是堆和堆栈)。但这不是根本区别。

关于new int[3]的唯一C ++是你使用new - 你可以使用malloc来获取免费商店的数据空间,并获得指向第一个元素的指针作为C中的int*

答案 2 :(得分:3)

听起来你有关于指针v阵列的问题比什么都重要,我试着通过并点击你所有的问题,

int myints[] = {20,40,60,80,100};

C样式数组?

是的,这就是你如何声明一个C风格的数组

是否在堆栈上?

是的,整个数组(所有5个变量)都位于堆栈中,这意味着当它的块超出范围时,数据本身也会超出范围,通过移动堆栈指针“释放”,这样做并不意味着你不能使用&amp; 运算符来指定这个数组用于其他函数。

这与myOtherInts有什么不同?

这是堆栈上的值,而myotherints是堆上的值,在堆栈上有一个指针。

int* myOtherInts = new int[]{20,40,60,80,100};

这个指针是在堆栈上创建的,而元素本身是在自由堆中创建的吗?

是声明int*表示您正在声明堆栈上的指针(指针在堆栈上创建值)以及何时将该变量赋值给{ {1}}函数返回,(应该是在堆上分配的内存,必须在以后释放)然后有一个堆栈存储指向堆存储值的指针。

new

myotherints的地址,这是存储指向数组的指针吗?

是的,这会返回内存中的哪个位置保存指向实际数据的指针的地址 ,而不是实际存储数据的地址。

cout << "&myotherints: " << &myotherints << endl;

您在此处打印的值是存储数据的地址,该地址与存储指针的位置不同。此值低得多的原因是此内存所在的地址由heap,通常包含程序的较低地址空间。

//myotherints: 0x2308010 <-- myotherints is a pointer to an array of ints, but its address is much lower compared to myints and j, why is that?

这有点棘手。回想一下,&amp; 运算符意味着“给我的地址”,以便将您的代码翻译成英文一点点

&myotherints: 0x7fff096df828 <-- how can the pointer to myotherints array be stored here           when dereferencing it (*) returns 20 and...
&myotherints[0]: 0x2308010   <-- dereferencing this address with [] also returns 20, yet they are different memory locations unlike myints

回想一下myotherints是你在堆栈上分配的指针,但是这个指针指向堆上的内存。所以你应该期望得到一个看起来像是来自堆栈的值(即,通常是更高的值)

give me the address of myotherints

现在,再次give me the address of the first element of myotherints 是指向堆上的值的指针,当您使用myotherints取消引用它时,您将获得存储在堆上的第一个元素的值,因此当你要求存储在堆上的东西的地址,你应该期望存储在堆栈上的东西有不同的结果。 (即表示堆数据的数字相当低)

编辑:对于真正的C ++分配数组的方式,您可能应该使用[0],请参阅Manu's answer

答案 3 :(得分:1)

实际上,

new运算符是一种特殊的方法,简而言之,它要求操作系统提供一些空闲内存并返回新分配内存的地址。因此,它返回标准内存地址。简而言之,myints仅仅是一个地址。存储它的内存未在堆栈上分配。您可以在其上执行基本指针代数,但不能修改它的地址。如果您熟悉asm,您可能会认为myints是一个简单的标签。在C和C ++中,您确实可以在方法中定义标签,如下所示:

...
some_label:
    /* some code here*/
    goto some_label;

编译器应生成代码,指示处理器使用某种跳转指令。一些jmp stack_pointer + some_label。同样,尝试修改myints第四个值编号指示编译器生成类似于前面示例的调用,例如&#34;将此值写入地址stack_pointer + myints + 4 * sizeof(member of myints)&#34;或类似的东西。