欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++面試基礎(chǔ)之static關(guān)鍵字詳解

 更新時(shí)間:2019年02月24日 08:34:50   作者:riccoqu  
這篇文章主要給大家介紹了關(guān)于C++面試基礎(chǔ)之static關(guān)鍵字的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

前言

static是 c++ 的關(guān)鍵字,顧名思義是表示靜態(tài)的含義。它在 c++ 中既可以修飾變量也可以修飾函數(shù)。那當(dāng)我們使用 static 時(shí),編譯器究竟做了哪些事情呢?

早先面試中被問(wèn)到 static 關(guān)鍵字,感覺(jué)既熟悉又陌生。熟悉是都知道如何去使用它,陌生又來(lái)自不知道它究竟對(duì)我們程序做了什么。今天就來(lái)好好復(fù)習(xí)下這個(gè)關(guān)鍵字,本文的重點(diǎn)也在第三部分。

先看一下示例代碼:

test1.cpp

#include <iostream>
extern int a_int;
extern void func2();

static char c_array[10000];

void func1() {
 static int a_tmp = 0;
 std::cout << a_tmp++ << std::endl;
 return;
}

int main(int argc, char **argv) {
 a_int = 1;

 //靜態(tài)局部變量示例
 for (auto i = 0; i < 5; i++) {
 func1();
 }

 //比較靜態(tài)全局變量的地址示例
 std::cout << static_cast<const void *>(c_array) << std::endl;
 func2();
 return 0;
}

test2.cpp

#include <iostream>

int a_int;
static char c_array[1000];
void func2() {
 std::cout << static_cast<const void *>(c_array) << std::endl;
 return 0;
}

1 先說(shuō)說(shuō) extern

extern 關(guān)鍵字用于告訴編譯器,在其他的模塊中尋找相應(yīng)的定義

為什么 static 前要先說(shuō) extern 呢?因?yàn)樗麄兙拖裣嗷?duì)立的一對(duì)關(guān)鍵字,所以 extern 與 static 一起用時(shí)編譯器會(huì)報(bào)錯(cuò)~

1.1 extern 用于修飾變量

以示例代碼中的 a_int 變量為例,假設(shè)其他的變量和函數(shù)不存在

我們先 將 extern 關(guān)鍵字去掉(test1.cpp:2) ,然后執(zhí)行步驟:

  • 編譯g++ -c -o test1.o test1.cpp
  • 查看符號(hào)nm test1.o

00000000000000d0 S _a_int

可以看到 a_int 為一個(gè)未初始化的符號(hào)。說(shuō)明符號(hào)在 test1.o 中已經(jīng)被定義了。此時(shí)直接編譯( g++ -o test1 test1.cpp )是不會(huì)報(bào)錯(cuò)的。

然后我們?cè)?將 extern 關(guān)鍵字加上(test1.cpp:2) ,并重復(fù)上面步驟觀察符號(hào)

nm test1.o

會(huì)發(fā)現(xiàn) test1.o 中沒(méi)有該符號(hào)的定義。并且再編譯會(huì)報(bào)錯(cuò):

Undefined symbols for architecture x86_64:
  "_a_int", referenced from:
      _main in test1-ed3c01.o
ld: symbol(s) not found for architecture x86_64

很明顯,鏈接器沒(méi)有找到 a_int 的定義。此時(shí)只需要將 test2.o 加入再編譯(g++ -o test test1.cpp test2.cpp)就可以啦

注:此時(shí)如果去掉 main 函數(shù)中對(duì) a_int 變量的引用,也可以編譯通過(guò),畢竟 a_int 在程序中實(shí)際沒(méi)有用到

1.2 extern 用于修飾函數(shù)

以示例代碼中的 func2() 函數(shù)為例

因?yàn)樵?main 函數(shù)中調(diào)用了 func2(),所以需要在 main 之前進(jìn)行函數(shù)聲明。 但此時(shí)的函數(shù)聲明無(wú)論加不加 extern 其實(shí) 并無(wú)多少區(qū)別

1.3 extern 用于指定編譯類(lèi)型

因?yàn)?C++ 編譯時(shí)會(huì)進(jìn)行 name mangling[wiki] ,導(dǎo)致所看到的函數(shù)與實(shí)際編譯后的符號(hào)差距很大。在某些情況下會(huì)導(dǎo)致鏈接時(shí)找不到符號(hào)的問(wèn)題

此時(shí)可以使用

extern "C"
{
 ...
}

這樣在范圍內(nèi)的代碼都將按照 C 的格式進(jìn)行編譯

static 關(guān)鍵字在我看來(lái)的作用是

1.能夠改變變量的存儲(chǔ)方式

2.能夠改變變量與函數(shù)的訪問(wèn)范圍

2.1 static 用于修飾變量

我們都知道當(dāng)程序經(jīng)過(guò)編譯后:

  • 函數(shù)體內(nèi)的局部變量會(huì)保存在棧中,局部變量隨著函數(shù)的調(diào)用和返回進(jìn)行構(gòu)造與析構(gòu),并且在函數(shù)返回后無(wú)法使用。
  • 全局變量保存在靜態(tài)數(shù)據(jù)區(qū)直到程序退出時(shí)才會(huì)被析構(gòu)掉。所以在整個(gè)程序內(nèi)全局變量都可以使用(當(dāng)然要考慮到作用域)。

對(duì)于局部變量,當(dāng)我們?cè)谧兞壳凹由?static 時(shí),就是告訴了編譯器將該變量放入靜態(tài)數(shù)據(jù)區(qū)。既函數(shù)退出時(shí)不會(huì)將該變量析構(gòu)掉,當(dāng)我們下次再調(diào)用改函數(shù)依然可以取得內(nèi)存中的這個(gè)變量。
例如 test1.cpp 中,每次調(diào)用 func1() 時(shí) a_tmp 變量都不會(huì)被銷(xiāo)毀,最后輸出

0
1
2
3
4

對(duì)于全局變量,加上 static 關(guān)鍵字后該變量只能用于當(dāng)前的文件。

例如 test1.cpp 中的 c_array,加上 static 后只能在當(dāng)前源文件使用。

此時(shí)如果我們?cè)僭?test2.cpp 中定義一個(gè)同名的全局靜態(tài)數(shù)組進(jìn)行編譯(g++ -o test test1.cpp test2.cpp)并且輸出他們的地址

test1.cpp[c_array]0x10bb5e100
test2.cpp[c_array]0x10bb60810

可以看到兩個(gè)地址是不同的,所以雖說(shuō)是同名的兩個(gè)全局變量。但都經(jīng)過(guò) static 修飾后,他們實(shí)際還是兩個(gè)地址不同相互的變量。

那么再試一下,將 test1.cpp 中的

static char c_array[10000];

修改為

extern char c_array[10000];

然后再編譯(g++ -o test test1.cpp test2.cpp)可以看到

Undefined symbols for architecture x86_64:
  "_c_array", referenced from:
      _main in test1-5d6201.o
ld: symbol(s) not found for architecture x86_64

這當(dāng)然是因?yàn)?test2.cpp 中的 c_array 還是有 static 進(jìn)行修飾的,導(dǎo)致我們無(wú)法在 test1.cpp 文件中訪問(wèn)到。那就將 static 去掉,看到結(jié)果

test1.cpp[c_array]0x10b1e2100
test2.cpp[c_array]0x10b1e2100

它們的地址相同對(duì)應(yīng)的同一塊內(nèi)存,是同一個(gè)變量!

2.2 STATIC 用于修飾函數(shù)

static 對(duì)于函數(shù)于變量其實(shí)比較類(lèi)似,它限定了函數(shù)只能在當(dāng)前的模塊中使用。

假如我們將 test2.cpp 中的 func2() 函數(shù)加上 static 關(guān)鍵字,那么編譯(g++ -o test test1.cpp test2.cpp)也會(huì)報(bào)錯(cuò)找不到符號(hào)

Undefined symbols for architecture x86_64:
  "func2()", referenced from:
      _main in test1-80a5c0.o
ld: symbol(s) not found for architecture x86_64

3 關(guān)于 text、bss 與 data 段

關(guān)于數(shù)據(jù)段、編譯、鏈接方面的知識(shí)非常推薦看看<<程序員的自我修養(yǎng):鏈接、裝載與庫(kù)>>

3.1 局部變量的編譯

是否曾經(jīng)好奇函數(shù)內(nèi)的臨時(shí)變量經(jīng)過(guò)編譯會(huì)變成什么樣子?

假設(shè)我們寫(xiě)了如下代碼,并編譯成名為 test 的可執(zhí)行文件

int main() {
 char s1[11] = "helloworld";
 char s2[11] = "helloworld";
 return 0;
}

那么可以通過(guò) objdump -DS test觀察到 main 函數(shù)中有如下片段(有省略)

Disassembly of section .text:
.....
00000000004005b0 <main>:
4005b4: 48 b8 68 65 6c 6c 6f movabs $0x726f776f6c6c6568,%rax
4005bb: 77 6f 72
4005be: 48 89 45 f0 mov %rax,-0x10(%rbp)
4005c2: 66 c7 45 f8 6c 64 movw $0x646c,-0x8(%rbp)
4005c8: c6 45 fa 00 movb $0x0,-0x6(%rbp)
4005cc: 48 b8 68 65 6c 6c 6f movabs $0x726f776f6c6c6568,%rax
4005d3: 77 6f 72
4005d6: 48 89 45 e0 mov %rax,-0x20(%rbp)
4005da: 66 c7 45 e8 6c 64 movw $0x646c,-0x18(%rbp)
4005e0: c6 45 ea 00 movb $0x0,-0x16(%rbp)
......

觀察下 0x646c 和 0x726f776f6c6c6568,轉(zhuǎn)化成 ascii 就是

100 108 114 111 119 111 108 108 101 104

對(duì)應(yīng)的字符

‘d’ ‘l’ ‘r’ ‘o’ ‘w’ ‘o’ ‘l’ ‘l’ ‘e’ ‘h’,看出來(lái)了吧,編譯器將 “helloworld” 以立即數(shù)的方式寫(xiě)到了 text 段內(nèi)。

然后通過(guò) readelf -a test會(huì)發(fā)現(xiàn)并沒(méi)有 s1 與 s2 的符號(hào)。

現(xiàn)在將代碼改為這樣又會(huì)如何?

static char s1[11] = "helloworld";
static char s2[11] = "helloworld";

繼續(xù)通過(guò) objdump -DS test觀察發(fā)現(xiàn) main 中發(fā)生了改變

Disassembly of section .text:
00000000004005b0 <main>:
  4005b0: 55                    push   %rbp
  4005b1: 48 89 e5              mov    %rsp,%rbp
  4005b4: b8 00 00 00 00        mov    $0x0,%eax
  4005b9: 5d                    pop    %rbp
  4005ba: c3                    retq
  4005bb: 0f 1f 44 00 00        nopl   0x0(%rax,%rax,1)

通過(guò) readelf -a test可以看到新增了兩個(gè)地址不同的符號(hào),由此可見(jiàn) static 確實(shí)改變了變量的存儲(chǔ)方式

Symbol table '.symtab' contains 66 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
   37: 000000000060102c    11 OBJECT  LOCAL  DEFAULT   24 _ZZ4mainE2s2
   38: 0000000000601037    11 OBJECT  LOCAL  DEFAULT   24 _ZZ4mainE2s1

那么如果指向常量呢?稍微改下

const char *s1 = "helloworld";
const char *s2 = "helloworld";

繼續(xù)通過(guò) objdump 觀察到 main 中有這兩代碼,很明顯了 0x400660存儲(chǔ)著我們的 “helloworld”的字符串常量

00000000004005b0 <main>:
  4005b4: 48 c7 45 f8 60 06 40  movq   $0x400660,-0x8(%rbp)
  4005bb: 00
  4005bc: 48 c7 45 f0 60 06 40  movq   $0x400660,-0x10(%rbp)

找到這個(gè)地址,發(fā)現(xiàn)這個(gè)地址屬于 .rodata 段。這就是我們常說(shuō)用來(lái)保存字面值常量的數(shù)據(jù)段。

Disassembly of section .rodata:
0000000000400658 <__dso_handle>:
 ...
  400660: 68 65 6c 6c 6f        pushq  $0x6f6c6c65
  400665: 77 6f                 ja     4006d6 <__dso_handle+0x7e>
  400667: 72 6c                 jb     4006d5 <__dso_handle+0x7d>
  400669: 64                    fs

觀察下十六進(jìn)制的值,就是我們的 “helloworld” 沒(méi)錯(cuò)啦。

3.2 全局變量的編譯

那么對(duì)于全局變量又應(yīng)該是如何存儲(chǔ)的呢?

首先我們知道無(wú)論靜態(tài)還是非靜態(tài)的變量都應(yīng)該存儲(chǔ)在靜態(tài)數(shù)據(jù)區(qū)。我們熟悉的靜態(tài)數(shù)據(jù)區(qū)就有 .bss 和 .data。
.bss 在編譯時(shí)實(shí)際上不占據(jù)空間,只有在運(yùn)行時(shí)才會(huì)由被分配空間。那么還是來(lái)驗(yàn)證下

char a_array[10000];
static char b_array[10000];
int main() {
 return 0;
}

編譯一下(g++ -o test test.cpp),然后通過(guò) size 命令觀察(size test)

text    data     bss     dec     hex filename
1320     588   20048   21956    55c4 test

可以看出 a_array 和 b_array 都實(shí)際記錄在 .bss 段,并且 .data段的大小顯然不符合我們定義的數(shù)組大小。通過(guò) ll test會(huì)發(fā)現(xiàn)文件大小不足10000 字節(jié),所以可以肯定的是申請(qǐng)的這兩個(gè)數(shù)組在編譯時(shí)并為被分配內(nèi)存。

那么繼續(xù)改一下看看

char a_array[10000] = "helloworld";
static char b_array[10000];

繼續(xù)使用 size test看下

text    data     bss     dec     hex filename
1320   10616   10032   21968    55d0 test

data 段和文件都多出了 10000 多字節(jié)!?。?/p>

這就是因?yàn)?a_array 進(jìn)行了初始化,所以編譯器為其分配了內(nèi)存。同理如果 b_array 也進(jìn)行了初始化,那么大小還會(huì)增加。

tips:

如果進(jìn)行了初始化,但是內(nèi)存中還是 0 值的話,編譯器依舊不會(huì)為其分配內(nèi)存的,例如

int a_array[10000] = {0};

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • C語(yǔ)言指針入門(mén)的簡(jiǎn)單實(shí)例教程

    C語(yǔ)言指針入門(mén)的簡(jiǎn)單實(shí)例教程

    這篇文章主要給大家介紹了關(guān)于C語(yǔ)言指針入門(mén)的簡(jiǎn)單實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • C++中l(wèi)ist的使用與模擬實(shí)現(xiàn)

    C++中l(wèi)ist的使用與模擬實(shí)現(xiàn)

    list相較于vector來(lái)說(shuō)會(huì)顯得復(fù)雜,它的好處是在任意位置插入,刪除都是一個(gè)O(1)的時(shí)間復(fù)雜度,下面這篇文章主要給大家介紹了關(guān)于C++中l(wèi)ist的使用與模擬實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下
    2022-05-05
  • C++實(shí)現(xiàn)LeetCode(208.實(shí)現(xiàn)字典樹(shù)(前綴樹(shù)))

    C++實(shí)現(xiàn)LeetCode(208.實(shí)現(xiàn)字典樹(shù)(前綴樹(shù)))

    這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(208.實(shí)現(xiàn)字典樹(shù)(前綴樹(shù))),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • C語(yǔ)言可變長(zhǎng)的參數(shù)列表詳解

    C語(yǔ)言可變長(zhǎng)的參數(shù)列表詳解

    這篇文章主要為大家介紹了C語(yǔ)言可變長(zhǎng)的參數(shù)列表,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-01-01
  • 深入了解C++優(yōu)先隊(duì)列(priority_queue)的使用方法

    深入了解C++優(yōu)先隊(duì)列(priority_queue)的使用方法

    在計(jì)算機(jī)科學(xué)中,優(yōu)先隊(duì)列是一種抽象數(shù)據(jù)類(lèi)型,它與隊(duì)列相似,但是每個(gè)元素都有一個(gè)相關(guān)的優(yōu)先級(jí)。C++中的優(yōu)先隊(duì)列是一個(gè)容器適配器(container adapter),它提供了一種在元素之間維護(hù)優(yōu)先級(jí)的方法。本文帶你深入了解C++優(yōu)先隊(duì)列的使用方法,需要的可以參考下
    2023-05-05
  • C++ 中回調(diào)函數(shù)詳解及簡(jiǎn)單實(shí)例

    C++ 中回調(diào)函數(shù)詳解及簡(jiǎn)單實(shí)例

    這篇文章主要介紹了C++ 中回調(diào)函數(shù)詳解及簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • Qt模仿IOS滑動(dòng)按鈕效果

    Qt模仿IOS滑動(dòng)按鈕效果

    這篇文章主要為大家詳細(xì)介紹了Qt模仿IOS滑動(dòng)按鈕效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-08-08
  • 深入C++四種強(qiáng)制類(lèi)型轉(zhuǎn)換的總結(jié)

    深入C++四種強(qiáng)制類(lèi)型轉(zhuǎn)換的總結(jié)

    本篇文章是對(duì)C++中四種強(qiáng)制類(lèi)型轉(zhuǎn)換進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • C++實(shí)現(xiàn)乒乓球比分判定

    C++實(shí)現(xiàn)乒乓球比分判定

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)乒乓球比分判定,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • C++數(shù)據(jù)結(jié)構(gòu)深入探究棧與隊(duì)列

    C++數(shù)據(jù)結(jié)構(gòu)深入探究棧與隊(duì)列

    棧和隊(duì)列,嚴(yán)格意義上來(lái)說(shuō),也屬于線性表,因?yàn)樗鼈円捕加糜诖鎯?chǔ)邏輯關(guān)系為 "一對(duì)一" 的數(shù)據(jù),但由于它們比較特殊,本章講解分別用隊(duì)列實(shí)現(xiàn)棧與用棧實(shí)現(xiàn)隊(duì)列
    2022-05-05

最新評(píng)論