2009-10-21 16 views
34

Tôi tìm thấy một số mã khởi tạo struct ngày hôm qua đã ném tôi cho một vòng lặp. Dưới đây là một ví dụ:Khởi tạo cấu trúc C bằng cách sử dụng nhãn. Nó hoạt động, nhưng làm thế nào?

typedef struct { int first; int second; } TEST_STRUCT; 
void testFunc() { 
    TEST_STRUCT test = { 
     second: 2, 
     first: 1 
    }; 
    printf("test.first=%d test.second=%d\n", test.first, test.second); 
} 

Đáng ngạc nhiên (với tôi), đây là kết quả:

-> testFunc 
test.first=1 test.second=2 

Như bạn có thể thấy, struct được khởi tạo đúng cách. Tôi đã không nhận thức được tuyên bố có nhãn có thể được sử dụng như thế. Tôi đã nhìn thấy một số cách khác để khởi tạo cấu trúc, nhưng tôi không tìm thấy bất kỳ ví dụ nào về kiểu khởi tạo cấu trúc này trên bất kỳ Câu hỏi thường gặp C trực tuyến nào. Có ai biết cách thức/tại sao nó hoạt động?

Trả lời

25

Dưới đây là phần của sổ tay gcc mà giải thích cú pháp của initializers định cho cả cấu trúc và các mảng:

Trong một initializer cấu trúc, xác định tên của một trường để khởi với '.fieldname = 'trước giá trị phần tử. Ví dụ, do cấu tạo sau,

struct point { int x, y; }; 

việc khởi tạo sau

struct point p = { .y = yvalue, .x = xvalue }; 

tương đương với

struct point p = { xvalue, yvalue }; 

Một cú pháp trong đó có ý nghĩa tương tự, lỗi thời kể từ GCC 2.5, là 'tên trường:', như được hiển thị ở đây:

struct point p = { y: yvalue, x: xvalue }; 

Trang liên quan có thể được tìm thấy here.

Trình biên dịch của bạn phải có tài liệu tương tự.

+2

Tuyệt vời, tài liệu đó giải thích rõ cú pháp: Một cú pháp khác có cùng ý nghĩa, lỗi thời kể từ GCC 2.5, là 'fieldname: ', như được hiển thị ở đây: struct điểm p = {y: yvalue, x: xvalue}; –

+1

@AndrewCottrell, cú pháp "fieldname:" này trông rất tự nhiên (và thích hợp hơn) với tôi, bất kỳ ý tưởng nào tại sao nó nên được coi là lỗi thời? – rick

+1

@rick "fieldname:" cú pháp là một phần mở rộng gcc và chưa bao giờ là một phần của bất kỳ tiêu chuẩn ISO C nào. – sigjuice

5

Nó không thực sự là "tuyên bố có nhãn", nhưng là một cách để đưa ra giá trị ban đầu cho các trường được đặt tên trong cấu trúc.

Gcc đưa ra một cảnh báo về "sử dụng đã lỗi thời của initializer định với ':'", và trong C99 bạn thay vì nên viết:

TEST_STRUCT test = { 
     .second = 2, 
     .first = 1 
    }; 
32

Đây là những không nhãn cũng không bitfields.

Đây là cú pháp để khởi tạo thành viên cấu trúc có niên đại từ ngày trước C99. Nó không được tiêu chuẩn hóa nhưng có sẵn trong ví dụ gcc.

typedef struct { int y; int x; } POINT; 
POINT p = { x: 1, y: 17 }; 

Trong C99, cú pháp để khởi tạo các thành viên struct cụ thể đã được giới thiệu lần đầu tiên trong một tiêu chuẩn, nhưng có vẻ một chút khác nhau:

typedef struct { int y; int x; } POINT; 
POINT p = { .x = 1, .y = 17 }; 
+0

Vâng, tôi đã biết định dạng khởi tạo được chỉ định. Thật không may là định dạng không tương thích với C++! (Không phải trong thử nghiệm của tôi anyway.) Cảm ơn câu trả lời mặc dù. Điều tốt để biết điều này không được chuẩn hóa. –

+6

@Andrew bạn không bao giờ nói bất cứ điều gì về C++ trong câu hỏi của bạn – horseyguy

3

Đó cú pháp không được xác định bởi các tiêu chuẩn C . Mục 6.7.8 Initialization nói

  designation: 
       designator-list = 
     designator-list: 
       designator 
       designator-list designator 
     designator: 
       [ constant-expression ] 
       . identifier 

Nếu trình biên dịch của bạn chấp nhận một định danh với một dấu hai chấm mà không có một thông điệp chẩn đoán nó có nghĩa là trình biên dịch của bạn không phải là (hoặc được cấu hình không đến được) Tiêu chuẩn tuân thủ.

9

Có, như đã chỉ ra ở trên, đây là các trình khởi tạo được chỉ định, là tiêu chuẩn C, mặc dù bạn nên chuyển sang sử dụng dấu chấm thay vì dấu hai chấm. Và như bạn lưu ý, hầu hết các cuốn sách hiện có vẫn còn bị mắc kẹt ở đâu đó khoảng năm 1984 trong cú pháp của họ và không đề cập đến chúng. Các sự kiện thú vị khác:

--Khi sử dụng trình khởi tạo được chỉ định, mọi thứ không được chỉ định được khởi tạo ở mức 0. Điều này giúp với cấu trúc đặc biệt lớn, ví dụ:

typedef struct { 
    double a, b, c, d, e; 
    char label[100]; 
} too_many_type; 

too_many_type tm = {.a = 1, .e = 2, .b=1.5}; 
assert(tm.a + tm.b + tm.c + tm.d + tm.e == 4.5); 
assert(!strlen(label)); 

--Also, bạn có thể sử dụng hình thức văn chương hợp chất sử dụng hình thức này trên một dòng phi khởi, ví dụ:

too_many_type tm2; 
tm2 = (too_many_type) {.a = 3, .e=6}; 

Đây là thực sự các tính năng tuyệt vời và được hỗ trợ bởi mọi trình biên dịch C mà tôi có thể nghĩ đến, đó là tiêu chuẩn. Đó là một sự xấu hổ mà họ không nổi tiếng lắm.

+1

Cú pháp thay thế (sử dụng dấu hai chấm) trông rất tự nhiên và thích hợp hơn với tôi, bạn có thể làm rõ tại sao chúng ta nên chuyển sang sử dụng dấu chấm thay vì dấu hai chấm? – rick

Các vấn đề liên quan