常量存储在何处以及如何存储?

时间:2014-02-12 17:59:59

标签: c++ memory constants

我从here 读到了这个问题,我也从c-faq读了相关问题,但我不明白这背后的确切原因: -

#include <iostream>

    using namespace std;

    int main()
    {
        //const int *p1 = (int*) &(5);  //error C2101: '&' on constant
        //cout << *p1;

        const int five = 5;
        const int *p2 = &(five);
        cout << *p2 << endl;

        char *chPtr = (char*) &("abcde");
        for (int i=0; i<4; i++) cout << *(chPtr+i);
        cout << endl;
        return 0;
    }

我想知道如何存储常量(整数或字符串文字)。我对字符串文字的理解是它们在程序启动时在全局静态内存中创建并持续到程序退出。在"abcde"的情况下,即使我没有给它一个变量名,我也可以使用它的地址(chPtr),我假设我可能在程序终止之前的任何时候取消引用chPtr字符值仍然存在,即使我在声明范围之外取消引用它。 const int变量"five"是否也置于全局静态中,地址p2也可以随时引用?

为什么我可以使用"five"的地址,但我不能要求:&(5)?常量"5""five"的存储方式是否不同?哪个"5"存储在内存中?

3 个答案:

答案 0 :(得分:6)

你不能获取文字的地址(例如&(5)),因为文字没有“存储”在任何地方 - 它实际上写在汇编指令中。根据平台的不同,您将获得不同的说明,但MIPS64附加示例如下所示:

DADDUI R1, R1, #5

尝试获取立即数的地址是没有意义的,因为它不驻留在(数据)内存中,但实际上是指令的一部分。

如果声明const int i = 5,并且不需要它的地址,编译器可以(并且可能会)将其转换为文字并将5放在适当的汇编指令中。一旦你尝试获取i的地址,编译器就会看到它不能再这样做,并将它放在内存中。如果您只是尝试获取文字的地址,则不是这种情况,因为您没有向编译器指示它需要为变量分配空间(当您声明const int i时,它会分配空间第一次通过,后来确定它不再需要它 - 它反向运行。

字符串常量存储在数据存储器的静态部分中 - 这就是为什么你可以获取它们的地址。

答案 1 :(得分:4)

“它取决于”可能不是一个令人满意的答案,但它是正确的答案。如果需要,编译器会将一些const变量存储在堆栈中(例如,如果你接受它的地址)。但是,编译器中始终存在“constexpr”变量的概念,即使我们并不总是直接调用它来进行调用:如果表达式可以在编译时计算,那么不要在运行时进行计算,我们可以在编译时计算它。如果我们可以在编译时计算它,并且我们从不做任何需要它不同的东西,那么我们可以将它们全部一起删除并将其转换为文字,这将是指令的一部分!

例如,以下代码:

int main(int argc, char** argv)
{
  const int a = 2;
  const int b = 3;
  const int c = a+b;

  volatile int d = 6;
  volatile int e = c+d;

  std::cout << e << std::endl;
  return 0;
}

看看编译器有多聪明:

    37    const int a = 2;
    38    const int b = 3;
    39    const int c = a+b;
    40
    41    volatile int d = 6;
0x400949  <+0x0009>         movl   $0x6,0x8(%rsp)
    42    volatile int e = c+d;
0x400951  <+0x0011>         mov    0x8(%rsp),%eax
0x400955  <+0x0015>         add    $0x5,%eax
0x400958  <+0x0018>         mov    %eax,0xc(%rsp)
    43
    44    std::cout << e << std::endl;
0x400944  <+0x0004>         mov    $0x601060,%edi
0x40095c  <+0x001c>         mov    0xc(%rsp),%esi
0x400960  <+0x0020>         callq  0x4008d0 <_ZNSolsEi@plt>
    45    return 0;
    46  }

(volatile告诉编译器不要对该变量进行花哨的内存技巧)在第41行中,当我使用c时,使用LITERAL 0x5完成添加,尽管它甚至是其他代码的组合。第37-39行包含NO指令。

现在让我们更改代码,以便我需要一个位置:

int main(int argc, char** argv)
{
  const int a = 2;
  const int b = 3;
  const int c = a+b;

  volatile int d = 6;
  volatile int e = c+d;
  volatile int* f = (int*)&a;
  volatile int g = *f;

  std::cout << e << std::endl;
  std::cout << g << std::endl;
  return 0;
}


    37        const int a = 2;
0x400955  <+0x0015>         movl   $0x2,(%rsp)
    38        const int b = 3;
    39        const int c = a+b;
    40
    41        volatile int d = 6;
0x400949  <+0x0009>         movl   $0x6,0x4(%rsp)
    42        volatile int e = c+d;
0x400951  <+0x0011>         mov    0x4(%rsp),%eax
0x40095c  <+0x001c>         add    $0x5,%eax
0x40095f  <+0x001f>         mov    %eax,0x8(%rsp)
    43        volatile int* f = (int*)&a;
    44        volatile int g = *f;
0x400963  <+0x0023>         mov    (%rsp),%eax
0x400966  <+0x0026>         mov    %eax,0xc(%rsp)
    45
    46        std::cout << e << std::endl;
0x400944  <+0x0004>         mov    $0x601060,%edi
0x40096a  <+0x002a>         mov    0x8(%rsp),%esi
0x40096e  <+0x002e>         callq  0x4008d0 <_ZNSolsEi@plt>
    47        std::cout << g << std::endl;
0x40097b  <+0x003b>         mov    0xc(%rsp),%esi
0x40097f  <+0x003f>         mov    $0x601060,%edi
0x400984  <+0x0044>         callq  0x4008d0 <_ZNSolsEi@plt>
    48        return 0;

所以我们可以看到a被初始化为堆栈中的实际内存空间(我可以告诉cuz rsp)。但等等...... c依赖于a,但每当我使用c时它仍然是文字5!这里发生了什么?好吧,编译器知道由于它的使用方式,需要在内存位置。但是,它知道变量的值永远不是2,所以每当我以不需要内存的方式使用它时,我可以将它用作文字2.这意味着第37行中的a与第43行。

那么const变量存储在哪里?它们存储在需要存储的地方。 CRAZY。

(顺便说一句,这些都是用g ++ -g -O2编译的,不同的编译器/标志会以不同的方式对它进行优化,这主要说明了编译器可以做什么,唯一的保证就是你的代码行为正确。)

答案 2 :(得分:1)

这是一个获取const int的地址并演示(至少在我的机器上的gcc中)它存储为本地(非全局静态)变量的示例。

#include <iostream>

const int *func() {
    const int five = 5;
    const int *p = &(five);
    std::cout << *p << '\n';
    return p;
}

// function to overwrite stack values left by earlier function call
int func2(int n, int x) {
    for (int i = 0; i < x; ++i)
        n *= 2;
    return n;
}

int main() {
    const int *p = func();
    std::cout << func2(2, 10) << '\n';
    std::cout << *p << '\n';
    return 0;
}

示例输出:

5
2048
1