【问题标题】:are all variables stored contiguously based on the order in which they are declared in the program?所有变量是否都根据它们在程序中声明的顺序连续存储?
【发布时间】:2020-02-26 04:32:40
【问题描述】:

我正在学习数组,以及当您尝试写入越界时,它可能对您的程序有害。发现这一点后,我试图越界阅读。

我玩弄了一个 char 数组和一个 char 变量。

   //this occurs within int main()

   char vowels[]{'a', 'e', 'i', 'o', 'u'};
   char x = 'x';

   std::cout << vowels[5]; //this outputs x

这让我想知道是否所有变量都按照声明的顺序连续存储。

【问题讨论】:

  • C dupe: stackoverflow.com/questions/1677415/… 试图找到 C++ 版本
  • 不,不是全部。例如,静态变量可以乱序创建。你正在做的是UB:你根本没有任何保证......任何事情都可能发生。
  • 我相信这是一个实现细节,而不是标准规定的。 C++ 不支持使用(或滥用)实现细节,并且完全属于未定义行为的领域。这就是坏事发生的地方。
  • 这可能会发生,但 AFAIK,不能保证。
  • en.cppreference.com/w/cpp/language/ub - 您的程序包含未定义的行为,因此没有意义任何事情都可能发生(并且是有效的解释)。

标签: c++ callstack


【解决方案1】:

这让我想知道是否所有变量都按照声明的顺序连续存储

不,在这种情况下,C++ 唯一保证的是,当您使用变量名读取值时,您将得到它。不能保证它们存储在内存中的顺序,或者即使它们根本存储在内存中,这样做对你来说最糟糕的事情是——越界访问实际上会使你的整个程序无效,从语言的角度来看,你不能指望您的程序中的任何内容。

【讨论】:

    【解决方案2】:

    来自标准(7.6.9.4):

    比较不相等的指向对象的指针的结果是按照符合以下规则的偏序来定义的:

    • 如果两个指针指向同一个数组的不同元素,或者指向它的子对象,则需要指向具有较高下标元素的指针来比较较大。

    • 如果两个指针指向同一个对象的不同非静态数据成员,或者指向这些成员的子对象,递归地,如果两个成员具有相同的访问控制,则需要指向后面声明的成员的指针比较更大( [class.access]),两个成员都不是大小为零的子对象,它们的类也不是联合。

    • 否则,两个指针都不需要比较大于另一个。

    由于前两个子句不适用于将x的地址与vowels的地址或其元素进行比较,因此它们的地址没有意义可比。

    但在这种情况下,特别是指针vowels + 5vowels[5] 试图取消引用的地址)具有特殊含义。指向具有n 元素的数组的索引n 处的假设元素的任何指针都称为“结束指针”。它表示数组结束后的第一个字节。允许计算此地址但不能取消引用它。该标准有一条注释明确指出您根本不能使用vowels[5] (6.8.2.3):

    [...] 超过对象末尾的指针 ([expr.add]) 不被视为指向可能位于该地址的对象类型的不相关对象。 [...]

    因此,即使x 正好位于vowels + 5,您仍然不能通过vowels[5] 访问它。

    【讨论】:

      【解决方案3】:

      这让我想知道是否所有变量都连续存储在 它们的声明顺序。

      没有,或者至少语言没有指定它(参见 François Andrieux 的answer)。内存中变量的布局(特别是堆栈上的非成员变量)由编译器自行决定。由于编译器必须处理诸如对齐之类的体系结构要求,它可以重新排列变量并在它们之间留下间隙。这对于优化很重要。

      这是一个示例 (x86-64),其中编译器 (clang-9) 不按声明顺序布局变量...

      #include <iostream>
      
      int main() {
      
        char a[] {'a', 'b'};
        int x;
      
        std::cout << "&a[0] = " << (void*) &a[0] << std::endl;
        std::cout << "&a[1] = " << (void*) &a[1] << std::endl;
        std::cout << "&x = " << &x << std::endl;
      
        return 0;
      }
      
      &a[0] = 0x7ffd5becf02a
      &a[1] = 0x7ffd5becf02b
      &x = 0x7ffd5becf024
      

      【讨论】:

        猜你喜欢
        • 2012-09-08
        • 2013-10-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-05-12
        • 1970-01-01
        • 2022-01-05
        • 2021-07-30
        相关资源
        最近更新 更多