7 ответов:
в 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 ); }в вопросе edit вы спрашиваете конкретно о передаче массива структур. У вас есть два решения: объявить 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, тип идентификатора неявно преобразуется из "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.
третий метод заключается в том, чтобы обернуть массив в a структуру:
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).
язык C не поддерживает передачу по ссылке любого типа. Ближайшим эквивалентом является передача указателя на тип.
вот пример на обоих языках
C++ style 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) }
в простом C вы можете использовать комбинацию указатель/размер в вашем API.
void doSomething(MyStruct* mystruct, size_t numElements) { for (size_t i = 0; i < numElements; ++i) { MyStruct current = mystruct[i]; handleElement(current); } }использование указателей является самым близким к call-by-reference доступным в C.
массивы эффективно передаются по ссылке по умолчанию. Фактически передается значение указателя на первый элемент. Поэтому функция или метод, получающие это, могут изменять значения в массиве.
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;
Эй, ребята, вот простая тестовая программа, которая показывает, как выделять и передавать массив с помощью 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; }
Comments