web-dev-qa-db-ja.com

Cの参照による配列の受け渡し

私はCが初めてなので、疑問があります。

C関数はその引数のローカルコピーを作成するため、次のコードが期待どおりに機能する理由を知りたいのです。

void function(int array[]){

    array[0] = 4;
    array[1] = 5;
    array[2] = 6;   
}

int main(){

    int array[] = {1,2,3};

    function(array);

    printf("%d %d %d",array[0],array[1],array[2]);

    return 0;
}

ライン出力は4 5 6。

以下は機能しないのに、なぜ機能するのですか?

void function(int integer){

    integer = 2;
}

int main(){

    int integer = 1;

    function(integer);

    printf("%d",integer);

    return 0;
}

この場合、出力は1です。

短いバージョン:配列として渡される場合、関数が親変数の値を変更できるのはなぜですか?

皆さん、ありがとうございました!

18
Costagero

これは、配列がポインターに崩壊する傾向があるという事実によって引き起こされます。

_int a[] = { 1, 2, 3 };
int* p = a; // valid: p is now the address of a[0]
a = p;  // NOT valid.

printf("a = %p\n", a);
printf("p = %p\n", p); // prints same address as a
_

apは同じ値を出力します。

他の人が言ったことに反して、anotポインターであり、単純に1つに減衰します。 http://c-faq.com/aryptr/aryptrequiv.html

最初のfunction()で渡されるのは、配列の最初の要素のアドレスであり、関数本体はそれを逆参照します。実際、コンパイラは関数プロトタイプを次のように扱っています。

_void function(int* array /*you wrote int array[]*/){
    array[0] = 4;
    array[1] = 5;
    array[2] = 6;   
}

function(&array[0]);
_

これは、「サイズが不明な配列」(int array [])と言ったために発生する必要があります。コンパイラーは、値渡しに必要なスタックの量を推定することを保証できなかったため、ポインターに減衰します。

----編集----

両方の例を組み合わせて、より明確な名前を使用して物事を明確にしましょう。

_#include <stdio.h>

void func1(int dynArray[]) {
    printf("func1: dynArray = %p, &dynArray[0] = %p, dynArray[0] = %d\n",
             dynArray, &dynArray[0], dynArray[0]);
}

void func2(int* intPtr) {
    printf("func2: intPtr = %p, &intPtr[0] = %p, intPtr[0] = %d\n",
             intPtr, &intPtr[0], intPtr[0]);
}

void func3(int intVal) {
    printf("func3: intVal = %d, &intValue = %p\n",
             intVal, &intVal);
}

int main() {
    int mainArray[3] = { 1, 2, 3 };
    int mainInt = 10;

    printf("mainArray = %p, &mainArray[0] = %p, mainArray[0] = %d\n",
             mainArray, &mainArray, mainArray[0]);
    func1(mainArray);
    func2(mainArray);

    printf("mainInt = %d, &mainInt = %p\n",
             mainInt, &mainInt);
    func3(mainInt);

    return 0;
}
_

Ideoneでのライブデモ: http://ideone.com/P8C1f4

_mainArray = 0xbf806ad4, &mainArray[0] = 0xbf806ad4, mainArray[0] = 1
func1: dynArray = 0xbf806ad4, &dynArray[0] = 0xbf806ad4, dynArray[0] = 1
func2: intPtr = 0xbf806ad4, &intPtr[0] = 0xbf806ad4, intPtr[0] = 1

mainInt = 10, &mainInt = 0xbf806acc
func3: intVal = 10, &intValue = 0xbf806ad0
_

_func1_および_func2_では、「dynArray」と「intPtr」はローカル変数ですが、mainから「mainArray」のアドレスを受け取るポインター変数です。

この動作は配列に固有です。構造体内に配列を配置する場合、値で渡すことができます。

20
kfsone

関数に渡された配列はポインターに変換されます。関数への引数としてポインタを渡すときは、メモリ内の変数のアドレスを指定するだけです。したがって、配列のセルの値を変更するときは、関数に指定されたアドレスの下の値を編集します。

単純な整数を関数に渡すと、整数がスタックにコピーされます。関数内の整数を変更すると、元の整数ではなく整数のコピーが変更されます。

Cのさまざまな種類の記憶を思い出させる

Cでは、3種類のメモリを使用できます。

  • ローカル変数および関数呼び出しに使用されるスタック:main()で変数を作成するとき、変数を保存するためにスタックを使用し、関数が呼び出されるとき、メソッドに与えられたパラメータースタックに登録されます。関数を終了すると、これらのパラメーターを「ポップ」して、関数の呼び出し前に使用済み変数を使用して元の状態に戻ります。 (anecdote:スタックオーバーフローは、パラメーターを渡さずに関数の前の変数を使用するためにスタックをハックする場合です)
  • 動的に割り当てられたメモリに対応するヒープ:大量のデータが必要な場合、スタックが数メガバイトに制限されているため、このヒープを使用します。
  • プログラムの命令が保存されているコード

ポインター(他の変数へのアドレス)である関数によって渡されるこの配列の場合、関数を呼び出すときにスタックに格納され、ポインターをスタックにコピーします。

整数の場合、関数を呼び出すときにスタックにも格納され、整数をコピーします。

整数を変更する場合は、次のように、整数のアドレスを渡して、ポインターの下の値を変更できます。

void function(int *integer)
{
    *integer = 2;
}

int main()
{
    int integer = 1;
    function(&integer);

    printf("%d", integer);

    return 0;
}
3
StarkOverflow

「参照による受け渡し」と「値による受け渡し」には違いがあります

参照渡しは、値渡しが値を直接渡すメモリ内の場所につながります。配列変数は常に参照であるため、メモリ内の場所を指します。整数はデフォルトで値で渡されます

1
Mazzy

最初のコードでは、配列の一番上の要素を指す配列のアドレスを渡します。そのため、関数の値を変更してメイン関数に戻ると、同じアドレスにある同じ配列にアクセスしています。これは、参照渡しと呼ばれます。

ただし、2番目の場合、整数の値はメイン関数から呼び出された関数にコピーされます。つまり、2つの整数はメモリ内の異なるアドレスにあります。したがって、一方を変更しても他方は変更されません。

1
user2502921

配列名は、配列の最初の要素へのポインターです。最初のコードサンプルでは、​​最初の配列要素を含むメモリの場所にポインターを渡しました。 2番目のコードサンプルでは、​​値で整数を渡しているため、「integer」という名前のローカル変数とは関係ありません。

そのリンクを確認してください

参照渡しと値渡し

参照渡し/ C++の値

0
Rami