73

Cで参照によって構造体の配列を渡すにはどうすればよいですか?

例として:

struct Coordinate {
   int X;
   int Y;
};
SomeMethod(Coordinate *Coordinates[]){
   //Do Something with the array
}
int main(){ 
   Coordinate Coordinates[10];
   SomeMethod(&Coordinates);
}
4

7 に答える 7

148

Cでは、配列は最初の要素へのポインタとして渡されます。これらは、実際には値によって渡されない唯一の要素です(ポインターは値によって渡されますが、配列はコピーされません)。これにより、呼び出された関数がコンテンツを変更できるようになります。

void reset( int *array, int size) {
   memset(array,0,size * sizeof(*array));
}
int main()
{
   int array[10];
   reset( array, 10 ); // sets all elements to 0
}

さて、必要なのが配列自体(要素の数...)を変更することである場合、スタックまたはグローバル配列では変更できず、ヒープ内に動的に割り当てられたメモリでのみ変更できます。その場合、ポインタを変更したい場合は、ポインタを渡す必要があります。

void resize( int **p, int size ) {
   free( *p );
   *p = malloc( size * sizeof(int) );
}
int main() {
   int *p = malloc( 10 * sizeof(int) );
   resize( &p, 20 );
}

質問の編集では、構造体の配列を渡すことについて具体的に質問します。そこには2つの解決策があります。typedefを宣言するか、構造体を渡すことを明示します。

struct Coordinate {
   int x;
   int y;
};
void f( struct Coordinate coordinates[], int size );
typedef struct Coordinate Coordinate;  // generate a type alias 'Coordinate' that is equivalent to struct Coordinate
void g( Coordinate coordinates[], int size ); // uses typedef'ed Coordinate

宣言するときに型をtypedefすることができます(これはCの一般的なイディオムです)。

typedef struct Coordinate {
   int x;
   int y;
} Coordinate;
于 2009-07-09T23:35:22.427 に答える
12

ここでいくつかの答えを少し拡張するには...

C では、配列識別子が & または sizeof のオペランドとして以外のコンテキストで出現する場合、識別子の型は "T の N 要素配列" から "T へのポインター" に暗黙的に変換され、その値は次のようになります。配列内の最初の要素のアドレスに暗黙的に設定されます (これは、配列自体のアドレスと同じです)。そのため、配列識別子を引数として関数に渡すと、関数は配列ではなく基本型へのポインターを受け取ります。最初の要素へのポインターを見るだけでは配列の大きさがわからないため、サイズを別のパラメーターとして渡す必要があります。

struct Coordinate { int x; int y; };
void SomeMethod(struct Coordinate *coordinates, size_t numCoordinates)
{
    ...
    coordinates[i].x = ...;
    coordinates[i].y = ...; 
    ...
}
int main (void)
{
    struct Coordinate coordinates[10];
    ...
    SomeMethod (coordinates, sizeof coordinates / sizeof *coordinates);
    ...
}

配列を関数に渡す別の方法がいくつかあります。

T へのポインタではなく、T の配列へのポインタのようなものがあります。このようなポインタは次のように宣言します。

T (*p)[N];

この場合、p は T の N 要素配列へのポインターです (T *p[N] とは対照的に、p は T へのポインターの N 要素配列です)。したがって、最初の要素へのポインターではなく、配列へのポインターを渡すことができます。

struct Coordinate { int x; int y };

void SomeMethod(struct Coordinate (*coordinates)[10])
{
    ...
    (*coordinates)[i].x = ...;
    (*coordinates)[i].y = ...;
    ...
}

int main(void)
{
    struct Coordinate coordinates[10];
    ...
    SomeMethod(&coordinates);
    ...
}

この方法の欠点は、T の 10 要素配列へのポインターが T の 20 要素配列へのポインターとは異なる型であるため、配列サイズが固定されることです。

3 番目の方法は、配列を構造体にラップすることです。

struct Coordinate { int x; int y; };
struct CoordinateWrapper { struct Coordinate coordinates[10]; };
void SomeMethod(struct CoordinateWrapper wrapper)
{
    ...
    wrapper.coordinates[i].x = ...;
    wrapper.coordinates[i].y = ...;
    ...
}
int main(void)
{
    struct CoordinateWrapper wrapper;
    ...
    SomeMethod(wrapper);
    ...
}

この方法の利点は、ポインターをいじらないことです。欠点は、配列のサイズが固定されていることです (ここでも、T の 10 要素の配列は、T の 20 要素の配列とは異なる型です)。

于 2009-07-10T15:23:32.180 に答える
9

C言語は、どのタイプの参照によるパスもサポートしていません。最も近いのは、型へのポインタを渡すことです。

これは両方の言語で考案された例です

C++スタイルのAPI

void UpdateValue(int& i) {
  i = 42;
}

最も近いC相当

void UpdateValue(int *i) {
  *i = 42;
}
于 2009-07-09T23:29:32.733 に答える
8

また、メソッド内で配列を作成している場合は、それを返すことができないことに注意してください。それへのポインタを返すと、関数が戻るときにスタックから削除されます。ヒープにメモリを割り当て、それへのポインタを返す必要があります。例えば。

//this is bad
char* getname()
{
  char name[100];
  return name;
}

//this is better
char* getname()
{
  char *name = malloc(100);
  return name;
  //remember to free(name)
}
于 2009-07-09T23:42:11.617 に答える
7

配列は、デフォルトで実質的に参照によって渡されます。実際には、最初の要素へのポインターの値が渡されます。したがって、これを受け取る関数またはメソッドは、配列内の値を変更できます。

void SomeMethod(Coordinate Coordinates[]){Coordinates[0].x++;};
int main(){
  Coordinate tenCoordinates[10];
  tenCoordinates[0].x=0;
  SomeMethod(tenCoordinates[]);
  SomeMethod(&tenCoordinates[0]);
  if(0==tenCoordinates[0].x - 2;){
    exit(0);
  }
  exit(-1);
}

2 つの呼び出しは同等であり、終了値は 0 である必要があります。

于 2009-07-09T23:51:01.113 に答える
6

プレーンCでは、APIでポインターとサイズの組み合わせを使用できます。

void doSomething(MyStruct* mystruct, size_t numElements)
{
    for (size_t i = 0; i < numElements; ++i)
    {
        MyStruct current = mystruct[i];
        handleElement(current);
    }
}

ポインターの使用は、Cで使用可能な参照による呼び出しに最も近いものです。

于 2009-07-09T23:36:24.060 に答える
2

ここにあるのは、new または malloc を使用して配列を割り当てて渡す方法を示す簡単なテスト プログラムです。切り取って貼り付けて実行するだけです。楽しむ!

struct Coordinate
{
    int x,y;
};

void resize( int **p, int size )
{
   free( *p );
   *p = (int*) malloc( size * sizeof(int) );
}

void resizeCoord( struct Coordinate **p, int size )
{
   free( *p );
   *p = (Coordinate*) malloc( size * sizeof(Coordinate) );
}

void resizeCoordWithNew( struct Coordinate **p, int size )
{
   delete [] *p;
   *p = (struct Coordinate*) new struct Coordinate[size];
}

void SomeMethod(Coordinate Coordinates[])
{
    Coordinates[0].x++;
    Coordinates[0].y = 6;
}

void SomeOtherMethod(Coordinate Coordinates[], int size)
{
    for (int i=0; i<size; i++)
    {
        Coordinates[i].x = i;
        Coordinates[i].y = i*2;
    }
}

int main()
{
    //static array
    Coordinate tenCoordinates[10];
    tenCoordinates[0].x=0;
    SomeMethod(tenCoordinates);
    SomeMethod(&(tenCoordinates[0]));
    if(tenCoordinates[0].x - 2  == 0)
    {
        printf("test1 coord change successful\n");
    }
    else
    {
        printf("test1 coord change unsuccessful\n");
    }


   //dynamic int
   int *p = (int*) malloc( 10 * sizeof(int) );
   resize( &p, 20 );

   //dynamic struct with malloc
   int myresize = 20;
   int initSize = 10;
   struct Coordinate *pcoord = (struct Coordinate*) malloc (initSize * sizeof(struct Coordinate));
   resizeCoord(&pcoord, myresize); 
   SomeOtherMethod(pcoord, myresize);
   bool pass = true;
   for (int i=0; i<myresize; i++)
   {
       if (! ((pcoord[i].x == i) && (pcoord[i].y == i*2)))
       {        
           printf("Error dynamic Coord struct [%d] failed with (%d,%d)\n",i,pcoord[i].x,pcoord[i].y);
           pass = false;
       }
   }
   if (pass)
   {
       printf("test2 coords for dynamic struct allocated with malloc worked correctly\n");
   }


   //dynamic struct with new
   myresize = 20;
   initSize = 10;
   struct Coordinate *pcoord2 = (struct Coordinate*) new struct Coordinate[initSize];
   resizeCoordWithNew(&pcoord2, myresize); 
   SomeOtherMethod(pcoord2, myresize);
   pass = true;
   for (int i=0; i<myresize; i++)
   {
       if (! ((pcoord2[i].x == i) && (pcoord2[i].y == i*2)))
       {        
           printf("Error dynamic Coord struct [%d] failed with (%d,%d)\n",i,pcoord2[i].x,pcoord2[i].y);
           pass = false;
       }
   }
   if (pass)
   {
       printf("test3 coords for dynamic struct with new worked correctly\n");
   }


   return 0;
}
于 2011-12-08T04:21:11.033 に答える