簡體   English   中英

這段代碼如何在不使用sizeof()的情況下確定數組大小?

[英]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)

作為a5 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.

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