2010-07-06 31 views
13

Tôi tò mò sự khác biệt ở đây là khi typedefing một enum hoặc struct. Có sự khác biệt nào về mặt ngữ nghĩa giữa hai khối này không?Sự khác biệt giữa hai kiểu typedef này trong C là gì?

này:

typedef enum { first, second, third } SomeEnum; 

và điều này:

enum SomeEnum { first, second, third }; 
typedef enum SomeEnum SomeEnum; 

Tương tự đối phó cho cấu trúc. Tôi đã nhìn thấy cả hai sử dụng, và cả hai dường như làm điều tương tự trong C hoặc Objective-C. Có sự khác biệt thực sự hay chỉ là một sở thích mà bạn có thể sử dụng kiểu nào?

Trả lời

15

Sự khác biệt là cách tiếp cận thứ hai tuyên bố loại có tên là enum SomeEnum và cũng khai báo tên typedef-SomeEnum - bí danh cho loại đó. Nó thực sự có thể được kết hợp thành tương đương với một lót

typedef enum SomeEnum { first, second, third } SomeEnum; 

mà làm cho nó khá rõ ràng rằng sự khác biệt duy nhất giữa hai phương pháp là liệu có một tên sau từ khóa enum. Với cách tiếp cận thứ hai, bạn có thể khai báo đối tượng của kiểu enum đó bằng cách sử dụng SomeEnum e hoặc enum SomeEnum e, tùy theo bạn thích.

Cách tiếp cận đầu tiên chỉ khai báo typedef-name SomeEnum cho loại enum ẩn danh ban đầu, có nghĩa là bạn bị giới hạn ở SomeEnum e khai báo.

Vì vậy, miễn là bạn chỉ sử dụng typedef-name SomeEnum trong các khai báo của bạn, sẽ không có sự khác biệt giữa hai loại. Tuy nhiên, trong một số trường hợp, bạn có thể phải sử dụng tên đầy đủ của loại enum SomeEnum. Trong cách tiếp cận đầu tiên mà tên không có sẵn, vì vậy bạn sẽ không may mắn.

Ví dụ, nếu sau khi tuyên bố ở trên, bạn cũng khai báo một biến có tên SomeEnum trong một số phạm vi lồng nhau

int SomeEnum; 

tên của biến sẽ ẩn typedef tên của enum, do đó làm cho tuyên bố này bất hợp pháp

SomeEnum e; /* ERROR: `SomeEnum` is not a type */ 

Tuy nhiên, nếu bạn sử dụng cách tiếp cận thứ hai khi tuyên bố enum, bạn có thể làm việc xung quanh vấn đề này bằng cách sử dụng các loại tên đầy đủ

enum SomeEnum e; /* OK */ 

Điều này sẽ không thể thực hiện được nếu bạn đã sử dụng cách tiếp cận đầu tiên khi khai báo loại enum của bạn.

Khi sử dụng với cấu trúc, tên sau khi struct là điều bắt buộc khi bạn cần một loại tự tham khảo (một loại có chứa một con trỏ đến cùng loại), như

typedef struct SomeStruct { 
    struct SomeStruct *next; 
} SomeStruct; 

Cuối cùng, trong cách tiếp cận thứ hai tên typedef là hoàn toàn tùy chọn. Bạn chỉ có thể khai báo

enum SomeEnum { first, second, third }; 

và chỉ sử dụng enum SomeEnum mỗi khi bạn cần tham khảo loại này.

7

Có, có sự khác biệt ngữ nghĩa. Đoạn mã thứ hai khai báo một mã định danh thẻ, nhưng từ đầu tiên không. Cả hai đều khai báo một định danh thông thường.

Điều đó có nghĩa rằng đối với lần đầu tiên, mã này là không hợp lệ, nhưng đối với thứ hai, đó là:

enum SomeEnum foo; 

Theo như tôi biết, không có sự khác biệt về ngữ nghĩa khác giữa chúng trong mã của bạn. Đối với cấu trúc và các đoàn thể, hình thức thứ hai, có thể kết hợp với typedef trong một tuyên bố, là cần thiết cho các loại đệ quy

typedef struct node { 
    struct node *parent; // refer to the tag identifier 
} node; 

Từ định danh thông thường vẫn chưa được nhìn thấy trong specifier của struct, và do đó bạn cần phải tham khảo các struct bởi từ định danh thẻ đã được khai báo. Định danh thẻ được đề cập đến bằng cách thêm chúng bằng "struct", "union" hoặc "enum", trong khi số nhận dạng thông thường được tham chiếu mà không có tiền tố (do đó tên "thông thường").

Bên cạnh đó tách các định đề cập tới cấu trúc, các đoàn thể và enumerations từ những người mà tham khảo giá trị, thẻ định danh cũng hữu ích cho việc tạo ra về phía trước tờ khai:

/* forward declaration */ 
struct foo; 

/* for pointers, forward declarations are entirely sufficient */ 
struct foo *pfoo = ...; 

/* ... and then later define its contents */ 
struct foo { 
    /* ... */ 
}; 

typedef tên không thể được khai báo liên tục tại cùng phạm vi (như trái ngược với C++ nơi chúng có thể), và chúng cần tham chiếu đến một kiểu hiện có, để chúng không thể được sử dụng để tạo ra các khai báo chuyển tiếp.

4

Sự khác biệt duy nhất là trong trường hợp thứ hai, bạn có thể sử dụng một cái gì đó như:

enum SomeEnum x; 

trong khi người đầu tiên chỉ hỗ trợ:

SomeEnum x; 

Để những người đã viết C một thời gian dài, xác định struct mà không có từ khóa struct thường "cảm thấy" lạ ...

2

Biểu mẫu đầu tiên tạo một loại enum ẩn danh và tạo bí danh SomeEnum cho nó.

Biểu mẫu thứ hai tạo cả loại enum SomeEnum và bí danh SomeEnum cho nó.

(Trong C, có các không gian tên riêng biệt cho các loại. Tức là, struct Foo khác với enum Foo khác với Foo.)

Điều này quan trọng hơn đối với struct s hơn enum vì bạn cần sử dụng biểu mẫu thứ hai nếu struct của bạn tự tham chiếu. Ví dụ:

struct LinkedListNode 
{ 
    void* item; 
    struct LinkedListNode* next; 
}; 

typedef struct LinkedListNode LinkedListNode; 

Ở trên sẽ không thể thực hiện được với biểu mẫu đầu tiên.

0

Đối với struct s có sự khác biệt thực sự không đơn giản chỉ là đặt tên.

này có giá trị C:

struct SomeEnum { struct SomeEnum *first; }; 

Đây không phải là:

typedef struct { SomeEnum *first; } SomeEnum; 
0

Thêm vào comment user207442, nó có thể cho một mô-đun mã nguồn để khai báo các biến kiểu "struct foo *" mà không bao giờ có định nghĩa cho cấu trúc. Một mô-đun như vậy sẽ không thể dereference con trỏ như vậy, nhưng có thể vượt qua chúng đến và đi từ các mô-đun khác.

Ví dụ: một tệp có thể có tệp tiêu đề xác định loại "USERCONSOLE" bằng cách sử dụng "typedef struct _USERCONSOLE * USERCONSOLE;". Mã # include của tệp tiêu đề đó có thể có các biến kiểu USERCONSOLE và chuyển các biến đó vào/từ các mô-đun biết một _USERCONSOLE thực sự là gì, không có tệp tiêu đề để hiển thị định nghĩa thực tế của cấu trúc.

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