2017-07-19 85 views
-3

這是我的示例代碼。strlen給出意想不到的輸出C

#include <stdio.h> 
#include <string.h> 

int main() { 
    char a[3] = { 'H', 'E', 'L', 'L', 'O', '\0' }; 
    printf("Length is %zd ", strlen(a)); 
} 

我知道這會產生以下警告!現在

test.c:5:26: warning: excess elements in array initializer 
char a[3] = {'H','E','L','L','O','\0'}; 

我的問題是,如果我指定的大小a[6]或任何比實際尺寸我的輸出將更大。

char a[100] = {'H','E','L','L','O','\0'};

輸出:

Length is 5 

char a[10] = {'H','E','L','L','O','\0'};

輸出:

Length is 5 

任何超過數組大小等於或大於我得到正確的輸出。

但是,當我給什麼小於實際尺寸總是我越來越 作爲輸出。

char a[5] = {'H','E','L','L','O','\0'};a[4]a[3]a[2]它總是

Length is 6

雖然對

char a[1] = {'H','E','L','L','O','\0'}; 

Length is 1

這是什麼原因?任何詳細的解釋是值得歡迎的。

+16

未定義的行爲? –

+3

這個警告不會讓編譯器編寫者感覺更好,你知道。你不知道爲什麼*「數組初始值設定項中的多餘元素」*可能是一個問題? – StoryTeller

+0

@s_vishnu是的。我得到9. –

回答

7

僅僅因爲你給了一個具有6個字符的初始化器,並不意味着這些字符實際上存儲在這些小陣列的任何地方。

過量的初始化器被丟棄。如果你不知道自己在做什麼,可能會返回並使你的程序不合格。

讓我們考慮一下a有兩個字符的空間的情況。編譯器知道這一點,所以它像這樣初始化它:

| 'H' | 'e' | 

就是這樣。雖然它是一個完全有效的字符數組,但它不是有效的C字符串。因爲數組不是\0終止。一旦你將這個數組提供給一個需要C字符串的庫函數,你不能保證任何東西。它使你的整個程序的行爲undefined

但實際上,strlen只會訪問超出您定義的數組邊界的內存。不知道它可以在那裏找到,或者即使它會有史以來找到一個\0並返回。

+1

沒有提到標準中會丟棄過多的初始化器,因此我相信這是由UB省略 –

+0

只有一個提到如果元素較少會發生什麼:http://port70.net/~nsz/c/c11 /n1570.html#6.7.9p21 –

+1

@AnttiHaapala - [「否則,從列表中只有足夠的初始化器被用來解釋子集或者包含聯合的第一個成員的元素或成員」](http:// port70達網絡/〜NSZ/C/C11/n1570.html#6.7.9p20)。這是描述它的一種相當複雜的方式,但今天沒有一個編譯器表現出其他行爲。 – StoryTeller

3

這是不確定的行爲因爲strlen(a)可以讀取超出的a規模和導致未定義的行爲(如崩潰)。這意味着會發生任何事情。

strlen僅當字符數組中存在空終止符'\ 0'時才起作用。如果不存在,那麼程序行爲是undefined

C11 - 7.23.6.3的strlen函數:

strlen的函數返回之前 終止空字符的字符數。

+5

我不認爲這是訪問越界。我認爲它將一個非空字符串傳遞給'strlen'。 –

+5

'char a [3] = {'H','E','L','L','O','\ 0'};'只會初始化'a'中的前三個字符,它不會是一個字符串,導致與'strlen' UB。 –

+0

@AjayBrahmakshatriya非常感謝。我已經更新了我的答案。 – rsp

2

下面是這個程序的部分程序集列表,該程序使用gcc版本4.8.5編譯而沒有任何優化。

 .cfi_startproc 
     pushq %rbp 
     .cfi_def_cfa_offset 16 
     .cfi_offset 6, -16 
     movq %rsp, %rbp 
     .cfi_def_cfa_register 6 
     subq $16, %rsp 
     movb $72, -16(%rbp) 
     movb $69, -15(%rbp) 
     movb $76, -14(%rbp) 
     leaq -16(%rbp), %rax 
     movq %rax, %rdi 
     call strlen 

你可以看到陣列a是在棧上分配在離堆的基本偏移的0x16。只有字母HEL放在堆棧上。當strlen被調用時,它會查看從rbp-16開始的內存,直到遇到第一個0字節。堆棧初始化的方式是,在位置rbp-10前面遇到0字節,所以你的程序打印

Length is 6