簡體   English   中英

將數組作為參數傳遞給 C 中的 function

[英]Passing an array as an argument to a function in C

我寫了一個包含數組作為參數的 function,並通過傳遞數組的值來調用它,如下所示。

void arraytest(int a[])
{
    // changed the array a
    a[0]=a[0]+a[1];
    a[1]=a[0]-a[1];
    a[0]=a[0]-a[1];
}

void main()
{
    int arr[]={1,2};
    printf("%d \t %d",arr[0],arr[1]);
    arraytest(arr);
    printf("\n After calling fun arr contains: %d\t %d",arr[0],arr[1]);
}

我發現雖然我通過傳遞值調用 arraytest arraytest() function,但int arr[]的原始副本已更改。

你能解釋一下為什么嗎?

將數組作為參數傳遞時,此

void arraytest(int a[])

意思完全一樣

void arraytest(int *a)

所以你正在修改main中的值。

由於歷史原因,arrays 不是第一個 class 公民,不能按值傳遞。

For passing 2D (or higher multidimensional) arrays instead, see my other answer here: How to pass a multidimensional array to a function in C and C++

在 C(和 C++)中將一維 arrays 作為 function 參數傳遞

1. C 中的標准數組使用,具有從數組到 ptr 的自然類型衰減(調整)

@Bo Persson 在他的精彩回答中正確指出:

將數組作為參數傳遞時,此

void arraytest(int a[])

意思完全一樣

void arraytest(int *a)

讓我添加一些注釋來增加這兩個代碼片段的清晰度:

// param is array of ints; the arg passed automatically "adjusts" (frequently said
// informally as "decays") from `int []` (array of ints) to `int *` 
// (ptr to int)
void arraytest(int a[])

// ptr to int
void arraytest(int *a)

但是,我還要補充一點,上面的兩個 forms 也:

  1. 意思完全一樣

     // array of 0 ints; automatically adjusts (decays) from `int [0]` // (array of zero ints) to `int *` (ptr to int) void arraytest(int a[0])
  2. 這意味着與

     // array of 1 int; automatically adjusts (decays) from `int [1]` // (array of 1 int) to `int *` (ptr to int) void arraytest(int a[1])
  3. 這意味着與

     // array of 2 ints; automatically adjusts (decays) from `int [2]` // (array of 2 ints) to `int *` (ptr to int) void arraytest(int a[2])
  4. 這意味着與

     // array of 1000 ints; automatically adjusts (decays) from `int [1000]` // (array of 1000 ints) to `int *` (ptr to int) void arraytest(int a[1000])
  5. 等等

在上面的每一個數組示例中,正如下面代碼中的示例調用所示,輸入參數類型調整(衰減)為int * ,並且可以在沒有警告和錯誤的情況下調用,即使使用 build options -Wall -Wextra -Werror打開(有關這 3 個構建選項的詳細信息,請參閱的倉庫),如下所示:

int array1[2];
int * array2 = array1;

// works fine because `array1` automatically decays from an array type
// to a pointer type: `int *`
arraytest(array1);
// works fine because `array2` is already an `int *` 
arraytest(array2);

事實上,此處數組參數中的“大小”值( [0][1][2][1000]等)顯然只是出於審美/自我記錄目的,並且可以您想要的任何正面 integer (我認為size_t類型)!

然而,在實踐中,您應該使用它來指定您希望 function 接收的數組的最小大小,以便在編寫代碼時可以輕松跟蹤和驗證。 MISRA-C-2012標准(在此處以 15.00 英鎊的價格購買/下載該標准的 236-pg 2012 版 PDF )甚至達到 state(強調添加):

規則 17.5 對應於聲明為數組類型的參數的 function 參數應具有適當數量的元素。

...

如果將參數聲明為具有指定大小的數組,則每個 function 調用中的相應參數應指向至少具有與數組一樣多的元素的 object。

...

對 function 參數使用數組聲明符比使用指針更清楚地指定 function 接口。 明確規定了 function 所期望的最小元素數量,而這對於指針是不可能的。

換句話說,他們建議使用顯式大小格式,即使 C 標准在技術上並未強制執行——它至少有助於向開發人員和其他使用代碼的人闡明 function 期望的大小數組你進去。


2. C 中的 arrays 強制類型安全

(不推薦(更正:有時推薦,特別是對於固定大小的多維 arrays ),但可能。請參閱我最后反對這樣做的簡短論點。另外,對於我的多維數組 [例如:二維數組] 版本其中,請在此處查看我的答案。)

正如@Winger Sendon 在我的答案下方的評論中指出的那樣,我們可以強制 C 根據數組大小將數組類型視為不同!

首先,您必須認識到,在我上面的示例中,使用int array1[2]; 像這樣: arraytest(array1); 導致array1自動衰減為int * 但是,如果您使用array1地址並調用arraytest(&array1) ,您會得到完全不同的行為! 現在,它不會衰減為int * 這是因為如果你獲取一個數組的地址,那么你已經有了一個指針類型,並且指針類型不會適應其他指針類型。 只有數組類型才能適應指針類型。 因此, &array1的類型是int (*)[2] ,這意味着“指向 int 大小為 2 的數組的指針” ,或“指向 int 類型的大小為 2 的數組的指針” ,或者也稱為“指針到 2 個整數的數組” 因此,您可以通過將顯式指針傳遞給 arrays 來強制 C 檢查數組的類型安全性,如下所示:

// `a` is of type `int (*)[2]`, which means "pointer to array of 2 ints"; 
// since it is already a ptr, it can NOT automatically decay further
// to any other type of ptr 
void arraytest(int (*a)[2])
{
    // my function here
}

此語法難以閱讀,但類似於function 指針的語法。 在線工具cdecl告訴我們int (*a)[2]的意思是: “將 a 聲明為指向 int 數組 2 的指針” (指向 2 個int數組的指針)。 不要將此與不帶括號的版本混淆: int * a[2] ,這意味着: “將 a 聲明為指向 int 的指針的數組 2” (AKA:指向int的 2 個指針數組,AKA:2 個int* s 的數組) .

現在,這個 function 要求您使用地址運算符 ( & ) 調用它,像這樣,使用指向正確大小的數組的指針作為輸入參數::

int array1[2];

// ok, since the type of `array1` is `int (*)[2]` (ptr to array of 
// 2 ints)
arraytest(&array1); // you must use the & operator here to prevent
                    // `array1` from otherwise automatically decaying
                    // into `int *`, which is the WRONG input type here!

但是,這將產生警告:

int array1[2];

// WARNING! Wrong type since the type of `array1` decays to `int *`:
//      main.c:32:15: warning: passing argument 1 of ‘arraytest’ from 
//      incompatible pointer type [-Wincompatible-pointer-types]                                                            
//      main.c:22:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
arraytest(array1); // (missing & operator)

您可以在此處測試此代碼

要強制 C 編譯器將此警告變為錯誤,因此您必須始終調用arraytest(&array1); 僅使用正確大小類型的輸入數組( int array1[2];在這種情況下),將-Werror添加到您的構建選項中。 如果在 onlinegdb.com 上運行上面的測試代碼,請單擊右上角的齒輪圖標並單擊“Extra Compiler Flags”以鍵入此選項。現在,此警告:

 main.c:34:15: warning: passing argument 1 of 'arraytest' from incompatible pointer type [-Wincompatible-pointer-types] main.c:24:6: note: expected 'int (*)[2]' but argument is of type 'int *'

將變成這個構建錯誤:

 main.c: In function 'main': main.c:34:15: error: passing argument 1 of 'arraytest' from incompatible pointer type [-Werror=incompatible-pointer-types] arraytest(array1); // warning. ^~~~~~ main:c:24:6: note: expected 'int (*)[2]' but argument is of type 'int *' void arraytest(int (*a)[2]) ^~~~~~~~~ cc1: all warnings being treated as errors

請注意,您還可以創建指向給定大小的 arrays 的“類型安全”指針,如下所示:

int array[2]; // variable `array` is of type `int [2]`, or "array of 2 ints"

// `array_p` is a "type safe" ptr to array of size 2 of int; ie: its type
// is `int (*)[2]`, which can also be stated: "ptr to array of 2 ints"
int (*array_p)[2] = &array;

...但我不一定推薦這個(在 C 中使用這些“類型安全”arrays),因為它讓我想起了很多用於在任何地方強制類型安全的 ZF6F87C9FDCF8B3C3F07F07F93F1EE8712C9Z 滑稽動作,其代價是語言語法復雜性和冗長性異常高,以及難以構建代碼,並且我不喜歡並且之前曾多次抱怨過(例如:請參閱“我對 C++ 的思考” )。


有關其他測試和實驗,另請參閱下面的鏈接。

參考

請參閱上面的鏈接。 還:

  1. 我的代碼在線實驗: https://onlinegdb.com/B1RsrBDFD

也可以看看:

  1. My answer on multi-dimensional arrays (ex: 2D arrays) which expounds upon the above, and uses the "type safety" approach for multi-dimensional arrays where it makes sense: How to pass a multidimensional array to a function in C and C++

如果要在 function 中將單維數組作為參數傳遞,則必須以以下三種方式之一聲明形式參數,並且所有三種聲明方法都會產生相似的結果,因為每種方法都告訴編譯器 integer 指針正在運行被接收

int func(int arr[], ...){
    .
    .
    .
}

int func(int arr[SIZE], ...){
    .
    .
    .
}

int func(int* arr, ...){
    .
    .
    .
}

因此,您正在修改原始值。

謝謝 !!!

您沒有將數組作為副本傳遞。 它只是一個指向數組第一個元素在memory中的地址的指針。

在大多數情況下,C 中的 Arrays 被轉換為指向數組本身第一個元素的指針。 更詳細地說,傳遞給函數的 arrays 總是轉換為指針。

這是來自K&R2nd的引述:

當數組名被傳遞給 function 時,傳遞的是初始元素的位置。 在被調用的function中,這個參數是一個局部變量,所以數組名參數是一個指針,即一個包含地址的變量。

寫作:

void arraytest(int a[])

與寫作具有相同的含義:

void arraytest(int *a)

因此,盡管您沒有明確地編寫它,但它就像您傳遞一個指針一樣,因此您正在修改 main 中的值。

更多我真的建議閱讀這個

此外,您可以在此處找到有關 SO 的其他答案

將多維數組作為參數傳遞給 function。 傳遞一個暗淡的數組作為參數或多或少是微不足道的。 讓我們看一個更有趣的傳遞 2 dim 數組的案例。 在 C 中,您不能使用指向指針構造 ( int ** ) 的指針來代替 2 個暗淡數組。 讓我們舉個例子:

void assignZeros(int(*arr)[5], const int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 5; j++) {
            *(*(arr + i) + j) = 0;
            // or equivalent assignment
            arr[i][j] = 0;
        }
    }

在這里,我指定了一個 function,它將指向 5 個整數數組的指針作為第一個參數。 我可以將任何具有 5 列的 2 個暗淡數組作為參數傳遞:

int arr1[1][5]
int arr1[2][5]
...
int arr1[20][5]
...

您可能會想到定義一個更通用的 function 可以接受任何 2 個昏暗數組並更改 function 簽名如下:

void assignZeros(int ** arr, const int rows, const int cols) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            *(*(arr + i) + j) = 0;
        }
    }
}

此代碼可以編譯,但在嘗試以與第一個 function 相同的方式分配值時會出現運行時錯誤。 因此,在 C 中,多維 arrays 與指向指針的指針......指向指針不同。 int(*arr)[5]是指向 5 個元素的數組的指針, int(*arr)[6]是指向 6 個元素的數組的指針,它們是指向不同類型的指針!

那么,如何為更高維度定義函數 arguments 呢? 很簡單,我們只需按照模式:這里是相同的 function 調整為采用 3 維數組:

void assignZeros2(int(*arr)[4][5], const int dim1, const int dim2, const int dim3) {
    for (int i = 0; i < dim1; i++) {
        for (int j = 0; j < dim2; j++) {
            for (int k = 0; k < dim3; k++) {
                *(*(*(arr + i) + j) + k) = 0;
                // or equivalent assignment
                arr[i][j][k] = 0;
            }
        }
    }
}

如您所料,它可以將任何 3 個暗色 arrays 作為參數,該參數在第二維 4 個元素和第三維 5 個元素中。 像這樣的任何事情都可以:

arr[1][4][5]
arr[2][4][5]
...
arr[10][4][5]
...

但是我們必須指定直到第一個的所有尺寸。

您正在傳遞數組第一個成員的 memory 位置的值。

因此,當您開始修改 function 中的數組時,您正在修改原始數組。

請記住a[1]*(a+1)

您正在傳遞數組第一個元素的地址

在 C 中,除了少數特殊情況,數組引用總是“衰減”到指向數組第一個元素的指針。 因此,不可能“按值”傳遞數組。 function 調用中的數組將作為指針傳遞給 function,這類似於通過引用傳遞數組。

編輯:在三種特殊情況下,數組不會衰減到指向其第一個元素的指針:

  1. sizeof asizeof (&a[0])不同。
  2. &a&(&a[0])不同(與&a[0] a[0] 不完全相同)。
  3. char b[] = "foo"char b[] = &("foo")不同。

如果使用a[]*a ,Arrays 總是通過引用傳遞:

int* printSquares(int a[], int size, int e[]) {   
    for(int i = 0; i < size; i++) {
        e[i] = i * i;
    }
    return e;
}

int* printSquares(int *a, int size, int e[]) {
    for(int i = 0; i < size; i++) {
        e[i] = i * i;
    }
    return e;
}

數組也可以稱為衰減指針。

通常,當我們在 printf 語句中放置變量名時,如果數組衰減到第一個元素的地址,則會打印該值,因此將其稱為衰減指針。

我們只能將衰減指針傳遞給 function。

數組作為形參就像博先生所說的 int arr[] 或 int arr[10] 等價於 int *arr;

他們將有自己的 4 字節 memory 空間並存儲接收到的衰減指針。我們對它們進行指針運算。

暫無
暫無

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

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