48

以下は、C Programming JusttheFAQsという本の抜粋です。配列は値で渡すことができないので、これは間違っていませんか?

VIII.6:配列を値で関数に渡すにはどうすればよいですか?

回答:[呼び出された関数で、末尾に角かっこ(および])を付けた配列名を宣言することにより、配列を値で関数に渡すことができます。関数を呼び出すときは、配列のアドレス(つまり、配列の名前)を呼び出された関数に渡すだけです。たとえば、次のプログラムは、 値x[]で指定された関数に配列を 渡します。byval_func()

このパラメーターは、 関数が1つの引数(整数の配列)を取ることをint[]コンパイラーに指示します。byval_func()関数が呼び出されると、 byval_func()配列のアドレスを次のアドレスに渡します byval_func()

byval_func(x);

配列は値で渡されるため、配列の正確なコピーが作成され、スタックに配置されます。呼び出された関数は、配列のこのコピーを受け取り、それを印刷できます。渡される配列は元の配列のコピーであるため 、関数byval_func()内で配列を変更しても元の配列には影響しません。byval_func()

4

6 に答える 6

73

配列は値で渡されるため、配列の正確なコピーが作成され、スタックに配置されます。

これは正しくありません。配列自体はコピーされておらず、そのアドレスへのポインタのコピーのみが呼び出し先に渡されます(スタックに配置されます)。(パラメーターをとして宣言するか、として宣言するかに関係なく、パラメーターint[]int*ポインターに減衰します。)これにより、呼び出された関数内から配列の内容を変更できます。したがって、これは

渡される配列は元の配列のコピーであるため、関数byval_func()内で配列を変更しても元の配列には影響しません。byval_func()

明らかに間違っています(以下のコメントについては@Jonathan Lefflerにkudos)。ただし、関数内でポインターを再割り当てしても、関数外の元の配列へのポインターは変更されません。

于 2011-01-23T15:03:17.787 に答える
60

その本を燃やしなさい。初心者プログラマーによって書かれていない実際のCFAQが必要な場合は、次のFAQを使用してください:http://c-faq.com/aryptr/index.html

構文的には、厳密に言えば、Cの値で配列を渡すことはできません。

void func (int* x); /* this is a pointer */

void func (int x[]); /* this is a pointer */

void func (int x[10]); /* this is a pointer */

ただし、レコードの場合、Cには、Cの値で配列を渡すことができる、汚いトリックがあります。自宅でこれを試さないでください。このトリックにもかかわらず、配列を値で渡す理由はまだありません。

typedef struct
{
  int my_array[10];
} Array_by_val;

void func (Array_by_val x);
于 2011-01-23T20:07:16.010 に答える
2

配列は値で渡されないので、これは間違っていませんか?

その通り。Cでは値で配列を渡すことはできません。

私は本の引用された部分を調べました、そしてこの混乱または間違いの原因はかなり速く見つけられます。

作者は、それが関数のパラメーターとして提供された場合*iと同等であることを知りませんでした。i[]後者の形式は、コードのリーダーを明示的に示すために考案されiました。配列は、この質問でよく示されているように、混乱の大きな原因です。

私が面白いと思うのは、本の特定の部分の著者または他の部分の少なくとも1つ(本には合計5人の著者がいるため)、または7人の校正者の1人が少なくとも文に言及していなかったことです。

"関数が呼び出されると、配列のアドレスを次のように渡します:byval_func()byval_func() "

少なくともそれで、彼らは対立があることに気づいたはずです。住所を渡すので、それはただの住所です。アドレスをまったく新しい配列に変える魔法のようなことは何も起こりません。


しかし、質問自体に戻りましょう。

すでに自分自身を知っているように見えるので、Cの値で配列をそのまま渡すことはできません。しかし、あなたは3つのことをすることができます(もっとあるかもしれませんが、それは私の実際のステータスです)、それはユニークなケースに応じて代替となるかもしれません、それで始めましょう。

  1. 配列を構造体にカプセル化します(他の回答で言及されているように):
#include <stdio.h>

struct a_s {
   int a[20];
};

void foo (struct a_s a)
{
   size_t length = sizeof a.a / sizeof *a.a;

   for(size_t i = 0; i < length; i++)
   {
       printf("%d\n",a.a[i]);
   }
}

int main()
{
   struct a_s array;

   size_t length = sizeof array.a / sizeof *array.a;

   for(size_t i = 0; i < length; i++)
   {
       array.a[i] = 15;
   } 

   foo(array);
}
  1. ポインタを渡すだけでなく、配列のサイズを決定するためのパラメータを追加します。呼び出された関数には、そのサイズ情報を使用して新しい配列が作成され、呼び出し元の配列の値が割り当てられます。
#include <stdio.h>

void foo (int *array, size_t length)
{
   int b[length];

   for(size_t i = 0; i < length; i++)
   {
       b[i] = array[i];
       printf("%d\n",b[i]);
   }
}

int main()
{
   int a[10] = {0,1,2,3,4,5,6,7,8,9};

   foo(a,(sizeof a / sizeof *a));
}
  1. ローカル配列を定義することを避け、グローバルスコープで1つの配列を使用するだけです。
#include <stdio.h>

int a[10];
size_t length = sizeof a / sizeof *a;

void foo (void)
{
   for(size_t i = 0; i < length; i++)
   {
       printf("%d\n",a[i]);
   }
}

int main()
{   
   for(size_t i = 0; i < length; i++)
   {
       a[i] = 25;
   } 

   foo();
}
于 2020-03-09T13:33:44.993 に答える
1

CおよびC++では、メモリの完全なブロックをパラメータとして関数に値で渡すことはできませんが、そのアドレスを渡すことはできます。実際には、これはほぼ同じ効果があり、はるかに高速で効率的な操作です。

安全のために、配列サイズを渡すか、ポインターの前にconst qualifierを置いて、呼び出し先がそれを変更しないようにすることができます。

于 2012-06-13T15:11:29.507 に答える
0

Yuoは、配列を構造体にラップすることで回避できます

#include <stdint.h>
#include <stdio.h>

struct wrap
{
    int x[1000];
};

struct wrap foo(struct wrap x)
{
    struct wrap y;

    for(int index = 0; index < 1000; index ++)
        y.x[index] = x.x[index] * x.x[index];
    return y;
}

int main ()
{
    struct wrap y;

    for(int index = 0; index < 1000; index ++)
        y.x[index] = rand();
    y = foo(y);
    for(int index = 0; index < 1000; index ++)
    {
        printf("%d %s", y.x[index], !(index % 30) ? "\n" : "");
    }


}
于 2020-03-09T12:36:32.363 に答える
-15
#include<stdio.h>
void  fun(int a[],int n);
int main()
{
    int a[5]={1,2,3,4,5};
    fun(a,5);
}
void  fun(int a[],int n)
{
    int i;
    for(i=0;i<=n-1;i++)
        printf("value=%d\n",a[i]);
}

この方法では、配列を値で渡すことができますが、実際には、配列は、実際にスタックにコピーされているベースアドレスを介してアクセスしています。

于 2013-06-10T06:04:11.963 に答える