Przekazywanie tablicy przez odniesienie w C?

Jak mogę przekazać tablicę struktur przez odniesienie w C?

Jako przykład:

struct Coordinate {
   int X;
   int Y;
};
SomeMethod(Coordinate *Coordinates[]){
   //Do Something with the array
}
int main(){ 
   Coordinate Coordinates[10];
   SomeMethod(&Coordinates);
}
Author: templatetypedef, 2009-07-10

7 answers

W C tablice są przekazywane jako wskaźnik do pierwszego elementu. Są one jedynym elementem, który tak naprawdę nie jest przekazywany przez wartość (wskaźnik jest przekazywany przez wartość, ale tablica nie jest kopiowana). Dzięki temu wywołana funkcja może modyfikować zawartość.

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
}

Teraz, jeśli chcesz zmienić samą tablicę (ilość elementów...) nie można tego zrobić ze stosem lub globalnymi tablicami, tylko z dynamicznie przydzielaną pamięcią w stercie. W takim przypadku, jeśli chcesz zmienić wskaźnik, musisz przejść wskaźnik do niego:

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

W pytaniu edit pytasz konkretnie o przekazanie tablicy struktur. Masz tam dwa rozwiązania: zadeklaruj typedef lub sprecyzuj, że przekazujesz strukturę:

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

Możesz wpisać Typ tak, jak go deklarujesz (i jest to wspólny idiom w C):

typedef struct Coordinate {
   int x;
   int y;
} Coordinate;
 118
Author: David Rodríguez - dribeas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-03 21:18:23

Aby rozwinąć trochę niektóre z odpowiedzi tutaj...

W C, gdy identyfikator tablicy pojawia się w kontekście innym niż operand na & lub sizeof, Typ identyfikatora jest domyślnie konwertowany z "tablicy N-elementów T" na "wskaźnik do T", a jego wartość jest domyślnie ustawiona na adres pierwszego elementu w tablicy (który jest taki sam jak adres samej tablicy). Dlatego, gdy po prostu przekazujesz identyfikator tablicy jako argument do funkcji, funkcja otrzymuje wskaźnik do typu bazowego, a nie tablicę. Ponieważ nie możesz określić wielkości tablicy tylko patrząc na wskaźnik do pierwszego elementu, musisz przekazać rozmiar jako oddzielny parametr.

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);
    ...
}

Istnieje kilka alternatywnych sposobów przekazywania tablic do funkcji.

Istnieje coś takiego jak wskaźnik do tablicy T, w przeciwieństwie do wskaźnika do T. zadeklarowałbyś taki wskaźnik jako

T (*p)[N];

W tym przypadku p jest wskaźnikiem do N-elementowa tablica T (w przeciwieństwie do t *p[n], gdzie p jest N-elementową tablicą wskaźnika do T). Można więc przekazać wskaźnik do tablicy w przeciwieństwie do wskaźnika do pierwszego elementu:

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);
    ...
}

Wadą tej metody jest to, że rozmiar tablicy jest stały, ponieważ wskaźnik do 10-elementowej tablicy T jest innym typem od wskaźnika do 20-elementowej tablicy T.

Trzecią metodą jest zawinięcie tablicy w strukturę:

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);
    ...
}

Zaletą tej metody jest to, że nie jesteś obijanie się wskaźnikami. Wadą jest to, że rozmiar tablicy jest stały (ponownie, 10-elementowa tablica T jest innym typem niż 20-elementowa tablica T).

 11
Author: John Bode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-10 15:23:32

Język C nie obsługuje żadnego typu pass by reference. Najbliższym odpowiednikiem jest przekazanie wskaźnika do typu.

Oto wymyślony przykład w obu językach

C++ style API

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

Najbliższy odpowiednik C

void UpdateValue(int *i) {
  *i = 42;
}
 8
Author: JaredPar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-09 23:29:32

Należy również pamiętać, że jeśli tworzysz tablicę wewnątrz metody, nie możesz jej zwrócić. Jeśli zwrócisz do niego wskaźnik, zostanie on usunięty ze stosu po powrocie funkcji. musisz przydzielić pamięć na stertę i zwrócić do niej wskaźnik. np.

//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)
}
 7
Author: user128026,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-09 23:42:11

W prostym C możesz użyć kombinacji wskaźnik / rozmiar w interfejsie API.

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

Użycie wskaźników jest najbliższe do wywołania po odwołaniu dostępnego w C.

 6
Author: haffax,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-09 23:36:24

Tablice są domyślnie przekazywane przez odniesienie. W rzeczywistości przekazywana jest wartość wskaźnika do pierwszego elementu. W związku z tym funkcja lub metoda, która to otrzyma, może modyfikować wartości w tablicy.

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);
}

Oba wywołania są równoważne, a wartość wyjścia powinna wynosić 0;

 6
Author: dlamblin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-09 23:51:01

Hey guys Oto prosty program testowy, który pokazuje, jak przydzielić i przekazać tablicę za pomocą new lub malloc. Po prostu Wytnij, Wklej i uruchom. Baw się dobrze!

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;
}
 2
Author: Jeff M,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-08 04:21:11