簡體   English   中英

當我無意中使用變量來聲明數組長度時,C ++真的在做什么?

[英]What's C++ Really Doing When I Accidently Use a Variables to Declare Array Length?

我正在幫朋友做一些C ++的家庭作業。 我警告說,我所做的那種編程(PHP,Perl,Python)與C ++有很大的不同,並且我無法保證我不會說出可怕的謊言。

我能夠回答他的問題,但不是沒有絆倒我自己的動態背景。 當我重新認識C ++數組語義時,我做了一些像這樣的蠢事(簡化示例讓我的問題更加清晰)

 #include <iostream>
 #include <cstring>
 using namespace std;
 int main()
 {
   char easy_as_one_two_three[] = {'A','B','C'};  
   int an_int = 1;

   //I want an array that has a length of the value 
   //that's currently in an_int (1)
   //This clearly (to a c++ programmer) doesn't do that.
   //but what is it doing?
   char breaking_things[an_int];

   cout << easy_as_one_two_three << endl;
   return 1;
 }

當我編譯並運行該程序時,它會產生以下輸出

 ABC????

但是,如果我注釋掉我的虛假陣列聲明

 #include <iostream>
 #include <cstring>
 using namespace std;
 int main()
 {
   char easy_as_one_two_three[] = {'A','B','C'};  
   int an_int = 1;

   //I want an array that has a length of the value 
   //that's currently in an_int (1)
   //This clearly (to a c programmer) doesn't do that.
   //but what is it doing?
   //char breaking_things[an_int];

   cout << easy_as_one_two_three << endl;
   return 1;
 }

我得到了我期望的輸出:

 ABC

那么,到底發生了什么? 我(隱約地)理解當你創建一個數組時,你指向一個特定的內存地址,當你給一個數組一個長度時,你告訴計算機“為我保留下一個X塊”。

我不明白的是,當我在數組聲明中使用變量時,我告訴計算機做什么,為什么它對一個完全獨立的數組產生影響?

編譯器是g ++,版本字符串是

 science:c++ alanstorm$ g++ -v
 Using built-in specs.
 Target: i686-apple-darwin9
 Configured with: /var/tmp/gcc/gcc-5493~1/src/configure --disable-checking -enable-werror --prefix=/usr --mandir=/share/man --enable-languages=c,objc,c++,obj-c++ --program-transform-name=/^[cg][^.-]*$/s/$/-4.0/ --with-gxx-include-dir=/include/c++/4.0.0 --with-slibdir=/usr/lib --build=i686-apple-darwin9 --with-arch=apple --with-tune=generic --host=i686-apple-darwin9 --target=i686-apple-darwin9
 Thread model: posix
 gcc version 4.0.1 (Apple Inc. build 5493)

更新:Neil在他的評論中指出,如果用g ++中的-Wall-pedantic標志編譯它,你會得到錯誤。

error: ISO C++ forbids variable-size array

你正在獲得ABC???? 因為它打印數組的內容( ABC )並繼續打印,直到遇到\\0

數組是{'A','B','C', '\\0'}; ,輸出將只是預期的ABC

在C99中引入了可變長度數組 - 但這似乎並不適用於C ++。


這是未定義的行為。 即使您注釋掉虛假聲明,打印輸出也不總是您所期望的(ABC)。 嘗試將某些可打印字符的ASCII值(32到126之間的值)給an_int而不是1,您將看到差異。

an_int            output
------------------------
 40                ABC(
 65                ABCA
 66                ABCB
 67                ABCC
 296               ABC(
 552               ABC(
 1064              ABC(
 1024*1024 + 40    ABC(

看到這里的模式? 顯然它將an_int的最后一個字節(LSB) an_int為char,打印它,然后以某種方式找到一個空字符並停止打印。 我認為“某種程度上”必須對an_int的MSB部分用零填充做一些事情,但我不確定(也不能得到任何結果來支持這個論點)。

更新:它是關於MSB填充零。 我得到了以下結果。

ABC( 40 - (3個零字節和40個),
ABC((對於10280(即(40 << 8)+ 40) - (2個零字節和兩個40),
ABC(((對於2631720(即(10280 << 8)+ 40) - (1個零字節和3個40),
ABC((((°¿®對於673720360(即(2631720 << 8)+ 40) - 沒有零字節,因此打印隨機字符,直到找到零字節。
ABCDCBA0á´¿á´¿® (((((65 << 8)+ 66)<< 8)+ 67)<< 8)+ 68;

這些結果是在具有8位原子元素大小和1字節地址增量的小端處理器上獲得的,其中32位整數40(十六進制為0x28)表示為0x28-0x00-0x00-0x00 (最低地址的LSB) 。 結果可能因編譯器和編譯器以及平台而異。

現在,如果您嘗試取消注釋虛假聲明,您會發現所有輸出都是ABC-randomchars-char_corresponding_to_an_int形式。 這又是未定義行為的結果。

這不會“重新認識”你“使用c ++數組語義”,因為在C ++中它只是非法的。 在C ++中,只能使用由Integral Constant Expressions(ICE)定義的大小來聲明數組。 在您的示例中,大小不是ICE。 它只能編譯,因為GCC特定的擴展。

從C的角度來看,這在C99版本的語言中實際上是完全合法的。 它確實產生了一個長度為1的所謂可變長度數組。所以你的“清晰”注釋是不正確的。

它不是無效的語法。 它在語法上很好。

它在語義上是無效的C ++,並被我的編譯器(VC ++)拒絕。 g ++似乎有一個擴展,允許在C ++中使用C99 VLA。

問號的原因是你的三個字符數組不是空終止; 它打印直到它在堆棧上找到null。 堆棧的布局受堆棧上聲明的變量的影響。 對於數組,布局是這樣的,在第一個null之前有垃圾; 沒有陣列就沒有。 就這些。

你可以得到你期望或者不期望的輸出。 因為你沒有null終止你的數組中的字符,當你去打印它cout它將打印A,B和C,以及它找到的任何其他任何東西,直到它遇到一個NULL字符。 使用數組聲明,可能會有一些編譯器正在推送到堆棧上以使數組在運行時調整大小,在A,B和C之后留下垃圾字符,而當你不在那里時恰好是0在堆棧上的C之后。

再一次,這只是運氣不好。 總是得到你期望的應該做的事: char easy_as_one_two_three[] = { 'A','B','C','\\0'}; 或者,更有用的是char easy_as_one_two_three[] = "ABC"; ,這將正確地null終止字符串。

char breaking_things [an_int]正在分配大小為an_int的char數組(在你的情況下為1),它被稱為可變長度數組 ,它是一個相對較新的特性。

在這種情況下,使用new動態分配內存更為常見:

char* breaking_things = new char[an_int]; // C++ way, C programmer would use malloc

輸出是這樣的,因為它將打印char數組的內容,直到找到空字符。

確保char數組必須是以null結尾的字符串並指定數組的大小 - > total chars + 1(對於null char)。

這可能不是破壞事情的破壞事件。 第一個數組不是NUL(\\ 0)終止的字符串,它解釋了輸出 - cout將打印ABC之后的任何內容,直到它遇到的第一個NUL。

至於break_things的大小,我懷疑它在編譯器之間有所不同。 我相信至少早期版本的gcc使用了變量在編譯時遇到的任何值,這可能很難確定。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM