自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

C++ 面試題:const 全局變量存在 .data 段還是 .bss 段?

開發(fā)
const 全局變量會存放在只讀數(shù)據(jù)段(.rodata),而非.data或.bss段。因?yàn)?data段存放可讀寫的已初始化數(shù)據(jù),而 const 變量需要保證只讀屬性,編譯器會將其分配到.rodata段。 

這是今年騰訊最新春招實(shí)習(xí)生的面試題,不知道是面試者總結(jié)手誤寫錯了還是面試官就這么問的,這問的就沒有正確答案。 

我們都知道:全局變量和靜態(tài)變量一般存放在 .data 或者 .bss 段,具體取決于是否被初始化?!?/p>

  • 如果全局變量被顯式初始化為非零值,它們通常放在 .data 段; 
  • 而如果未初始化或者初始化為零,則可能放在 .bss 段?!?/li>

.data 段(已初始化數(shù)據(jù)段):存放顯式初始化且初始值非零的全局變量和靜態(tài)變量?!?/p>

int global_var = 42;          // 存儲在 .data 段
static int static_var = 10;   // 存儲在 .data 段

.bss 段(未初始化數(shù)據(jù)段):存放未顯式初始化或初始化為零的全局變量和靜態(tài)變量(節(jié)省磁盤空間,加載時自動清零)。 

int global_uninit;            // 存儲在 .bss 段
static int static_zero = 0;   // 存儲在 .bss 段(初始化為零)

為什么說節(jié)省空間? 

比如,如果一個全局?jǐn)?shù)組很大但未初始化,那么在編譯后的文件中,BSS段可能只是記錄這個數(shù)組的大小,而不是實(shí)際存儲所有的零值。當(dāng)程序加載到內(nèi)存時,系統(tǒng)會根據(jù)需要分配內(nèi)存并清零,這樣在磁盤上的可執(zhí)行文件就不會包含這些零值的數(shù)據(jù),從而減少了文件的大小?!?/p>

const 全局變量不一樣

首先 const全局變量必須顯式初始化(否則編譯報錯),因此不存在“未初始化”的情況?!?/p>

const 全局變量會存放在只讀數(shù)據(jù)段(.rodata),而非.data或.bss段。因?yàn)?data段存放可讀寫的已初始化數(shù)據(jù),而 const 變量需要保證只讀屬性,編譯器會將其分配到.rodata段?!?/p>

我們測試驗(yàn)證下: 

編寫如下測試代碼: 

const int a = 10;
const int b = 0;

int main() 
{
        return 0;
}

然后執(zhí)行 g++ 1.cpp 生成 a.out, 我們使用 nm 工具查看符號表,可以發(fā)現(xiàn)輸出:

0000000000400530 r _ZL1a
0000000000400534 r _ZL1b

這里 r 表示符號位于只讀數(shù)據(jù)段?!?/p>

所以const全局變量若初始化(無論是否為非零),通常位于.rodata段?!?/p>

nm輸出中 B b T 這種字符,代表什么意思

在 nm 的輸出中,符號類型通過 大小寫字母 表示符號的存儲位置和屬性。以下是常見符號類型的解釋: 

字母

大小寫

解釋

B

大寫

未初始化的全局變量

b

小寫

未初始化的靜態(tài)變量

T

大寫

全局函數(shù)/代碼

t

小寫

靜態(tài)函數(shù)/代碼

r

小寫

只讀數(shù)據(jù)

D

大寫

已初始化的全局變量

d

小寫

已初始化的靜態(tài)變量

U

大寫

未定義符號

關(guān)鍵區(qū)別:

  • 大寫字母(B/T/D):表示符號是全局的(全局變量或函數(shù)),可以被其他文件訪問。
  • 小寫字母(b/t/d):表示符號是局部的或靜態(tài)的(作用域限于當(dāng)前文件)。
  • 大小寫 B/b:都位于 .bss 段,但作用域不同(大寫為全局,小寫為靜態(tài))。
  • 大小寫 T/t:都位于 .text 段,但作用域不同(大寫為全局函數(shù),小寫為靜態(tài)函數(shù))。

我們寫個測試代碼再驗(yàn)證下: 

const int a = 10; //const 初始化
constint b = 0;//const 初始化為0

int c; //全局 未初始化
int d = 9; //全局 初始化
int dd = 0; //全局 初始化0

staticint e; //靜態(tài) 未初始化
staticint f = 10; //靜態(tài)初始化
staticint g = 0; //靜態(tài)初始化0 

intmain()
{
        return0;
}

編譯后用 nm 查看: 

nm a.out
0000000000601024 B __bss_start
0000000000601028 B c
0000000000601024 b completed.7247
000000000060101c D d
0000000000601018 D __data_start
0000000000601018 W data_start
000000000060102c B dd
0000000000400400 t deregister_tm_clones
0000000000400470 t __do_global_dtors_aux
0000000000600e28 t __do_global_dtors_aux_fini_array_entry
0000000000400528 R __dso_handle
0000000000600e30 d _DYNAMIC
0000000000601024 D _edata
0000000000601038 B _end
0000000000400514 T _fini
00000000004004a0 t frame_dummy
0000000000600e20 t __frame_dummy_init_array_entry
000000000040062c r __FRAME_END__
0000000000601000 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
0000000000400538 r __GNU_EH_FRAME_HDR
00000000004003b8 T _init
0000000000600e28 t __init_array_end
0000000000600e20 t __init_array_start
0000000000400520 R _IO_stdin_used
0000000000400510 T __libc_csu_fini
00000000004004b0 T __libc_csu_init
                 U __libc_start_main@@GLIBC_2.2.5
00000000004004a2 T main
0000000000400430 t register_tm_clones
00000000004003d0 T _start
0000000000601028 D __TMC_END__
0000000000400530 r _ZL1a
0000000000400534 r _ZL1b
0000000000601030 b _ZL1e
0000000000601020 d _ZL1f
0000000000601034 b _ZL1g

這是截圖證明上面不是我虛造的:

可以發(fā)現(xiàn): 

  • a b 前面的符號是 r,表示存放在 .rodata 段 
  • e g 前面的符號是 b,表示存放在 .bss 段 
  • f 前面的符號是 d,表示存放在 .data 段 
  • c dd 前面的符號是 B ,表示存放在 .bss 段 
  • D 前面的符號是 D,表示存放在 .data 段 

所以總結(jié)就是:

  • .bss 段:未初始化或零初始化的全局/靜態(tài)變量(B 和 b)。
  • .text 段:可執(zhí)行代碼(T 和 t)。
  • .rodata 段:只讀數(shù)據(jù)(r)。
  • .data 段:已初始化的全局/靜態(tài)變量(D 和 d)。

rodata 段在內(nèi)存哪個位置呢?

答案是:在程序的內(nèi)存布局中,.rodata 段(只讀數(shù)據(jù)段) 通常位于 代碼段(.text)和數(shù)據(jù)段(.data/.bss)之間。 

我們看前面nm 輸出中,.rodata 段的符號地址集中在 0x400520 到 0x40062c 范圍內(nèi): 

0000000000400520 R _IO_stdin_used     # .rodata 起始附近
0000000000400530 r _ZL1a             # const int a = 10
0000000000400534 r _ZL1b             # const int b = 0
000000000040062c r __FRAME_END__     # .rodata 結(jié)束附近

(1) .rodata 段地址范圍:0x400520 ~ 0x40062c 該段緊鄰代碼段(.text 段),代碼段的符號地址(如 _start、main 等)在 0x4003d0 ~ 0x400514 之間 

(2) .data 和 .bss 段地址范圍: 

  • .data 段地址:0x6001018 ~ 0x6001020 
  • .bss 段地址:0x6001020 ~ 0x6001030 這些段位于高地址區(qū)域(0x60...),與 .rodata 段(0x40...)明顯分離?!?/li>

這種布局體現(xiàn)為: 

低地址(0x400000) → 高地址(0x600000)
.text → .rodata → ... → .data → .bss

為什么 .rodata 和 .text 都在 0x40... 地址?

  • 權(quán)限隔離: .text(代碼)和 .rodata(只讀數(shù)據(jù))通常被標(biāo)記為 可讀+可執(zhí)行(r-x),而 .data 和 .bss 被標(biāo)記為 可讀+可寫(rw-)。 操作系統(tǒng)會將權(quán)限相同的段映射到相鄰區(qū)域,因此 .rodata 與 .text 位于同一地址范圍內(nèi)(0x40...)?!?/li>
  • 優(yōu)化內(nèi)存使用: 將只讀數(shù)據(jù)與代碼相鄰,可以減少內(nèi)存碎片,提高緩存命中率?!?/li>

驗(yàn)證 .rodata 的物理位置

可以通過 readelf工具直接查看段頭信息: 

readelf -S a.out

輸出示例(關(guān)鍵部分): 

[11] .text             PROGBITS         00000000004003d0  000003d0
       0000000000000141  0000000000000000  AX       0     0     16
[12] .fini             PROGBITS         0000000000400514  00000514
       0000000000000009  0000000000000000  AX       0     0     4
[13] .rodata           PROGBITS         0000000000400520  00000520
       0000000000000018  0000000000000000   A       0     0     8
[21] .data             PROGBITS         0000000000601018  00001018
       000000000000000c  0000000000000000  WA       0     0     4
[22] .bss              NOBITS           0000000000601024  00001024
       0000000000000014  0000000000000000  WA       0     0     4

.rodata 的物理地址:0x400520,與 nm 輸出的符號地址一致。 

完整信息這里也放出來:

readelf -S a.out
There are34 section headers, starting atoffset0x2708:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             000000000000000000000000
       00000000000000000000000000000000           0     0     0
  [ 1] .interp           PROGBITS         000000000040023800000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             000000000040025400000254
       00000000000000200000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             000000000040027400000274
       00000000000000240000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         000000000040029800000298
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000004002b8  000002b8
       00000000000000480000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           000000000040030000000300
       000000000000005f  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           000000000040036000000360
       00000000000000060000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          000000000040036800000368
       00000000000000200000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             000000000040038800000388
       00000000000000300000000000000018   A       5     0     8
  [10] .init             PROGBITS         00000000004003b8  000003b8
       00000000000000170000000000000000  AX       0     0     4
  [11] .text             PROGBITS         00000000004003d0  000003d0
       00000000000001410000000000000000  AX       0     0     16
  [12] .fini             PROGBITS         000000000040051400000514
       00000000000000090000000000000000  AX       0     0     4
  [13] .rodata           PROGBITS         000000000040052000000520
       00000000000000180000000000000000   A       0     0     8
  [14] .eh_frame_hdr     PROGBITS         000000000040053800000538
       000000000000002c  0000000000000000   A       0     0     4
  [15] .eh_frame         PROGBITS         000000000040056800000568
       00000000000000c8  0000000000000000   A       0     0     8
  [16] .init_array       INIT_ARRAY       0000000000600e2000000e20
       00000000000000080000000000000008  WA       0     0     8
  [17] .fini_array       FINI_ARRAY       0000000000600e2800000e28
       00000000000000080000000000000008  WA       0     0     8
  [18] .dynamic          DYNAMIC          0000000000600e3000000e30
       00000000000001c0  0000000000000010  WA       6     0     8
  [19] .got              PROGBITS         0000000000600ff0  00000ff0
       00000000000000100000000000000008  WA       0     0     8
  [20] .got.plt          PROGBITS         000000000060100000001000
       00000000000000180000000000000008  WA       0     0     8
  [21] .data             PROGBITS         000000000060101800001018
       000000000000000c  0000000000000000  WA       0     0     4
  [22] .bss              NOBITS           000000000060102400001024
       00000000000000140000000000000000  WA       0     0     4
  [23] .comment          PROGBITS         000000000000000000001024
       000000000000002c  0000000000000001  MS       0     0     1
  [24] .debug_aranges    PROGBITS         000000000000000000001050
       00000000000001000000000000000000           0     0     16
  [25] .debug_info       PROGBITS         000000000000000000001150
       00000000000003130000000000000000           0     0     1
  [26] .debug_abbrev     PROGBITS         000000000000000000001463
       00000000000001950000000000000000           0     0     1
  [27] .debug_line       PROGBITS         0000000000000000000015f8
       000000000000023f  0000000000000000           0     0     1
  [28] .debug_str        PROGBITS         000000000000000000001837
       00000000000002bd  0000000000000001  MS       0     0     1
  [29] .debug_loc        PROGBITS         000000000000000000001af4
       00000000000001550000000000000000           0     0     1
  [30] .debug_ranges     PROGBITS         000000000000000000001c50
       00000000000000800000000000000000           0     0     16
  [31] .symtab           SYMTAB           000000000000000000001cd0
       00000000000007080000000000000018          32    56     8
  [32] .strtab           STRTAB           0000000000000000000023d8
       00000000000001d8  0000000000000000           0     0     1
  [33] .shstrtab         STRTAB           0000000000000000000025b0
       00000000000001520000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

.rodata段與其他段的關(guān)系

段名

內(nèi)容

讀寫權(quán)限

位置特征

.text

可執(zhí)行代碼

只讀 + 可執(zhí)行

最低地址區(qū)域

.rodata

只讀常量(如

只讀

緊鄰

.data

已初始化全局/靜態(tài)變量

可讀寫

高于

.bss

未初始化全局/靜態(tài)變量

可讀寫

緊鄰

總結(jié)

  • 答案:const 全局變量既不在 .data 段也不在 .bss 段,而是在 .rodata 段 
  • 位置:.rodata 段位于 代碼段(.text)和數(shù)據(jù)段(.data/.bss)之間?!?/li>
  • 權(quán)限:與代碼段共享只讀屬性,但不可執(zhí)行?!?/li>
責(zé)任編輯:趙寧寧 來源: CppPlayer
相關(guān)推薦

2021-10-27 11:00:30

C++語言面試

2013-07-22 14:07:47

2010-02-02 14:06:50

C++ const變量

2011-03-29 14:31:41

CC++

2010-11-12 10:08:55

SQL Server全

2013-07-25 15:15:26

iOS開發(fā)學(xué)習(xí)iOS全局變量

2013-07-17 16:16:06

Android全局變量定義全局變量Application

2009-09-07 15:15:43

2020-06-04 14:40:40

面試題Vue前端

2022-04-12 11:38:06

C語言全局變量

2010-03-09 14:12:55

Python全局變量

2023-11-13 07:37:36

JS面試題線程

2011-03-24 13:27:37

SQL

2009-07-15 17:38:58

Jython全局函數(shù)

2021-09-07 05:02:50

C++ConstexprConst

2009-08-28 09:29:02

2009-09-24 09:28:00

Scala講座全局變量scala

2009-11-06 13:28:19

Javascript框

2018-02-01 16:26:44

面試題static變量

2011-07-20 10:06:54

CC++const
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號