2010-07-12 23 views
37

ở đây là rất đơn giản mã của vấn đề tôi có:Công đoàn ẩn danh trong cấu trúc không phải trong c99?

 
enum node_type { 
    t_int, t_double 
}; 

struct int_node { 
    int value; 
}; 

struct double_node { 
    double value; 
}; 

struct node { 
    enum node_type type; 
    union { 
     struct int_node int_n; 
     struct double_node double_n; 
    }; 
}; 

int main(void) { 
    struct int_node i; 
    i.value = 10; 
    struct node n; 
    n.type = t_int; 
    n.int_n = i; 
    return 0; 
} 

Và những gì tôi không undestand này là:

 
$ cc us.c 
$ cc -std=c99 us.c 
us.c:18:4: warning: declaration does not declare anything 
us.c: In function ‘main’: 
us.c:26:4: error: ‘struct node’ has no member named ‘int_n’ 

Sử dụng GCC mà không -std tùy chọn biên dịch mã trên mà không cần bất kỳ vấn đề (và mã tương tự đang hoạt động khá tốt), nhưng có vẻ như là c99 không cho phép kỹ thuật này. Tại sao nó như vậy và nó có thể thực hiện là c99 (hoặc c89, c90) tương thích? Cảm ơn.

+1

Chỉ cần lưu ý, clang biên dịch mã đã cho và không có '-std = c99' âm thầm, không có bất kỳ lỗi và cảnh báo nào. – Martin

Trả lời

2

Liên minh phải có tên và được khai báo như thế này:

union UPair { 
    struct int_node int_n; 
    struct double_node double_n; 
}; 

UPair X; 
X.int_n.value = 12; 
+2

Không có trong C11, nhưng trong C99 có. Nhưng kể từ khi chúng tôi đã vượt qua mốc ba năm kể từ khi phát hành, có lẽ đã đến lúc bắt đầu đi qua -std = c11 :). –

50

đoàn Anonymous là một phần mở rộng GNU, không nằm trong bất kỳ phiên bản tiêu chuẩn của ngôn ngữ C. Bạn có thể sử dụng -std = gnu99 hoặc một cái gì đó như thế cho c99 + GNU mở rộng, nhưng tốt nhất là viết đúng C và không dựa vào phần mở rộng không cung cấp gì ngoài cú pháp đường ...

Chỉnh sửa: Công đoàn ẩn danh được thêm vào trong C11, vì vậy chúng bây giờ là một phần tiêu chuẩn của ngôn ngữ. Có lẽ -std=c11 của GCC cho phép bạn sử dụng chúng.

4

Vâng, giải pháp là đặt tên trường hợp của liên minh (có thể vẫn ẩn danh là kiểu dữ liệu) và sau đó sử dụng tên đó làm proxy.

 
$ diff -u old_us.c us.c 
--- old_us.c 2010-07-12 13:49:25.000000000 +0200 
+++ us.c  2010-07-12 13:49:02.000000000 +0200 
@@ -15,7 +15,7 @@ 
    union { 
    struct int_node int_n; 
    struct double_node double_n; 
- }; 
+ } data; 
}; 

int main(void) { 
@@ -23,6 +23,6 @@ 
    i.value = 10; 
    struct node n; 
    n.type = t_int; 
- n.int_n = i; 
+ n.data.int_n = i; 
    return 0; 
} 

Bây giờ nó biên dịch thành c99 mà không gặp bất kỳ sự cố nào.

 
$ cc -std=c99 us.c 
$ 

Lưu ý: Tôi không hài lòng về giải pháp này.

+3

Bạn nên hạnh phúc! Đó là cách tiêu chuẩn để truy cập các thành viên công đoàn, đảm bảo làm việc với bất kỳ trình biên dịch C nào kể từ ngày 1 tháng 1 năm 1970. – Jens

+2

Nó ugglies lên mã phần nào, không có ý tưởng tại sao nó không được bao gồm trong K & R C, có vẻ như một tính năng đơn giản và hữu ích cho tôi ... Dù sao tôi sử dụng cùng một phương pháp proxy nhưng xác định macro để tránh tất cả các đánh máy. –

+2

Tôi nhận ra đây là một bài đăng rất cũ, nhưng sao chép mã thực tế thay vì một bản vá khác có thể đọc được nhiều hơn. – ysap

0

Nhìn vào 6.2.7.1 của C99, tôi thấy rằng các định danh là không bắt buộc:

  struct-or-union-specifier: 
        struct-or-union identifier-opt { struct-declaration-list } 
        struct-or-union identifier 

      struct-or-union: 
        struct 
        union 

      struct-declaration-list: 
        struct-declaration 
        struct-declaration-list struct-declaration 

      struct-declaration: 
        specifier-qualifier-list struct-declarator-list ; 

      specifier-qualifier-list: 
        type-specifier specifier-qualifier-list-opt 
        type-qualifier specifier-qualifier-list-opt 

Tôi đã lên xuống tìm kiếm, và không thể tìm thấy bất kỳ tài liệu tham khảo để công đoàn vô danh là chống lại spec. Hậu tố toàn bộ -opt chỉ ra rằng, trong trường hợp này, identifier là tùy chọn theo 6.1.

+4

Tôi nghĩ có một sự hiểu lầm ở đây. Mã định danh cho cấu trúc hoặc thẻ ** công đoàn ** là tùy chọn, nhưng không phải là số nhận dạng đang được khai báo. Bạn không thể nói 'union {...};' trong một số tổng hợp cho cùng một lý do bạn không thể nói 'int;'. Trong trường hợp công đoàn, các phần mở rộng của trình biên dịch cho phép nó, bởi vì bạn có thể sử dụng các mã định danh trong phần '{...}' khi sử dụng một liên kết ẩn danh. – Jens

21

Tôi đang tìm câu hỏi này khoảng một năm rưỡi sau khi mọi người khác làm, vì vậy tôi có thể đưa ra một câu trả lời khác: các cấu trúc ẩn danh không có trong tiêu chuẩn C99, nhưng chúng nằm trong tiêu chuẩn C11. GCC và clang đã hỗ trợ điều này (tiêu chuẩn C11 dường như đã nâng tính năng từ Microsoft, và GCC đã cung cấp hỗ trợ cho một số phần mở rộng MSFT một thời gian).

1

Một giải pháp khác là đặt giá trị tiêu đề chung (enum node_type type) vào mọi cấu trúc và đặt cấu trúc cấp cao nhất thành liên kết. Nó không chính xác là "Đừng lặp lại chính mình", nhưng nó không tránh được cả các công đoàn ẩn danh và các giá trị proxy tìm kiếm không thoải mái.

enum node_type { 
    t_int, t_double 
}; 
struct int_node { 
    enum node_type type; 
    int value; 
}; 
struct double_node { 
    enum node_type type; 
    double value; 
}; 
union node { 
    enum node_type type; 
    struct int_node int_n; 
    struct double_node double_n; 
}; 

int main(void) { 
    union node n; 
    n.type = t_int; // or n.int_n.type = t_int; 
    n.int_n.value = 10; 
    return 0; 
} 
+0

Đối với những người đọc cuối như tôi: DRY có thể tránh được bằng cách sử dụng mẫu và các kiểu chữ thích hợp: 'template struct base_node {/*[...]*/ T value;}; typedef base_node int_node; ' – Aconcagua

+3

Trong C++ có thể, nhưng không phải trong C99. – theJPster

+0

Ah, xin lỗi, bằng cách nào đó quên mất việc ở trong C trong khi đọc và suy nghĩ về ... Tôi đã quá sâu trong C + + cuối cùng. – Aconcagua

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