電子產(chǎn)業(yè)一站式賦能平臺

PCB聯(lián)盟網(wǎng)

搜索
查看: 67|回復(fù): 0
收起左側(cè)

C語言為什么不檢查數(shù)組下標(biāo)

[復(fù)制鏈接]

485

主題

485

帖子

1623

積分

三級會員

Rank: 3Rank: 3

積分
1623
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2022-5-24 08:30:00 | 只看該作者 |只看大圖 回帖獎勵 |正序?yàn)g覽 |閱讀模式
引言+ F: |$ j, H6 E6 h3 y- _
最近在查一個bug,查到最后發(fā)現(xiàn)是數(shù)組越界導(dǎo)致的。數(shù)組只有30個字節(jié),代碼卻向這個數(shù)組填充了35個數(shù)據(jù),這個bug還是偶現(xiàn)的,查到它確實(shí)廢了一番功夫。我就突然想到:C語言為什么不檢查數(shù)組下標(biāo)呢???先來個demo驗(yàn)證下
1 \# f( C* R# N1 Y2 Q1 t" W
  • #include#include
    . _5 l- o# w6 e, G& O9 Uint main(){    int data[5]={0};    for(int i=0;i8;++i)    {        printf("%d ",data);    }    printf("
    1 G' i& `# t7 i7 o( P");
    & n2 ^5 |- ?0 k, y9 C' U" b    return 0;}結(jié)果顯示,C語言還真的不檢查數(shù)組的下標(biāo)。不僅沒有報錯,而且運(yùn)行正常9 O& r) j5 m) a$ Z' G* D
    ; i! Q- _) N2 m" q0 n0 \% W4 L
    思考
    ' d: [/ i4 s  ~這就讓我陷入了思考,C語言為什么不檢查下標(biāo)呢?想上文這么簡單的,data數(shù)據(jù)組就5個數(shù)據(jù),編譯器是知道的,為什么是訪問第8個數(shù)據(jù)時,編譯器來個報錯也沒有呢?我想到了之前的文章《指針與數(shù)組》中有如下示例代碼:% v4 W, U1 D8 Z/ n0 y3 S* t4 m5 Y
  • void main(){    int data[4] = {0, 1, 2, 3};    int *p;    p = data +2;    printf("p[-1] is %d, R& Z0 G9 G4 K+ s" h
    ",p[-1]);    printf("*(p-1) is %d) |/ U7 e( m- V+ ~
    ",*(p-1));}運(yùn)行結(jié)果如下
    & a$ K- B' B7 v# k7 u! y
    : _% P8 J. T, v- V$ v不僅可以編譯通過,還能正確的輸出結(jié)果為1。這表明,C的下標(biāo)引用和間接訪問表達(dá)式是一樣的。這讓我突然意識到,數(shù)組的這些特性,如數(shù)組名本質(zhì)上是一個常量指針(不懂的同學(xué)看之前的推文《指針與數(shù)組》)C語言很難檢查下標(biāo)合法性的。如果C語言檢查數(shù)組是否越界,因?yàn)楫?dāng)數(shù)組出現(xiàn)在表達(dá)式中的時候,它會立刻被解讀成指針。此外,使用其他的指針變量也可以指向數(shù)組的任意元素,并且這個指針可以隨意進(jìn)行加減運(yùn)算。引用數(shù)組元素的時候,雖然你可以寫成a,但是它只不過是*(a+i)的一種表達(dá),C語言本身的語法是無法檢查的,只能通過編譯器檢查。那么編譯器將加入額外的代碼用于檢測數(shù)組是否越界,C的下標(biāo)檢查所涉及的開銷比你開始想象的要多。編譯器必須在程序中插入指令,證實(shí)下標(biāo)的結(jié)果所引用的元素和指針表達(dá)式所指向的元素屬于同一個數(shù)組,可能僅僅是個小功能,生成的程序的數(shù)組檢查占有大量的代碼空間,這必將影響程序的運(yùn)行效率。這也讓我意識到一個事情:數(shù)組的標(biāo)識符(也就是數(shù)組名),它只包含并沒有包含數(shù)組的長度的信息,它只是個地址信息,也就是上面說的數(shù)組名本質(zhì)上是個常量指針。讀到這里,請你想一下,C語言有提供數(shù)組長度的底層函數(shù)嗎???答案是否定的,一般情況下,我們獲取一個數(shù)組的長度,我們可以獲取數(shù)組所占的內(nèi)存大小,然后除以單個元素的內(nèi)存大小計算數(shù)組長度。
  • int a[8];printf("%d",sizeof(a)/sizeof(a[0]));
    3 s) V6 z  j& d為什么不修復(fù)“漏洞”
    - D- U9 r6 n$ Q- D, O$ m4 N+ W$ S& ?既然我們發(fā)現(xiàn)了上述問題,那么那些C語言的大神為什么不修復(fù)這個“漏洞”呢?其他編程語言會吸取“教訓(xùn)”嗎?學(xué)過JAVA的同學(xué)可以看下面代碼. N1 S1 A# j5 {3 P  \/ s5 ~, |7 n1 n
  • int [][] array = {{1,2,3},{1,4}};System.out.println(array[1][2]);這也是一個數(shù)組越界訪問的例子,但是JAVA的控制臺會打印如下信息Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2% Y. g# @5 V' R; L" Y
    at demo.Array.main(Array.java:31)
    , f( ^( v# O* E會明確告訴你數(shù)組下標(biāo)越界了,是的,高級語言JAVA是支持的。那么我們就來講講C語言的設(shè)計目標(biāo):提供一種能以簡易的方式編譯、處理低級存儲器、僅產(chǎn)生少量的機(jī)器碼以及不需要任何運(yùn)行環(huán)境支持便能運(yùn)行的編程語言。如果C語言加入了類似下標(biāo)檢查,實(shí)現(xiàn)一個簡單的數(shù)組數(shù)據(jù)寫入,需要大量指令檢查下標(biāo)是否正確,那么還符合C語言設(shè)計目標(biāo)嗎?如果C語言有大量的這樣設(shè)計,操作系統(tǒng)內(nèi)核還會使用C語言編寫嗎?單片機(jī)等實(shí)時系統(tǒng)還會使用C語言嗎?所以C語言給了程序員更大空間,C語言執(zhí)行效率高,可以直接訪問硬件,具有非常好的可移植性,所以世界上絕大部分的操作系統(tǒng)內(nèi)核都是用C語言編寫的。那么問題來了,JAVA都檢查了數(shù)組下標(biāo),C語言難道一點(diǎn)進(jìn)步也沒有嗎?其實(shí)也不然,微軟在這一方面也做了貢獻(xiàn)。在早期的CRT函數(shù)中也不對字符串指針或數(shù)組進(jìn)行越界檢查,都是要求程序員確保空間足夠,因此也才也才有了在VS2005之后微軟提供的安全的CRT函數(shù)版本。(CRT函數(shù)不是本文的重點(diǎn),不懂的同學(xué)請面向百度編程)。, p' ?7 B/ s- w0 H6 @
    總結(jié)2 F/ H) n# w9 l( q& u
    C語言為什么不檢查數(shù)組下標(biāo)???答案一個字:
    ) D) J) `4 t* u7 n0 \
    7 o7 h# s; R' X+ I5 ?4 nEND
    # w! e( J) k8 C8 A$ L! D& _
    8 W) P$ W2 |) L  i- | / Y% Q- P) b' p* o
    ?STM32 IIC詳解
    5 ?# Z* H  P5 O& b: D8 \" p?VScode 調(diào)試C語言 必讀3 w' D  ^, _2 N/ p5 U, X( ]
    ?單片機(jī)中volatile的應(yīng)用* g5 Z6 y1 ^+ n! f; F
    ?聯(lián)合體在單片機(jī)編程中的應(yīng)用  必讀
    1 ^1 O) b5 Q& o- Y( P?STM32串口開發(fā)之環(huán)形緩沖區(qū)
  • 回復(fù)

    使用道具 舉報

    發(fā)表回復(fù)

    您需要登錄后才可以回帖 登錄 | 立即注冊

    本版積分規(guī)則


    聯(lián)系客服 關(guān)注微信 下載APP 返回頂部 返回列表