Передача массива по ссылке в C?

Как я могу передать массив структур по ссылке в C?

Как пример:

struct Coordinate {
   int X;
   int Y;
};
SomeMethod(Coordinate *Coordinates[]){
   //Do Something with the array
}
int main(){ 
   Coordinate Coordinates[10];
   SomeMethod(&Coordinates);
}
60 голосов | спросил Hannoun Yassir 10 J000000Friday09 2009, 03:27:50

7 ответов


0

В Си массивы передаются как указатель на первый элемент. Они являются единственным элементом, который на самом деле не передается по значению (указатель передается по значению, но массив не копируется). Это позволяет вызываемой функции изменять содержимое.

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

В редактировании вопроса вы спрашиваете конкретно о передаче массива структур. У вас есть два решения: объявить 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;
ответил David Rodríguez - dribeas 10 J000000Friday09 2009, 03:35:22
0

Чтобы немного расширить некоторые ответы здесь ...

В C, когда идентификатор массива появляется в контексте, отличном от операнда для любого & или sizeof, тип идентификатора неявно преобразуется из «N-элементного массива T» в «указатель на 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 - указатель на массив из N элементов из T (в отличие от T * p [N], где p - массив указателей из N в T). Таким образом, вы можете передать указатель на массив, а не указатель на первый элемент:

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

Недостатком этого метода является то, что размер массива является фиксированным, поскольку указатель на массив из 10 элементов T отличается от указателя на массив из 20 элементов T.

Третий метод - обернуть массив в структуру:

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

Преимущество этого метода в том, что вы не балуетесь указателями. Недостатком является то, что размер массива является фиксированным (опять же, массив из 10 элементов T отличается от массива из 20 элементов T).

ответил John Bode 10 J000000Friday09 2009, 19:23:32
0

Язык C не поддерживает передачу по ссылке любого типа. Ближайшим эквивалентом является передача указателя на тип.

Вот надуманный пример на обоих языках

API стиля C ++

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

Ближайший эквивалент C

void UpdateValue(int *i) {
  *i = 42;
}
ответил JaredPar 10 J000000Friday09 2009, 03:29:32
0

также помните, что если вы создаете массив в методе, вы не можете его вернуть. Если вы вернете указатель на него, он будет удален из стека, когда функция вернется. Вы должны выделить память на кучу и вернуть указатель на это. например.

//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)
}
ответил user128026 10 J000000Friday09 2009, 03:42:11
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.

ответил haffax 10 J000000Friday09 2009, 03:36:24
0

Массивы по умолчанию передаются по ссылке. На самом деле значение указателя на первый элемент передается. Поэтому функция или метод, получающие это, могут изменять значения в массиве.

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

Два вызова эквивалентны, и значение выхода должно быть 0;

ответил dlamblin 10 J000000Friday09 2009, 03:51:01
0

Привет, ребята, вот простая тестовая программа, которая показывает, как распределять и передавать массив, используя 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;
}
ответил Jeff M 8 ThuEurope/Moscow2011-12-08T08:21:11+04:00Europe/Moscow12bEurope/MoscowThu, 08 Dec 2011 08:21:11 +0400 2011, 08:21:11

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132