色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

從一次字符串拼接失敗說起

CPP開發者 ? 來源:CPP開發者 ? 2023-05-15 14:30 ? 次閱讀

幾個月前的時候,有一次討論,關于單例模式實現的,其中,提到了一種使用static方式,也就是Scott Meyers提出的另一種更優雅的單例模式實現,俗稱Scott Meyers單例模式。當時聊到的一個關鍵點是靜態變量的初始化線程安全問題,今天借助本文,聊聊靜態變量的另外一個問題:靜態變量初始化順序

從一個示例開始

首先看下如下代碼:

static_test.h

#include

externstd::stringstr;

static_test.cc

std::stringstr="test";

main.cc

#include"static_test.h"
#include

staticstd::stringmsg="hello"+str+"world!";

intmain(){
std::cout<

好了,在閱讀下文之前,不妨先思考下,main()函數中的輸出結果是什么,很多人第一反應是hello test world!,恭喜你,跟我一樣,答錯了~~~

現在看下編譯器的結果:

g++-gstatic_test.ccmain.cc-ostatic_test&&./static_test
helloworld

沒錯,編譯器的輸出結果是hello world!

之所以編譯器的輸出與我們的預期不一致,是因為靜態變量初始化順序導致。

初始化

我們知道,對于已經初始化的全局和靜態變量時存放在可執行文件的數據段(.data),對于未初始化的全局和靜態變量,則在BSS段中。如果對這塊沒有做過深入的研究,往往很容易出錯,先看下示例:

structTest{
inti;
Test(intii):i(ii){}
Test(){}
};

Testt1=Test(5);
Testt2;
staticTestt3;
staticTestt4{5};

inti=1;
intj;
staticintk;
staticintl=1;


intmain(){
return0;
}

相信很多人看了上面代碼后給出的答案會是t1 t4 i l 在.data,t2 t3 j k在.bss。在給出答案之前,不妨看下編譯器的輸出結果:

g++ test.cpp && objdump -dj .data a.out:

a.out:fileformatelf64-x86-64


Disassemblyofsection.data:

0000000000600a88<__data_start>:
...

0000000000600a90<__dso_handle>:
...

0000000000600a98:
600a98:01000000....

0000000000600a9c<_ZL1l>:
600a9c:01000000....

objdump -dj .bss a.out:

a.out:fileformatelf64-x86-64


Disassemblyofsection.bss:

0000000000600aa0:
...

0000000000600aa8:
...

0000000000600ab0:
600ab0:00000000....

0000000000600ab4:
600ab4:00000000....

0000000000600ab8:
600ab8:00000000....

0000000000600abc<_ZL2t3>:
600abc:00000000....

0000000000600ac0<_ZL2t4>:
600ac0:00000000....

0000000000600ac4<_ZL1k>:
600ac4:00000000....

從上述輸出可知只有i、l在.data段,其它的在.bss段,還有一個比較有意思的點就是**.bss段的數據都被0進行初始化**,針對這兩個問題:

?t1 t2 t3 t4都調用了構造函數(有些是拷貝有些是默認構造函數)進行了初始化,但因為其類型不是POD,所以其被放在bss段

?編譯器默認的編譯選項是**-fzero-initialized-in-bss,即對bss段進行0初始化,如果不想進行0初始化,可以使用-fno-zero-initialized-in-bss**

針對上面的輸出,i、l在.data段,可稱之為常量初始化,而其它變量在.bss段且被0初始化,稱之為0初始化。從可執行程序的角度來說,如果一個數據未被初始化,就不需要為其分配空間,所以.data 和.bss 的區別就是 .bss 并不占用可執行文件的大小,僅僅記錄需要用多少空間來存儲這些未初始化的數據,而不分配實際空間,編譯器往往通過memset(bss_str, len, 0)進行初始化,類似于如下這種:

staticvoidzero_fill_bss(void)
externchar__START_BSS[];
externchar__END_BSS[];

memset(__START_BSS,0,(__END_BSS-__START_BSS));
}

看到這,可能大家會有個疑問,.bss段什么時候會進行真正的初始化呢?記得一開始接觸全局變量和靜態變量的時候,書上就有提到,在可執行程序執行之前(main函數運行之前),會進行一些初始化操作,.bss就是在這個階段進行初始化的。也就是說.data和.bss段的數據,在main()函數執行之前就初始化完成,那么,可以得出的結論是這部分數據不存在多線程競爭的問題(main()函數執行前還不存在多線程現象)。

根據標準的定義:

Together, zero-initialization and constant initialization are called static initialization; all other initialization isdynamic initialization.

也就是說要將靜態變量活全局變量初始化分類的話,可以分為靜態初始化動態初始化,其中靜態初始化已經在上面例子中講到,就是說編譯器在編譯的過程中完成(包括常量初始化和0初始化兩種),剩下的就是動態初始化:

Dynamic initialization happens at runtime for variables that can’t be evaluated at compile time2. Here, static variables are initialized every time the executable is run and not just once during compilation

動態初始化,又稱為運行時初始化或者懶漢式初始化,是指在程序運行階段才能完成的初始化,比如動態分配的內存,通過函數參數進行初始化賦值,或者使用函數返回值初始化等等,常見于函數調用方式,如下:

intfun(){
staticinta=0;
returna;
}

intmain(){
intx=fun();
return0;
}

初始化順序

在上一節中,我們聊到了編譯器對靜態變量的初始化相關知識點,c++標準規定,在同一個編譯單元中,對全局變量或者靜態變量的初始化順序與其定義順序一致。但是對于不同的編譯單元中的靜態變量的初始化順序,標準沒有做規定,也就是說假如兩個全局靜態變量A和B分別存在與兩個.cc文件中,那么編譯器對于這倆的初始化順序是不確定的,而正是因為這個原因,才是導致了文章開頭示例的輸出結果不符合語氣的關鍵。對于這種因為不同編譯單元初始化順序導致的異常,cppreference將其稱之為Static Initialization Order Fiasco

Thestatic initialization order fiascorefers to the ambiguity in the order that objects with static storage duration in different translation unitsare initializedin. If an object in one translation unit relies on an object in another translation unit already being initialized, a crash can occur if the compiler decides to initialize them in the wrong order. For example, the order in which .cpp files are specified on the command line may alter this order. The Construct on First Use Idiom can be used to avoid the static initialization order fiasco and ensure that all objects are initialized in the correct order.

Within a single translation unit, the fiasco does not apply because the objects are initialized from top to bottom.

繼續回到文章開頭的示例,在程序執行main()函數之前,進行初始化操作,因為沒有規定不同編譯單元中的初始化順序,所以先初始化main.cc中的靜態變量msg為hello world!(因為此時static_test.cc中的str還未進行初始化),然后再初始化static_test.cc中的靜態變量。接著執行main()函數,進行輸出操作...

解決

既然出現了因為不同編譯單元中的靜態變量初始化導致,那么就需要針對性的解決這個問題,通常有如下幾個方案:

?將所有的靜態全局變量放在一個編譯單元中(如果涉及到依賴的話,需要修改順序)

?強制編譯器在編譯階段進行初始化,通常有constexprconstinit兩種

?Initialization On First Use,即在使用時候,通過函數獲取靜態對象的方式進行初始化:

//static_test.h
#include

staticstd::stringstr;

//static_test.cc
std::stringGetStr(){
str="test";
reurnstr;
}

//main.cc
#include"static_test.h"
#include

staticstd::stringmsg="hello"+GetStr()+"world!";

intmain(){
std::cout<

?指定初始化優先級(即順序,以下實現僅限于gcc,msvc未做研究):

//static_test.h
#include

staticstd::stringstr;

//static_test.cc
std::string__attribute__((init_priority(300)))str="test";

//main.cc
#include"static_test.h"
#include

staticstd::string__attribute__((init_priority(400)))msg="hello"+str+"world";

intmain(){
std::cout<

在上述代碼中指定了靜態變量str的優先級300,msg的優先級400,那么在執行的時候,會先初始化str,然后初始化msg,這樣就會得到預期結果。

結語

靜態變量在程序中使用很常見,其引起的靜態初始化順序難題也就隨之而來,對于這種初始化順序導致的異常,通過很難察覺,由于標準沒有規定執行標準,因此編譯器往往也不會給出報錯或者警告。所以,在寫代碼的時候,應該避免這種情況的發生,當有時候不得不使用靜態變量的時候,需要注意是否會導致初始化順序問題,如果遇到了,則開源參考上一節的解決方式~~

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 字符串
    +關注

    關注

    1

    文章

    578

    瀏覽量

    20508
  • 函數
    +關注

    關注

    3

    文章

    4329

    瀏覽量

    62575
  • 代碼
    +關注

    關注

    30

    文章

    4780

    瀏覽量

    68535
  • 編譯器
    +關注

    關注

    1

    文章

    1624

    瀏覽量

    49109
  • 靜態變量
    +關注

    關注

    0

    文章

    13

    瀏覽量

    6645

原文標題:從一次字符串拼接失敗說起

文章出處:【微信號:CPP開發者,微信公眾號:CPP開發者】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    labview在循環中顯示字符串,當字符串為空時,保持上一次的值,不顯示空字符串

    在循環中顯示字符串,當字符串為空時,保持上一次的值,不顯示空字符串
    發表于 10-19 17:25

    字符串的表示

    字符串的表示  隨著計算機在文字處理與信息管理中的廣泛應用, 字符串已成為最常用的數據類型之, 許多計算機中都提供字符串操作功能, 些計
    發表于 10-13 17:11 ?3065次閱讀
    <b class='flag-5'>字符串</b>的表示

    mysql運行拼接字符串和導出數據

    “prepare+execute” 學習存儲過程中發現sql語句有些部分不能夠使用變量,因此采用拼接字符串的形式,然后執行字符串代表的SQL。基本形式如下: set @ sql =concat
    發表于 11-28 20:37 ?1097次閱讀

    python字符串拼接方式了解

    python字符串拼接的方式 在Python的實際開發中,很多都需要用到字符串拼接,python中字符串
    發表于 12-06 10:09 ?1032次閱讀

    什么是復制字符串?Python如何復制字符串

    連續幾篇文章都在寫 Python 字符串,這出乎我的意料了。但是,有的問題,不寫不行,特別是那種靈機動想到的問題,最后你發現,很多人根本不懂卻又誤以為自己懂了。那就繼續刨根問底,探究個明白吧
    發表于 11-25 10:32 ?3011次閱讀

    詳解Python如何拼接字符串

    占位符,它僅代表字符串,并不是拼接的實際內容。實際的拼接內容在個單獨的%號后面,放在個元
    發表于 11-26 11:16 ?1044次閱讀

    字符串函數重寫練習

    字符串函數重寫練習:字符串比較、字符串拼接字符串查找、字符串拷貝、內存比較、內存拷貝、內存初始
    的頭像 發表于 05-05 15:02 ?1992次閱讀

    C語言總結_字符串函數封裝練習

    字符串函數重寫練習:字符串比較、字符串拼接字符串查找、字符串拷貝、內存比較、內存拷貝、內存初始
    的頭像 發表于 08-14 09:42 ?993次閱讀

    labview字符串控件二封裝

    Labiew字符串控件二封裝,用起來方便的很.
    發表于 11-14 15:14 ?4次下載

    文詳解JavaScript字符串

    JavaScript字符串是原始值。此外,字符串是不可變的。這意味著如果你修改一個字符串,你總是會得到個新的字符串。原始
    的頭像 發表于 12-08 16:36 ?1197次閱讀

    字符串的相關知識

    TCL 中的數據類型只有種:字符串。這些字符串可以是字母、數字、布爾值、標點符號等特殊字符的組合。在某些特殊命令的作用下,字符串可以向其他
    的頭像 發表于 03-29 11:41 ?1147次閱讀

    python輸出固定長度的字符串

    Python 是種強大而靈活的編程語言,具有許多用于處理字符串的功能。在 Python 中,有多種方法可以輸出固定長度的字符串。下面將詳細介紹這些方法。 方法:使用
    的頭像 發表于 11-22 10:41 ?3306次閱讀

    oracle中拼接字符串函數

    , string2) 其中,string1 和 string2 是需要連接的字符串參數。 除了 CONCAT 函數,Oracle 還提供了些其他的字符串拼接函數和操作符,這些函數和操
    的頭像 發表于 12-06 09:49 ?2912次閱讀

    oracle拼接字符串函數wm_con

    在Oracle數據庫中,有時候我們需要將多個字符串拼接一個字符串,以滿足特定的需求。而Oracle提供了個非常方便的函數,就是WM_CONCAT函數。本文將詳細介紹WM_CONCA
    的頭像 發表于 12-06 09:51 ?1759次閱讀

    labview掃描字符串怎么用

    介紹如何在 LabVIEW 中使用掃描字符串以及相關的技巧和注意事項。 字符串是 LabVIEW 中的種基本數據類型,表示系列字符的序列
    的頭像 發表于 12-29 10:12 ?1984次閱讀
    主站蜘蛛池模板: 国产精品久久久久a影院| 亚洲高清国产拍精品影院| 麻豆免费高清完整版| 黄色三级网络| 国产婷婷色一区二区三区在线| 成人手机在线观看| xnxx高中生| www.青青草| 纯肉高H放荡受BL文库| 波多结衣一区二区三区| RUNAWAY韩国动漫免费网| cntv官网| 成人区精品一区二区不卡AV免费| chinese极品嫩模videos| 99热只有这里有精品| 99久久亚洲综合精品| freehd另类xxxx喷水| GOGOGO高清在线播放免费| YIN荡的老师系列第6部分视频| oldgrand欧洲老妇人| www.99在线| 粉嫩AV国产一区二区福利姬| 动漫美女性侵| 国产精品视频一区二区猎奇| 国产手机在线亚洲精品观看| 国内精品免费视频精选在线观看| 好男人午夜www视频在线观看 | 中文国产在线观看| 在线观看视频中文字幕| 123超碰在线视频| WWW国产精品人妻一二三区| 成人免费观看在线视频| 国产精品成人网| 护士WC女子撒尿| 毛片免费在线| 日韩欧美一区二区三区免费看 | 欧美日韩一级黄色片| 日韩在线视频www色| 亚洲不卡视频在线| 亚洲天堂av2017| 98久久人妻少妇激情啪啪|