Cで参照によって構造体の配列を渡すにはどうすればよいですか?
例として:
struct Coordinate {
int X;
int Y;
};
SomeMethod(Coordinate *Coordinates[]){
//Do Something with the array
}
int main(){
Coordinate Coordinates[10];
SomeMethod(&Coordinates);
}
Cで参照によって構造体の配列を渡すにはどうすればよいですか?
例として:
struct Coordinate {
int X;
int Y;
};
SomeMethod(Coordinate *Coordinates[]){
//Do Something with the array
}
int main(){
Coordinate Coordinates[10];
SomeMethod(&Coordinates);
}
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;
ここでいくつかの答えを少し拡張するには...
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 要素の配列とは異なる型です)。
C言語は、どのタイプの参照によるパスもサポートしていません。最も近いのは、型へのポインタを渡すことです。
これは両方の言語で考案された例です
C++スタイルのAPI
void UpdateValue(int& i) {
i = 42;
}
最も近いC相当
void UpdateValue(int *i) {
*i = 42;
}
また、メソッド内で配列を作成している場合は、それを返すことができないことに注意してください。それへのポインタを返すと、関数が戻るときにスタックから削除されます。ヒープにメモリを割り当て、それへのポインタを返す必要があります。例えば。
//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)
}
配列は、デフォルトで実質的に参照によって渡されます。実際には、最初の要素へのポインターの値が渡されます。したがって、これを受け取る関数またはメソッドは、配列内の値を変更できます。
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 である必要があります。
プレーンCでは、APIでポインターとサイズの組み合わせを使用できます。
void doSomething(MyStruct* mystruct, size_t numElements)
{
for (size_t i = 0; i < numElements; ++i)
{
MyStruct current = mystruct[i];
handleElement(current);
}
}
ポインターの使用は、Cで使用可能な参照による呼び出しに最も近いものです。
ここにあるのは、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;
}