簡體   English   中英

常量存儲在何處以及如何存儲?

[英]Where and how are constants stored?

我從這里讀到了這個問題,我也從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"被存儲在內存中?

你不能獲取文字的地址(例如&(5) )因為文字沒有“存儲”在任何地方 - 它實際上寫在匯編指令中。 根據平台的不同,您將獲得不同的說明,但MIPS64附加示例如下所示:

DADDUI R1, R1, #5

嘗試獲取立即數的地址是沒有意義的,因為它不駐留在(數據)內存中,但實際上是指令的一部分。

如果你聲明一個const int i = 5 ,並且不需要它的地址,編譯器可以(並且可能會)將它轉換為文字並將5放在適當的匯編指令中。 一旦你嘗試獲取i的地址,編譯器就會看到它不能再這樣做,並將它放在內存中。 如果您只是嘗試獲取文字的地址,則不是這種情況,因為您沒有向編譯器指示它需要為變量分配空間(當您聲明一個const int i ,它會在第一個中分配空間通過,稍后將確定它不再需要它 - 它不會反向運行)。

字符串常量存儲在數據存儲器的靜態部分中 - 這就是為什么你可以獲取它們的地址。

“這取決於”可能不是一個令人滿意的答案,但它是正確的答案。 如果需要,編譯器會將一些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變量存儲在哪里? 它們存儲在需要存儲的地方。 瘋。

(順便說一下,這些都是用g ++ -g -O2編譯的,不同的編譯器/標志會以不同的方式對它進行優化,這主要證明了編譯器可以做什么,唯一的保證就是你的代碼行為正確。)

這是一個獲取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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM