簡體   English   中英

C語言中的數組長度

[英]Array length in C language

我以前是 C# 程序員,我對 C 語言有一些不明白的地方(具體來說,我正在使用 C99 標准進行編碼)

我被告知沒有辦法知道 C 中數組的長度,我需要將它的長度作為參數發送給我願意使用的函數,但這是為什么呢? 例如在 C# 中,我們可以輸入array_name.lenght

加上在二維數組中為什么我必須指定數組的列數? 我的意思是為什么這項工作:

void test1 (int arr[][m])
{
}

雖然這沒有:

void test2 (int arr[][])
{
}

例如在 C# 中,我們可以輸入array_name.length

我不使用 C#,但是,如果在子例程中您可以獲得在其他地方創建的數組的長度,那么有關該長度的信息必須存儲在內存中並與數組一起傳遞。 有些東西必須把那個長度放在內存中,當數組作為參數傳遞時,有些東西必須包含比數組長度更多的信息。 所以 C# 正在使用內存和計算時間。

這樣做的結果是您無法直接控制計算機。 只要有東西傳遞額外的信息,你就無法編寫更簡單、更高效的程序。 必然是浪費。 只要您是在有大量資源可用的情況下編寫程序,就可以了。

C 沒有做這個額外的努力。 傳遞數組時,僅傳遞其位置,這就是訪問其元素所需的全部內容。 如果某個特定的子例程需要它的長度,您可以手動傳遞它——您可以選擇在需要時這樣做,但您也可以選擇在不需要時不浪費資源。 您可以編寫更高效的程序。

在二維數組中為什么我必須指定數組的列數?

如果我們知道arr是一個int數組,我們就知道元素arr[0]在開頭, arr[1] arr[2]其后, arr[2]在那之后,依此類推。 要使用一維數組,我們唯一需要知道的是它從哪里開始。

如果我們知道arrayint的二維數組,我們知道a[0][0]在開始, arr[0][1]在之后,依此類推,但我們不知道arr[1][0]是。 它在一定數量的元素arr[0][i] ,但除非我們知道第二維,否則我們不知道有多少。 因此,為了使用二維數組,必須知道第二維的長度。 這是一個邏輯要求,而不是一個選擇。

補充

通常,例程只需要知道它應該使用數組的哪些元素。 它不需要知道數組中有多少個元素。

例程不需要指定數組長度的情況包括:

  • 要計算緩沖區中字符串的長度,例程(如strlen )只需要檢查緩沖區中的每個字節,直到找到空字節。 它不需要知道整個緩沖區有多大。 (例如:一個程序創建了一個 100 字節的緩沖區。它從終端讀取字節,直到找到換行符。用戶只鍵入 12 個字符,然后是一個換行符。緩沖區填充了 12 個字節和一個空字符. 檢查字符串的子程序只需要處理 13 個字節,而不是 100 個。)
  • 例程可能會處理固定數量的元素。 例如,一個幫助數值積分的子程序可能一次取三個函數值,將它們擬合成一條曲線,然后返回曲線下的面積。 主例程可能有一個完整的函數值數組,它反復調用子例程來評估數組中的不同點,向子例程傳遞一個指向要處理的位置的指針。 在每次調用中,子例程只需要知道在給定地址有三個值。 它不需要知道完整數組中有多少。
  • 一個例程可能會處理多個數組中相同數量的元素。 例如,執行離散傅立葉變換的例程可能需要處理多個元素N和四個數組:一個用於實部的輸入,一個用於虛部的輸入,一個用於實部的輸出,以及一種用於虛部的輸出。 對於每個數組,例程使用N 個元素。 這個數字N只需要在一個參數中傳遞給例程。 將它存儲在多個位置會很浪費,每個數組一個。

另一個考慮是有時我們只將數組的一部分傳遞給例程。 如果緩沖區中有一些字符串,我可能希望子例程僅處理該字符串的一部分,也許只是已解析命令中的一個單詞。 為此,我可以只傳遞一個指向該單詞開頭的指針和要處理的單詞長度。 在這種情況下,子程序不僅不需要知道數組的長度,甚至不需要知道數組從哪里開始。 它只需要知道它被要求做什么。 傳遞任何其他信息都是浪費。

在大多數編程語言中,數據類型是抽象的:也就是說,如果您要求一個數字列表,它將在內存中創建用於存儲數字列表的結構,並跟蹤其容量,有多少元素已滿,以及也許元素是“空”還是包含值等。

C 是一種低級語言,不涉及抽象; 它直接處理物理內存。 如果您要求空間放置 5 個整數,它會為 5 個整數分配內存。 您希望它在某處跟蹤數字“5”以記住您分配了 5 個整數? 你沒有要求那個——你必須自己做。

在 C 中,作為參數傳遞給函數的數組被轉換為指向數組第一個元素的指針。 數組的大小不會隱式傳遞給函數。 您,程序員,負責將正確的數組大小傳遞給您的函數。

int sum(int *num, size_t length)
{
   int total = 0;
   int i;
   for (i = 0; i < length; i++)
   {
      total += num[i];
   }
}

這種方法的問題之一是數組的參數僅假定指向數組。 它可以指向任何 int,無論該 int 是否是數組的元素。 如果發生此錯誤,則會發生典型的緩沖區溢出。

C 是一種過程語言(並且比大多數過程語言更接近匯編程序),而不是面向對象的語言。 IOW、Algol(和 C)在 Smalltalk(和 C#)之前出現,Smalltalk 教會了我們一些重要的教訓。

有時您可以在 C 中使用以下內容:

#define num_elements(array) (sizeof(array) / sizeof(array[0]))

...但是當一個數組被傳遞給一個函數時,這通常不再起作用。

另一個幾乎適用於 C 中任何情況的好方法是:

#define MY_ARRAY_ELEMENTS 1000
int a[MY_ARRAY_ELEMENTS];
foo(a, MY_ARRAY_ELEMENTS);

IOW,為特定數組的長度定義一個符號常量,並使用它而不是硬編碼常量。

OO 語言無論如何都有與對象關聯的元數據,那么為什么不在元數據中存儲長度呢? C 並沒有做那種事情——它是在字節非常寶貴的時候創建的,元數據被認為是太多的開銷。

為什么必須部分定義 n 維數組的大小? 因為在幕后 C 正在做一些數學運算來乘以內存中 a[x][y] 存在的位置,而且它沒有存儲元數據來幫助您跟蹤這些維度。

考慮另一種過程語言 Pascal 將數組維度作為數組類型的一部分 那是一種相反的極端——大小和形狀在類型系統中被跟蹤,但實際上在實踐中使用起來非常嚴格。 因此,編寫一個函數來對兩個不同長度的兩個不同數組中的浮點數求和是不切實際的。

暫無
暫無

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

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