c++ 在堆栈上声明的数组的值(指针)如何存储在内存中?

np8igboo  于 12个月前  发布在  其他
关注(0)|答案(3)|浏览(99)

最近,我意识到如果我在堆栈上创建一个数组,那么以下值将等于它:

  • 变量本身在堆栈上的地址
  • 该变量的值(即相同的指针)
  • 数组第一个元素的地址

结果是,当我创建一个类型为T、大小为N的数组时,堆栈上会出现一个sizeof(T)*N字节的区域。对于下面的代码,所有三个地址都是相同的:

#include <bits/stdc++.h>

using namespace std;

int main() {

    int automaticArray[5] {1, 2, 3, 4, 5};

    cout << "Variable value = " << automaticArray << ", variable adress = " << &automaticArray << ", first element addres = " << &automaticArray[0] << endl;
}

字符串
如果我对动态内存区域中的数组做同样的操作,它将是不正确的。变量本身的地址(将在堆栈上的地址)将不同于其余两个值。
这是合乎逻辑的,因为我只在堆栈上存储指针,并为它分配了8个字节。数组本身位于堆上的某个地方。对于下面的例子,地址将不相同:

#include <bits/stdc++.h>

using namespace std;

int main() {

    int* dynamicArray = new int[5] {0};

    cout << "Variable value = " << dynamicArray << ", variable adress = " << &dynamicArray << ", first element addres = " << &dynamicArray[0] << endl;
}


当在堆栈上创建一个数组时(考虑一个int的例子),前8个字节将同时包含一个指向数组的指针(即指向这个变量本身的指针),以及数组的前2个元素,这是怎么可能的呢?
我的猜测是变量的值(数组第一个元素的地址)没有存储在任何地方。
我通过GodBolt检查了这个,但我不擅长组装,我不能100%确定我的猜测是正确的。


的数据

w9apscun

w9apscun1#

当在堆栈上创建一个数组时,我们(考虑一个int的例子)在前8个字节中同时包含一个指向数组的指针(即指向这个变量本身的指针)和数组的前2个元素,这是怎么可能的呢?
这是不可能的。您误解了您的输出。在大多数情况下,当数组类型的子表达式出现在表达式中时,它会自动转换为指向第一个数组元素的指针。这是左值转换通常规则的特殊例外,其结果是

cout << "Variable value = " << automaticArray

字符串
实际上,并不打印数组的内容。它100%等效于

cout << "Variable value = " << &automaticArray[0]


这种行为在C++诞生时从C继承而来,并且从那时起一直保持不变。
我的猜测是变量的值(数组第一个元素的地址)没有存储在任何地方。
注意,数组和指针根本不是一回事。数组的地址确实没有以任何可访问的形式存储在内存中。但是数组的值是由其元素的值组成的集合,它肯定存储在内存中。

tzxcd3kk

tzxcd3kk2#

在C++中使用std::array和std::vector。如果没有必要,不要挂在指向数组的指针上。如果需要指针,可以在std::array和std::vector上使用.data()方法

#include <array>
#include <vector>

// you cannot return "C" style (stack) arrays
// but you can return std::array
std::array<int,4> make_array()
{
    std::array<int,4> values{1,2,3,4};
    return values;
}

int main()
{
    std::array<int,4> array1 = make_array(); // Allocated on the stack

    std::array<int,4> array2{2,3,4,5}; // Also allocated on the stack    

    std::vector<int> vector{1,2,3,4}; // Allocatd (for you) on the heap;

    // when vector goes out of scope memory is deallocated for you
}

字符串

qxgroojn

qxgroojn3#

不同之处在于dynamicArray是指针,而automaticArray是数组。
更简单的情况是dynamicArraynew int[5] {0};分配一个数组并返回指向其第一个元素的指针。

----------------             
 | dynamicArray |   ------
 ----------------        |
                         |
                         v
                        -------------
                        | 0 1 2 3 4 |
                        -------------

字符串
也就是说,你有一个指向int的指针。它的值是新分配的数组的第一个元素的地址。因此&dynamicArray[0] == dynamicArray。然而,该指针的地址是其他远程地址,因为指针与实际数组分开存储。
automaticArray的时候事情就不一样了。在内存中你只有

-------------
                        | 0 1 2 3 4 |
                        -------------


就是这样。你缺少的是数组到指针的衰减。在这一行中,

cout << automaticArray << &automaticArray << &automaticArray[0] << endl;


所有三个地址都是相同的,因为automaticArray在传递给std::ostream::operator<<时会衰减为指针。这是因为没有重载会引用5个整数的数组。你可以自己写一个。为了说明,考虑一下:

#include <iostream>

std::ostream& operator<<(std::ostream& out, int (&arr)[5]) {
    for (int i=0;i<5;++i) out << arr[i] << " ";
    return out;
}

int main() {

    int automaticArray[5] {1, 2, 3, 4, 5};
    std::cout << automaticArray;
}


Which outputs:
你不会真的写这样的代码,但这只是为了证明automaticArray不是指针。它是一个数组。它可能会衰减为指向数组第一个元素的指针。这就是在你的代码中发生的事情,因为没有<<重载接受数组的引用。但是如果你通过引用传递数组,那么就没有-指针衰减发生。在上面的例子中,automaticArray是一个数组,arr是对该数组的引用。不涉及指针。

相关问题