[英]How does this piece of code determine array size without using sizeof( )?
通過一些C面試問題,我發現了一個問題,說明“如何在不使用sizeof運算符的情況下在C中查找數組的大小?”,並提供以下解決方案。 它有效,但我無法理解為什么。
#include <stdio.h>
int main() {
int a[] = {100, 200, 300, 400, 500};
int size = 0;
size = *(&a + 1) - a;
printf("%d\n", size);
return 0;
}
正如預期的那樣,它返回5。
編輯:人們指出了這個答案,但語法確實有所不同,即索引方法
size = (&arr)[1] - arr;
所以我相信這兩個問題都是有效的,並且對問題的解決方法略有不同。 謝謝大家的大力幫助和徹底的解釋!
向指針添加1時,結果是指向類型的對象序列(即數組)中下一個對象的位置。 如果p
指向int
對象,則p + 1
將指向序列中的下一個int
。 如果p
指向int
的5元素數組(在本例中為表達式&a
),那么p + 1
將指向序列中int
的下一個5元素數組 。
減去兩個指針(前提是它們都指向同一個數組對象,或者一個指向一個超過數組的最后一個元素)會產生這兩個指針之間的對象數(數組元素)。
表達&a
產生的地址a
,並且具有類型int (*)[5]
指針至5個元素的數組的int
)。 表達&a + 1
產生的下一5個元素的數組的地址int
以下a
,並且還具有的類型int (*)[5]
表達式*(&a + 1)
解引用的結果&a + 1
,使得其產生所述第一地址int
的最后一個元素以下a
,並且具有類型int [5]
在此上下文中,“衰減”到int *
表達式。
類似地,表達a
“衰減”的指針數組的第一元素和具有類型int *
。
圖片可能會有所幫助:
int [5] int (*)[5] int int *
+---+ +---+
| | <- &a | | <- a
| - | +---+
| | | | <- a + 1
| - | +---+
| | | |
| - | +---+
| | | |
| - | +---+
| | | |
+---+ +---+
| | <- &a + 1 | | <- *(&a + 1)
| - | +---+
| | | |
| - | +---+
| | | |
| - | +---+
| | | |
| - | +---+
| | | |
+---+ +---+
這是同一存儲的兩個視圖 - 在左側,我們將其視為一個由5個元素組成的int
序列,而在右側,我們將其視為一個int
序列。 我還展示了各種表達方式及其類型。
請注意,表達式*(&a + 1)
導致未定義的行為 :
...
如果結果指向數組對象的最后一個元素之后,則不應將其用作已計算的一元*運算符的操作數。
C 2011在線草案 ,6.5.6 / 9
這條線最重要:
size = *(&a + 1) - a;
正如你所看到的,它首先采取的地址a
,並增加了一個它。 然后,取消引用該指針和減去的原始值a
從它。
C中的指針運算會導致返回數組中元素的數量,或者5
。 加入一種和&a
是指向的5下一個陣列int
年代后a
。 之后,此代碼取消引用結果指針並從中減去a
(已衰減為指針的數組類型),從而給出數組中的元素數。
指針算術如何工作的詳細信息:
假設你有一個指向xyz
的指針,它指向一個int
類型並包含值(int *)160
。 當你從xyz
減去任何數字時,C指定從xyz
減去的實際數量是該數字乘以它指向的類型的數量。 例如,如果從xyz
減去5
,則如果指針算術不適用,則xyz
結果的值將為xyz - (sizeof(*xyz) * 5)
。
作為a
是5
int
類型的數組,結果值將為5.但是,這不適用於指針,僅適用於數組。 如果使用指針嘗試此操作,結果將始終為1
。
這是一個顯示地址以及未定義的示例。 左側顯示地址:
a + 0 | [a[0]] | &a points to this
a + 1 | [a[1]]
a + 2 | [a[2]]
a + 3 | [a[3]]
a + 4 | [a[4]] | end of array
a + 5 | [a[5]] | &a+1 points to this; accessing past array when dereferenced
這意味着代碼從&a[5]
(或a+5
)中減去a
,給出5
。
請注意,這是未定義的行為,不應在任何情況下使用。 不要指望這種行為在所有平台上都是一致的,並且不要在生產程序中使用它。
嗯,我懷疑這是在C早期就不會有效的事情。但這很聰明。
一次一個步驟:
&a
獲取指向int [5]類型的對象的指針 +1
會得到下一個這樣的對象 *
有效地將該地址轉換為int的類型指針 -a
減去兩個int指針,返回它們之間的int實例計數。 我不確定它是完全合法的(在這里我的意思是語言律師合法 - 不會在實踐中起作用),因為某些類型的操作正在進行中。 例如,當你指向同一個數組中的元素時,你只能“允許”減去兩個指針。 *(&a+1)
通過訪問另一陣列,雖然是一個父陣列合成,所以是不實際的指針到同一陣列a
。 此外,雖然允許您通過數組的最后一個元素合成指針,並且可以將任何對象視為1個元素的數組,但是對於此合成指針,取消引用( *
)的操作不是“允許的”,即使在這種情況下它沒有行為!
我懷疑在C的早期(K&R語法,任何人?),一個數組更快地衰減成指針,所以*(&a+1)
可能只返回int **類型的下一個指針的地址。 現代C ++中更嚴格的定義肯定允許指向數組類型的指針存在並知道數組大小,並且可能C標准也遵循了。 所有C函數代碼僅將指針作為參數,因此技術上可見的差異很小。 但我只是在這里猜測。
這種詳細的合法性問題通常適用於C解釋器或lint類型工具,而不是編譯代碼。 解釋器可能會將2D數組實現為指向數組的指針數組,因為實現的運行時功能較少,在這種情況下解除引用+1將是致命的,即使它工作也會給出錯誤的答案。
另一個可能的弱點可能是C編譯器可能對齊外部數組。 想象一下,如果這是一個包含5個字符的數組( char arr[5]
),當程序執行&a+1
它會調用“數組數組”行為。 編譯器可能會認為5個字符數組的數組( char arr[][5]
)實際上是作為8個字符數組( char arr[][8]
)的數組生成的,因此外部數組很好地對齊。 我們正在討論的代碼現在將數組大小報告為8,而不是5.我不是說特定的編譯器肯定會這樣做,但它可能會。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.