【问题标题】:Is there a way to check if variable length arrays are created on stack / heap?有没有办法检查是否在堆栈/堆上创建了可变长度数组?
【发布时间】:2017-06-02 08:45:34
【问题描述】:

据我了解,以下代码生成可变长度数组(通过 C++ 的非标准扩展)。

int main() 
{
    int valone = rand();
    int valtwo = rand();
    int array[valone][valtwo];
    // Printing size
    cout << sizeof(array) << endl;
}

有没有办法检查它是在堆栈还是堆上生成的?维基百科描述 heregcc 在堆栈中生成相同的内容,但是当我尝试上面的代码时,大多数时候,数组大小似乎太大而无法放入堆栈,但它从不抱怨。

注意:此代码仅适用于 gcc 和 clang,不适用于 Visual Studio

【问题讨论】:

  • 您指向的维基百科链接谈论的是 C99,而不是 C++。 gcc 可能会也可能不会将 C99 代码和 C++ 代码视为相同。
  • 不确定标准方式(“标准”适用于“非标准扩展”),但为了快速测试,您可以比较valtwoarray[0]的地址。如果数组在堆栈上,它们应该是相邻的。如果在堆上,它们将完全不同。这不是 100% 可靠的,但在大多数情况下都可以工作。
  • 在你访问外部临时存储之前它可能不会抱怨,但谁知道呢?
  • 请注意,堆栈溢出的情况并不总是很明显,尤其是如果您从不写入数组末尾的项目(即使您这样做也可能不会)。因此,“它从不抱怨”这一事实不应被视为非标准扩展的实现的任何意义。

标签: c arrays c99


【解决方案1】:

数组大小似乎太大而无法放入堆栈,但它从不抱怨。

“从不抱怨”,我认为您的意思是程序不会崩溃。

你永远不会触及你分配的内存,编译器足够聪明,可以证明它并没有分配任何东西。

让我们获取变量的地址,并将其发送到在别处定义的函数:

int array[valone][valtwo] = {};
cout << &array << endl;

现在,编译器不太确定数组永远不会被访问。那是因为它不能进入​​在另一个翻译单元中实现的流式操作符。也许操作员会取消引用指针;我们必须确保数组存在。

Segfault 在我第一次尝试时使该程序崩溃。堆栈溢出。


我想这种崩溃测试是一种测试 VLA 是否在堆栈上的方法。

Mikhail 在 cmets 中将自动变量的邻接性与 VLA 进行比较的建议是一个不错的平台相关想法,但它只有在分配足够小的 VLA 以防止程序崩溃时才有效。

【讨论】:

    【解决方案2】:

    这可能是一个棘手的问题,我已经尝试过类似的方法

    #include "iostream"
    
    int Stack_or_heap(void* ptr)
    {
     int dummy;
     return ptr > &dummy;
    }
    
    int main(int argc, char** argv)
    {
       int* i = new int();
       int x, y, z;
       std::cout << Stack_or_heap(&x) << Stack_or_heap(&y) << Stack_or_heap(&z) << Stack_or_heap(i);
    }
    

    【讨论】:

    • 这不是完全可靠的,但也很聪明,这个想法可能对 op 来说已经足够了。在 op 的情况下,您可以直接在数组附近使用 int 并检查数组和 int 的地址是否彼此靠近
    【解决方案3】:

    首先,我假设您知道使用 malloc 或 new 在堆中分配的任何内存块都会在整个程序中继续其生命周期,除非显式解除分配。因此我们可以得出结论,如果一个可变长度数组只存在于其范围内而不存在于程序的生命周期内,那么它很可能是在堆栈中分配的。

    我不知道是否存在在堆中分配一块内存但表现得好像它是堆栈成员的情况。最好查看汇编代码。这样的实现可能会在后台使用动态分配函数/系统调用。

    如果它看起来像鸭子,走路像鸭子,说话像鸭子,那么可以安全地假设它是鸭子(大部分时间)。

    【讨论】:

    • 我认为您在第一段中的推理有缺陷。如果可以隐式分配内存块,则也可以隐式释放该隐式分配的块。 VLA完全可以用malloc实现。
    • 可能是这样,这就是为什么在第二段中我说我不知道​​是否存在分配发生在堆中但表现为堆栈成员的情况。另一种推理方式是在堆栈中分配的任何内容都是在编译时确定的。对于一般的 VLA,情况并非如此。据我所知,在运行时分配的任何东西都必须在堆中分配。因此,我们面临一个两难境地,即分配要求要求在堆中分配空间,因为它在运行时被解析,但通过作用域规则表现得好像它是堆栈的成员。
    • 我认为真正的检查需要知道虚拟地址空间中的堆地址空间。
    猜你喜欢
    • 1970-01-01
    • 2011-07-05
    • 2021-04-28
    • 1970-01-01
    • 2017-04-12
    • 2014-07-13
    • 2016-01-06
    • 2021-08-01
    • 1970-01-01
    相关资源
    最近更新 更多