2009-07-09 50 views
56

Làm cách nào để truyền một mảng cấu trúc bằng tham chiếu trong C?Chuyển mảng bằng tham chiếu trong C?

Như một ví dụ:

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

Trả lời

115

Trong mảng C được thông qua như là một con trỏ đến phần tử đầu tiên. Chúng là phần tử duy nhất không thực sự được truyền bởi giá trị (con trỏ được truyền theo giá trị, nhưng mảng không được sao chép). Điều đó cho phép hàm được gọi để sửa đổi nội dung.

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 
} 

Bây giờ, nếu những gì bạn muốn thay đổi các mảng chính nó (số nguyên tố ...) bạn không thể làm điều đó với chồng hoặc toàn cầu mảng, chỉ với bộ nhớ cấp phát động trong heap. Trong trường hợp đó, nếu bạn muốn thay đổi con trỏ bạn phải vượt qua một con trỏ đến nó:

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

Trong chỉnh sửa câu hỏi mà bạn hỏi cụ thể về đi qua một loạt các cấu trúc. Bạn có hai giải pháp đó: khai báo một typedef, hoặc làm cho rõ ràng rằng bạn đang đi qua một struct:

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 

Bạn có thể typedef kiểu như bạn khai báo nó (và nó là một thành ngữ phổ biến trong C):

typedef struct Coordinate { 
    int x; 
    int y; 
} Coordinate; 
+1

sử dụng bộ nhớ (mảng, 0, mảng sizeof); thay vì cho() chu kỳ bên trong thiết lập lại :) – rodi

+4

@rodi: Đó là một điểm thú vị, xem xét rằng bạn đã giới thiệu một lỗi :) Tôi đã cập nhật mã để sử dụng 'memset' vì nó nên được sử dụng:' memset (array, 0, size * sizeof * array) '-' sizeof (mảng) 'trong trường hợp này là kích thước của một con trỏ * *, không phải là dữ liệu nhọn. –

+0

Thực hành tốt ** không ** loại bỏ giá trị trả về của malloc. xem [ở đây] (https: // stackoverflow.com/questions/605845/do-i-cast-the-kết quả-of-malloc) – Aka

8

C ngôn ngữ không hỗ trợ vượt qua bằng cách tham chiếu của bất kỳ loại. Tương đương gần nhất là chuyển con trỏ tới kiểu.

Dưới đây là một ví dụ giả tạo trong cả hai ngôn ngữ

++ phong cách C API

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

gần nhất C tương đương

void UpdateValue(int *i) { 
    *i = 42; 
} 
+0

tôi biết nhưng tôi thấy vấn đề chuyển một mảng với cùng phương thức: s –

+3

@Yassir, bạn có thể đưa ra ví dụ không? – JaredPar

+0

Ví dụ này chứng tỏ việc truyền đi một kiểu nguyên thủy bằng tham chiếu (sử dụng một con trỏ được truyền theo giá trị) nhưng không chứng minh việc truyền một mảng các cấu trúc bằng tham chiếu như được trình bày đúng trong bài viết dưới đây. –

6

Trong đồng bằng C, bạn có thể sử dụng kết hợp con trỏ/kích thước trong API của mình.

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

Sử dụng con trỏ là gần nhất để gọi-by-reference sẵn trong C.

6

cũng lưu ý rằng nếu bạn đang tạo một mảng trong vòng một phương pháp, bạn không thể trả lại. Nếu bạn trả về một con trỏ đến nó, nó sẽ bị loại bỏ khỏi ngăn xếp khi hàm trả về. bạn phải cấp phát bộ nhớ trên heap và trả về một con trỏ tới đó. ví dụ:

//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) 
} 
5

Mảng được chuyển hiệu quả theo tham chiếu theo mặc định. Trên thực tế, giá trị của con trỏ tới phần tử đầu tiên được chuyển. Do đó hàm hoặc phương thức nhận được điều này có thể sửa đổi các giá trị trong mảng.

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

Hai cuộc gọi tương đương và giá trị thoát phải bằng 0;

9

Để mở rộng một chút về một số câu trả lời ở đây ...Trong C, khi một định danh mảng xuất hiện trong một ngữ cảnh không phải là toán hạng cho & hoặc sizeof, loại định danh được chuyển đổi hoàn toàn từ "phần tử N thành phần của T" thành "con trỏ đến T", và giá trị của nó được đặt ngầm định đến địa chỉ của phần tử đầu tiên trong mảng (giống như địa chỉ của chính mảng đó). Đó là lý do tại sao khi bạn chỉ chuyển định danh mảng làm đối số cho một hàm, hàm nhận một con trỏ tới kiểu cơ sở, chứ không phải là một mảng. Vì bạn không thể biết được một mảng lớn như thế nào chỉ bằng cách nhìn vào con trỏ đến phần tử đầu tiên, bạn phải chuyển kích thước thành một tham số riêng biệ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); 
    ... 
} 

Có một số cách thay thế để chuyển mảng vào chức năng.

Có một điều như một con trỏ đến một mảng của T, như trái ngược với một con trỏ tới T. Bạn sẽ khai báo một con trỏ như

T (*p)[N]; 

Trong trường hợp này, p là một con trỏ đến một Mảng phần tử N của T (ngược với T * p [N], trong đó p là mảng phần tử N của con trỏ tới T). Vì vậy, bạn có thể vượt qua một con trỏ đến mảng như trái ngược với một con trỏ đến phần tử đầu tiê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); 
    ... 
} 

Những bất lợi của phương pháp này là kích thước mảng là cố định, vì một con trỏ đến một mảng 10 phần tử của T là một loại khác nhau từ một con trỏ đến một mảng 20 phần tử của T.

một phương pháp thứ ba là để bọc mảng trong một cấu trúc:

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

Ưu điểm của phương pháp này là bạn không mút xung quanh với con trỏ. Điểm bất lợi là kích thước mảng được cố định (một lần nữa, một mảng 10 phần tử của T là một kiểu khác với một mảng 20 phần tử của T).

+0

Một bất lợi của công thức bao bọc của bạn là 'wrapper' đang được truyền bởi _value_. 80 byte (-ish), và phép gán bên trong 'SomeMethod()' không có hiệu lực trên 'wrapper' được khai báo trong' main'. Trông giống như một con bọ. – bobbogo

2

Xin chào các bạn ở đây là một chương trình thử nghiệm đơn giản cho thấy cách phân bổ và truyền một mảng bằng cách sử dụng mới hoặc malloc. Chỉ cần cắt, dán và chạy nó. Chúc vui vẻ!

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; 
} 
Các vấn đề liên quan