Tại sao chúng ta nên gõ một cấu trúc thường xuyên trong C?


406

Tôi đã thấy nhiều chương trình bao gồm các cấu trúc như bên dưới

typedef struct 
{
    int i;
    char k;
} elem;

elem user;

Tại sao nó cần thiết thường xuyên như vậy? Bất kỳ lý do cụ thể hoặc khu vực áp dụng?


14
Câu trả lời kỹ lưỡng và chính xác hơn: stackoverflow.com/questions/612328/ từ
AbiusX 17/03/2016

Nó có nhược điểm Tôi nghĩ rằng bạn không thể tạo danh sách liên kết với cấu trúc ẩn danh vì dòng struct * ptrbên trong cấu trúc sẽ gây ra lỗi
Raghib Ahsan

9
"Câu trả lời chính xác và chính xác hơn" là sự khác biệt giữa struct và typedef struct trong C ++ , và có sự khác biệt đáng kể giữa C và C ++ trong lĩnh vực này khiến câu trả lời đó không hoàn toàn phù hợp cho câu hỏi về C.
Jonathan Leffler

Câu hỏi này có một định nghĩa typedef struct so với định nghĩa struct cũng có câu trả lời xuất sắc.
Jonathan Leffler

2
OTOH, kernel.org/doc/html/v4.10/ process / code-style.html cho chúng ta biết rằng chúng ta không nên làm những typedefs như vậy.
glglgl

Câu trả lời:


454

Như Greg Hewgill đã nói, typedef có nghĩa là bạn không còn phải viết ở structkhắp mọi nơi. Điều đó không chỉ tiết kiệm tổ hợp phím, nó còn có thể làm cho mã sạch hơn vì nó cung cấp một bản tóm tắt nhiều hơn.

Những thứ như

typedef struct {
  int x, y;
} Point;

Point point_new(int x, int y)
{
  Point a;
  a.x = x;
  a.y = y;
  return a;
}

trở nên sạch hơn khi bạn không cần phải xem từ khóa "struct" ở mọi nơi, nó trông giống như thực sự có một loại gọi là "Điểm" trong ngôn ngữ của bạn. Mà, sau khi typedef, là trường hợp tôi đoán.

Cũng lưu ý rằng trong khi ví dụ của bạn (và của tôi) đã bỏ qua việc đặt tên cho struct chính nó, thì thực tế việc đặt tên nó cũng hữu ích khi bạn muốn cung cấp một loại mờ. Sau đó, bạn sẽ có mã như thế này trong tiêu đề:

typedef struct Point Point;

Point * point_new(int x, int y);

và sau đó cung cấp structđịnh nghĩa trong tệp thực hiện:

struct Point
{
  int x, y;
};

Point * point_new(int x, int y)
{
  Point *p;
  if((p = malloc(sizeof *p)) != NULL)
  {
    p->x = x;
    p->y = y;
  }
  return p;
}

Trong trường hợp sau này, bạn không thể trả về Điểm theo giá trị, vì định nghĩa của nó bị ẩn khỏi người dùng của tệp tiêu đề. Đây là một kỹ thuật được sử dụng rộng rãi trong GTK + , ví dụ.

CẬP NHẬT Lưu ý rằng cũng có những dự án C được đánh giá cao trong đó việc sử dụng typedefđể ẩn structnày được coi là một ý tưởng tồi, hạt nhân Linux có lẽ là dự án nổi tiếng nhất như vậy. Xem Chương 5 của tài liệu Mã hóa hạt nhân Linux để biết những lời tức giận của Linus. :) Quan điểm của tôi là "nên" trong câu hỏi có lẽ không được đặt trong đá, sau tất cả.


58
Bạn không nên sử dụng mã định danh có dấu gạch dưới theo sau là chữ in hoa, chúng được bảo lưu (xem phần 7.1.3 đoạn 1). Mặc dù không có nhiều vấn đề, nhưng đó là hành vi không xác định về mặt kỹ thuật khi bạn sử dụng chúng (7.1.3 đoạn 2).
dreamlax

16
@dreamlax: Trong trường hợp không rõ ràng với người khác, điều đó chỉ bắt đầu một định danh với dấu gạch dưới và chữ hoa mà bạn không nên làm; bạn có thể sử dụng nó ở giữa một định danh.
brianmearn

12
Thật thú vị khi ví dụ được đưa ra ở đây (trong đó typedef ngăn sử dụng "struct" "ở khắp mọi nơi") thực sự dài hơn cùng một mã mà không có typedef, vì nó lưu chính xác một lần sử dụng từ "struct". Các smidgen của sự trừu tượng thu được hiếm khi so sánh thuận lợi với obfuscation bổ sung.
William Pursell

7
@Rerito fyi, trang 166 của dự thảo C99 , Tất cả các định danh bắt đầu bằng dấu gạch dưới và một chữ cái viết hoa hoặc một dấu gạch dưới khác luôn được dành riêng cho mọi mục đích sử dụng. tất cả các định danh bắt đầu bằng dấu gạch dưới luôn được dành riêng để sử dụng làm định danh với phạm vi trong cả hai không gian tên thông thường và thẻ.
e2-e4

10
Thật thú vị, các hướng dẫn mã hóa hạt nhân linux nói rằng chúng ta nên thận trọng hơn rất nhiều về việc sử dụng typedefs (phần 5): kernel.org/doc/Documentation/CodingStyle
gsingh2011

206

Thật đáng ngạc nhiên khi có nhiều người mắc phải sai lầm này. VUI LÒNG không gõ các cấu trúc typedef trong C, nó không gây ô nhiễm không gian tên toàn cầu thường bị ô nhiễm trong các chương trình C lớn.

Ngoài ra, các cấu trúc typedef'd không có tên thẻ là nguyên nhân chính của việc áp đặt các mối quan hệ không cần thiết giữa các tệp tiêu đề.

Xem xét:

#ifndef FOO_H
#define FOO_H 1

#define FOO_DEF (0xDEADBABE)

struct bar; /* forward declaration, defined in bar.h*/

struct foo {
  struct bar *bar;
};

#endif

Với định nghĩa như vậy, không sử dụng typedefs, một đơn vị biên dịch có thể bao gồm foo.h để có được FOO_DEFđịnh nghĩa. Nếu nó không cố gắng hủy đăng ký thành viên 'bar' của foostruct thì sẽ không cần phải bao gồm tệp "bar.h".

Ngoài ra, do không gian tên khác nhau giữa tên thẻ và tên thành viên, nên có thể viết mã rất dễ đọc như:

struct foo *foo;

printf("foo->bar = %p", foo->bar);

Vì các không gian tên là riêng biệt, không có xung đột trong việc đặt tên biến trùng với tên thẻ struct của chúng.

Nếu tôi phải duy trì mã của bạn, tôi sẽ xóa cấu trúc typedef'd của bạn.


37
Điều tuyệt vời hơn là 13 tháng sau khi câu trả lời này được đưa ra, tôi là người đầu tiên nâng cao nó! cấu trúc typedef'ing là một trong những lạm dụng lớn nhất của C và không có chỗ trong mã được viết tốt. typedef rất hữu ích cho việc loại bỏ các loại con trỏ hàm phức tạp và thực sự không phục vụ mục đích hữu ích nào khác.
William Pursell

28
Peter van der Linden cũng đưa ra một trường hợp chống lại các cấu trúc đánh máy trong cuốn sách khai sáng của mình "Expert C Lập trình - Bí mật Deep C". Ý chính là: Bạn MUỐN biết rằng một cái gì đó là một cấu trúc hoặc liên minh, không che giấu nó.
Jens

34
Kiểu mã hóa nhân Linux rõ ràng cấm các cấu trúc typedefing. Chương 5: Typedefs: "Thật sai lầm khi sử dụng typedef cho các cấu trúc và con trỏ." kernel.org/doc/Documentation/CodingStyle
jasso

63
Chính xác thì lợi ích gì khi gõ "struct" nhiều lần? Và nói về ô nhiễm, tại sao bạn muốn có một cấu trúc và một hàm / biến / typedef có cùng tên trong một không gian tên toàn cầu (trừ khi đó là một typedef cho cùng chức năng đó)? Các mô hình an toàn là sử dụng typedef struct X { ... } X. Bằng cách đó, bạn có thể sử dụng biểu mẫu ngắn Xđể giải quyết cấu trúc ở bất kỳ nơi nào định nghĩa có sẵn, nhưng vẫn có thể chuyển tiếp khai báo và sử dụng struct Xkhi muốn.
Pavel Minaev

7
Cá nhân tôi rất hiếm khi sử dụng typedef, tôi sẽ không nói người khác không nên sử dụng nó, đó không phải là phong cách của tôi. Tôi muốn thấy một cấu trúc trước một loại biến để tôi biết ngay một cấu trúc của nó. Đối số dễ gõ là hơi khập khiễng, có biến là một chữ cái cũng dễ gõ hơn, cũng như tự động hoàn thành mức độ khó để gõ struct ngày nay ở bất cứ đâu.
Ngày của Nathan ngày

138

Từ một bài viết cũ của Dan Saks ( http://www.ddj.com/cpp/184403396?pgno=3 ):


Các quy tắc ngôn ngữ C để đặt tên cấu trúc là một chút lập dị, nhưng chúng khá vô hại. Tuy nhiên, khi được mở rộng cho các lớp trong C ++, các quy tắc tương tự sẽ mở ra các vết nứt nhỏ để các lỗi phát hiện.

Trong C, tên s xuất hiện trong

struct s
    {
    ...
    };

là một thẻ. Tên thẻ không phải là tên loại. Đưa ra định nghĩa trên, các khai báo như

s x;    /* error in C */
s *p;   /* error in C */

là lỗi trong C. Bạn phải viết chúng là

struct s x;     /* OK */
struct s *p;    /* OK */

Tên của các hiệp hội và liệt kê cũng là các thẻ chứ không phải là các loại.

Trong C, các thẻ khác với tất cả các tên khác (cho các hàm, loại, biến và hằng số liệt kê). Trình biên dịch C duy trì các thẻ trong bảng ký hiệu về mặt khái niệm nếu không tách biệt về mặt vật lý với bảng chứa tất cả các tên khác. Vì vậy, chương trình C có thể có cả thẻ và tên khác có cùng cách viết trong cùng một phạm vi. Ví dụ,

struct s s;

là một khai báo hợp lệ khai báo biến s của kiểu struct s. Nó có thể không phải là thực hành tốt, nhưng trình biên dịch C phải chấp nhận nó. Tôi chưa bao giờ thấy một lý do tại sao C được thiết kế theo cách này. Tôi đã luôn nghĩ rằng đó là một sai lầm, nhưng nó có.

Nhiều lập trình viên (bao gồm cả bạn thực sự) thích nghĩ về tên cấu trúc như tên loại, vì vậy họ xác định bí danh cho thẻ bằng cách sử dụng typedef. Ví dụ: xác định

struct s
    {
    ...
    };
typedef struct s S;

cho phép bạn sử dụng S thay cho struct s, như trong

S x;
S *p;

Một chương trình không thể sử dụng S làm tên của cả loại và biến (hoặc hàm hoặc hằng số liệt kê):

S S;    // error

Điều này là tốt

Tên thẻ trong định nghĩa struct, union hoặc enum là tùy chọn. Nhiều lập trình viên gấp định nghĩa cấu trúc vào typedef và phân phối với thẻ hoàn toàn, như trong:

typedef struct
    {
    ...
    } S;

Bài viết được liên kết cũng có một cuộc thảo luận về cách hành vi của C ++ không yêu cầu typedefcó thể gây ra các vấn đề ẩn tên tinh tế. Để ngăn chặn những vấn đề này, đó cũng là một ý tưởng tốt cho typedefcác lớp và cấu trúc của bạn trong C ++, mặc dù thoạt nhìn có vẻ không cần thiết. Trong C ++, với việc typedefẩn tên trở thành một lỗi mà trình biên dịch cho bạn biết chứ không phải là một nguồn ẩn của các vấn đề tiềm ẩn.


2
Một ví dụ về nơi tên thẻ giống với tên không có thẻ trong chương trình (POSIX hoặc Unix) có int stat(const char *restrict path, struct stat *restrict buf)chức năng. Ở đó bạn có một chức năng stattrong không gian tên thông thường và struct stattrong không gian tên thẻ.
Jonathan Leffler

2
tuyên bố của bạn, SS; // lỗi .... LÀ SAU Nó hoạt động tốt. Ý tôi là tuyên bố của bạn rằng "chúng tôi không thể có cùng tên cho thẻ typedef và var" là
SAU

63

Sử dụng typedefđể tránh phải viết structmỗi khi bạn khai báo một biến loại đó:

struct elem
{
 int i;
 char k;
};
elem user; // compile error!
struct elem user; // this is correct

2
ok chúng tôi không có vấn đề đó trong C ++. Vậy tại sao không ai gỡ bỏ trục trặc đó khỏi trình biên dịch của C và làm cho nó giống như trong C ++. Ok C ++ đang có một số lĩnh vực ứng dụng khác nhau và vì vậy nó có các tính năng nâng cao. Nhưng chúng ta không thể thừa hưởng một số trong chúng trong C mà không thay đổi C gốc?
Manoj nghi ngờ

4
Manoj, tên thẻ ("struct foo") là cần thiết khi bạn cần xác định cấu trúc tham chiếu chính nó. ví dụ con trỏ "tiếp theo" trong danh sách được liên kết. Hơn nữa, trình biên dịch thực hiện tiêu chuẩn và đó là những gì tiêu chuẩn nói.
Michael Carman

41
Nó không phải là trục trặc trong trình biên dịch C, nó là một phần của thiết kế. Họ đã thay đổi điều đó đối với C ++, điều mà tôi nghĩ làm cho mọi thứ dễ dàng hơn, nhưng điều đó không có nghĩa là hành vi của C là sai.
Herms

5
Thật không may, nhiều 'lập trình viên' định nghĩa một cấu trúc sau đó gõ nó với một số tên 'không liên quan' (như struct mySturation ...; typedef struct mySturation susan *; một biến / tham số và dẫn dắt sai tất cả mọi người, kể cả người viết mã gốc.
user3629249

1
@ user3629249 Tôi đồng ý với bạn rằng phong cách lập trình được đề cập là khủng khiếp nhưng đây không phải là lý do để chê bai các typedefcấu trúc ing nói chung. Bạn cũng có thể làm typedef struct foo foo;. Tất nhiên structtừ khóa được yêu cầu nhiều hơn sau đó có thể là một gợi ý hữu ích rằng loại bí danh mà chúng ta nhìn vào là bí danh cho một cấu trúc nhưng nói chung nó không tệ. Cũng xem xét một trường hợp trong đó định danh của bí danh loại kết quả typedefchỉ ra rằng đó là bí danh cho một cấu trúc, fe : typedef struct foo foo_struct;.
RobertS hỗ trợ Monica Cellio

38

Một lý do chính đáng khác để luôn luôn đánh máy và kết quả từ vấn đề này:

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

struct StructDef
{
  enum EnuumDef MyEnum;
  unsigned int MyVar;
} MyStruct;

Lưu ý lỗi đánh máy trong EnumDef trong struct (Enu u mDef)? Điều này biên dịch không có lỗi (hoặc cảnh báo) và (tùy theo cách hiểu theo nghĩa đen của Tiêu chuẩn C) đúng. Vấn đề là tôi vừa tạo một định nghĩa liệt kê mới (trống) trong cấu trúc của mình. Tôi không (như dự định) sử dụng định nghĩa EnumDef trước đó.

Với một lỗi đánh máy tương tự, lỗi chính tả sẽ dẫn đến lỗi trình biên dịch khi sử dụng một kiểu không xác định:

typedef 
{
  FIRST_ITEM,
  SECOND_ITEM
} EnumDef;

typedef struct
{
  EnuumDef MyEnum; /* compiler error (unknown type) */
  unsigned int MyVar;
} StructDef;
StrructDef MyStruct; /* compiler error (unknown type) */

Tôi sẽ ủng hộ LUÔN LUÔN đánh máy các sơ đồ và bảng liệt kê.

Không chỉ để lưu một số cách gõ (không có ý định chơi chữ;)), mà bởi vì nó an toàn hơn.


1
Thậm chí tệ hơn, lỗi đánh máy của bạn có thể trùng với một thẻ khác. Trong trường hợp của một cấu trúc, điều này có thể dẫn đến toàn bộ chương trình biên dịch chính xác và có hành vi không xác định thời gian chạy.
MM

3
định nghĩa này: 'typedef {FIRST_ITEM, SECOND_ITEM} EnumDef;' không định nghĩa một enum. Tôi đã viết hàng trăm chương trình lớn và không may mắn thực hiện bảo trì các chương trình mà người khác đã viết. Từ bàn tay cứng của kinh nghiệm, sử dụng typedef trên một cấu trúc chỉ dẫn đến các vấn đề. Hy vọng rằng lập trình viên không bị khuyết tật đến mức họ gặp vấn đề khi nhập một định nghĩa đầy đủ khi họ khai báo một thể hiện cấu trúc. C không phải là Cơ bản, vì vậy việc nhập thêm một số ký tự không gây bất lợi cho hoạt động của chương trình.
dùng3629249

2
Đối với những người cảm thấy khó chịu khi gõ nhiều hơn số ký tự tối thiểu tuyệt đối, tôi có thể đề nghị tham gia một số nhóm cố gắng viết các ứng dụng với số lần nhấn phím tối thiểu. Chỉ cần không sử dụng 'kỹ năng' mới của họ trong môi trường làm việc, đặc biệt là môi trường làm việc thực hiện nghiêm túc các đánh giá ngang hàng
user3629249

Lưu ý, thường không được khuyến khích sử dụng enumnhư một loại, vì nhiều trình biên dịch phát ra các cảnh báo lạ khi sử dụng chúng 'sai'. Ví dụ: khởi tạo enumtừ 0 có thể đưa ra cảnh báo 'hằng số nguyên không trong enum'. Chuyển tiếp khai báo một enumcũng không được phép. Người ta nên sử dụng int(hoặc unsigned int) thay thế.
yyny

5
Ví dụ đó không biên dịch, tôi cũng không mong đợi nó. Biên dịch Debug / test.o test.c: 10: 17: error: trường có loại không đầy đủ 'enum EnuumDef' enum EnuumDef MyEnum; ^ test.c: 10: 8: lưu ý: khai báo chuyển tiếp 'enum EnuumDef' enum EnuumDef MyEnum; ^ 1 lỗi được tạo. gnuc, với std = c99.
natersoz

30

Phong cách mã hóa nhân Linux Chương 5 đưa ra những ưu và nhược điểm lớn (chủ yếu là nhược điểm) của việc sử dụng typedef.

Vui lòng không sử dụng những thứ như "vps_t".

Đó là một sai lầm khi sử dụng typedef cho các cấu trúc và con trỏ. Khi bạn nhìn thấy một

vps_t a;

trong nguồn, nó có nghĩa là gì?

Ngược lại, nếu nó nói

struct virtual_container *a;

bạn thực sự có thể nói "a" là gì.

Nhiều người nghĩ rằng typedefs "giúp đọc". Không phải vậy. Chúng chỉ hữu ích cho:

(a) các đối tượng hoàn toàn mờ đục (trong đó typedef được sử dụng tích cực để che giấu đối tượng là gì).

Ví dụ: "pte_t", v.v. các đối tượng mờ mà bạn chỉ có thể truy cập bằng các hàm truy cập thích hợp.

GHI CHÚ! Bản thân và "chức năng truy cập" không tốt. Lý do chúng tôi có chúng cho những thứ như pte_t, vv là vì thực sự không có thông tin có thể truy cập được ở đó.

(b) Xóa các kiểu số nguyên, trong đó việc trừu tượng hóa giúp tránh nhầm lẫn cho dù đó là "int" hay "long".

u8 / u16 / u32 là typedefs hoàn toàn tốt, mặc dù chúng phù hợp với thể loại (d) tốt hơn ở đây.

GHI CHÚ! Một lần nữa - cần phải có một lý do cho việc này. Nếu một cái gì đó là "không dấu dài", thì không có lý do gì để làm

typedef unsigned long myflags_t;

nhưng nếu có một lý do rõ ràng về lý do tại sao nó trong một số trường hợp nhất định có thể là "int unsign" và trong các cấu hình khác có thể là "không dấu dài", thì bằng mọi cách hãy tiếp tục và sử dụng một typedef.

(c) khi bạn sử dụng thưa thớt để tạo ra một kiểu mới để kiểm tra kiểu.

(d) Các loại mới giống hệt với các loại C99 tiêu chuẩn, trong một số trường hợp đặc biệt.

Mặc dù sẽ chỉ mất một khoảng thời gian ngắn để mắt và não làm quen với các loại tiêu chuẩn như 'uint32_t', một số người vẫn phản đối việc sử dụng chúng.

Do đó, các loại 'u8 / u16 / u32 / u64' dành riêng cho Linux và các loại tương đương đã ký giống với các loại tiêu chuẩn được cho phép - mặc dù chúng không bắt buộc trong mã mới của riêng bạn.

Khi chỉnh sửa mã hiện có đã sử dụng một hoặc một nhóm loại khác, bạn nên tuân thủ các lựa chọn hiện có trong mã đó.

(e) Các loại an toàn để sử dụng trong không gian người dùng.

Trong một số cấu trúc nhất định có thể nhìn thấy đối với không gian người dùng, chúng tôi không thể yêu cầu các loại C99 và không thể sử dụng biểu mẫu 'u32' ở trên. Do đó, chúng tôi sử dụng __u32 và các loại tương tự trong tất cả các cấu trúc được chia sẻ với không gian người dùng.

Có thể có những trường hợp khác nữa, nhưng về cơ bản, quy tắc nên KHÔNG BAO GIỜ sử dụng typedef trừ khi bạn có thể khớp rõ ràng với một trong những quy tắc đó.

Nói chung, một con trỏ hoặc một cấu trúc có các phần tử có thể được truy cập trực tiếp một cách hợp lý không bao giờ nên là một typedef.


4
"Bản thân và" chức năng truy cập "không tốt trong bản thân họ". Ai đó có thể giải thích tại sao? Tôi nghĩ rằng việc che giấu thông tin và đóng gói sẽ là một ý tưởng rất tốt.
Yawar

5
@Yawar Tôi mới đọc tài liệu này và có cùng suy nghĩ. Chắc chắn, C không phải là đối tượng định hướng, nhưng trừu tượng vẫn là một điều.
Baldrickk

12

Nó chỉ ra rằng có những ưu và nhược điểm. Một nguồn thông tin hữu ích là cuốn sách bán kết "Chuyên gia lập trình C" ( Chương 3 ). Tóm lại, trong C bạn có nhiều không gian tên: thẻ, loại, tên thành viên và mã định danh . typedefgiới thiệu một bí danh cho một loại và định vị nó trong không gian tên thẻ. Cụ thể là

typedef struct Tag{
...members...
}Type;

định nghĩa hai điều. Một thẻ trong không gian tên thẻ và một Loại trong không gian tên loại. Vì vậy, bạn có thể làm cả hai Type myTypestruct Tag myTagType. Tuyên bố như struct Type myTypehoặc Tag myTagTypelà bất hợp pháp. Ngoài ra, trong một tuyên bố như thế này:

typedef Type *Type_ptr;

chúng tôi xác định một con trỏ đến Loại của chúng tôi. Vì vậy, nếu chúng tôi tuyên bố:

Type_ptr var1, var2;
struct Tag *myTagType1, myTagType2;

sau đó var1, var2myTagType1là con trỏ tới Loại nhưng myTagType2không.

Trong cuốn sách được đề cập ở trên, nó đề cập rằng các cấu trúc typedefing không hữu ích lắm vì nó chỉ cứu lập trình viên khỏi việc viết từ struct. Tuy nhiên, tôi có một sự phản đối, giống như nhiều lập trình viên C khác. Mặc dù đôi khi nó chuyển sang làm xáo trộn một số tên (đó là lý do tại sao không nên sử dụng các cơ sở mã lớn như kernel) khi bạn muốn triển khai đa hình trong C, nó giúp tìm hiểu chi tiết rất nhiều ở đây . Thí dụ:

typedef struct MyWriter_t{
    MyPipe super;
    MyQueue relative;
    uint32_t flags;
...
}MyWriter;

bạn có thể làm:

void my_writer_func(MyPipe *s)
{
    MyWriter *self = (MyWriter *) s;
    uint32_t myFlags = self->flags;
...
}

Vì vậy, bạn có thể truy cập một thành viên bên ngoài ( flags) bằng struct bên trong ( MyPipe) thông qua việc truyền. Đối với tôi, việc sử dụng toàn bộ loại sẽ ít gây nhầm lẫn hơn là thực hiện (struct MyWriter_ *) s;mỗi khi bạn muốn thực hiện chức năng đó. Trong những trường hợp này, tham chiếu ngắn là một vấn đề lớn đặc biệt nếu bạn sử dụng nhiều kỹ thuật trong mã của mình.

Cuối cùng, khía cạnh cuối cùng với typedefcác loại ed là không có khả năng mở rộng chúng, trái ngược với các macro. Nếu ví dụ, bạn có:

#define X char[10] or
typedef char Y[10]

sau đó bạn có thể tuyên bố

unsigned X x; but not
unsigned Y y;

Chúng tôi không thực sự quan tâm đến điều này cho các cấu trúc vì nó không áp dụng cho các chỉ định lưu trữ ( volatileconst).


1
MyPipe *s; MyWriter *self = (MyWriter *) s;và bạn vừa phá vỡ răng cưa nghiêm ngặt.
Jonathon Reinhart

@JonathonReinhart Sẽ thật minh họa khi đề cập đến cách tránh điều này, ví dụ như cách GTK + cực kỳ vui vẻ hoạt động xung quanh nó: bugzilla.gnome.org/show_orms.cgi?id=140722 / mail.gnome.org/archives -devel-list / 2004-April / dir00196.html
underscore_d

"typedef giới thiệu một bí danh cho một loại và định vị nó trong không gian tên thẻ. Cụ thể là" typedef struct Tag{ ...members... }Type; "định nghĩa hai điều" không hoàn toàn có ý nghĩa. nếu typedef định nghĩa các thẻ thì 'Loại' ở đây cũng phải là một thẻ. Sự thật là định nghĩa định nghĩa 2 thẻ và 1 loại (hoặc 2 loại và 1 thẻ không chắc chắn.): struct Tag, TagType. struct Tagchắc chắn là một loại Taglà một thẻ. nhưng sự nhầm lẫn là dù Typelà thẻ hay loại
Qwertyzw

12

Tôi không nghĩ rằng tuyên bố về phía trước thậm chí có thể với typedef. Việc sử dụng struct, enum và union cho phép chuyển tiếp các khai báo khi các phụ thuộc (biết về) là hai chiều.

Phong cách: Sử dụng typedef trong C ++ có ý nghĩa khá lớn. Nó gần như có thể cần thiết khi xử lý các mẫu yêu cầu nhiều tham số và / hoặc biến. Typedef giúp giữ cho việc đặt tên thẳng.

Không phải như vậy trong ngôn ngữ lập trình C. Việc sử dụng typedef thường không phục vụ mục đích nào ngoài việc làm xáo trộn việc sử dụng cấu trúc dữ liệu. Vì chỉ có {struct (6), enum (4), union (5)} số lần nhấn phím được sử dụng để khai báo một kiểu dữ liệu nên hầu như không sử dụng cho bí danh của struct. Là kiểu dữ liệu đó là một liên minh hoặc một cấu trúc? Sử dụng khai báo không đánh máy đơn giản cho phép bạn biết ngay đó là loại gì.

Lưu ý cách Linux được viết với sự tránh nghiêm ngặt của typedef vô nghĩa này mang lại. Kết quả là một phong cách tối giản và sạch sẽ.


10
Sạch sẽ không lặp lại structở mọi nơi ... Typedef tạo ra các loại mới. Bạn dùng gì? Các loại. Chúng tôi không quan tâm nếu đó là một cấu trúc, liên minh hoặc enum, đó là lý do tại sao chúng tôi đánh máy nó.
GManNickG

13
Không, chúng ta quan tâm nếu đó là một struct hay nghiệp đoàn, so với một enum hoặc một số loại nguyên tử. Bạn không thể ép buộc một cấu trúc thành một số nguyên hoặc một con trỏ (hoặc bất kỳ loại nào khác, đối với vấn đề đó), đó là tất cả những gì bạn đôi khi phải lưu trữ một số ngữ cảnh. Có các từ khóa 'struct' hoặc 'union' xung quanh giúp cải thiện khả năng suy luận. Không ai nói bạn cần biết những gì bên trong cấu trúc.
Bernd Jendrissek

1
@BerndJendrissek: Cấu trúc và công đoàn khác với các loại khác, nhưng mã khách hàng có nên quan tâm đến điều gì trong hai điều đó (struct hoặc union) giống như một FILE?
supercat

4
@supercat FILE là một cách sử dụng tốt typedef. Tôi nghĩ rằng typedef bị lạm dụng quá mức , không phải đó là sự không phù hợp với ngôn ngữ. IMHO sử dụng typedef cho mọi thứ là mùi mã "quá mức đầu cơ". Lưu ý rằng bạn khai báo các biến là FILE * foo, không bao giờ là FILE foo. Đối với tôi, vấn đề này.
Bernd Jendrissek

2
@supercat: "Nếu các biến nhận dạng tệp là loại FILE chứ không phải FILE * ..." Nhưng đó chính xác là sự mơ hồ mà typedefs cho phép! Chúng ta chỉ quen với việc sử dụng TẬP_TIN * vì vậy chúng tôi không băn khoăn về điều đó, nhưng mỗi lần bạn thêm typedef bạn sẽ giới thiệu thêm một chút chi phí nhận thức: API này muốn foo_t args hay foo_t *? Hoàn toàn mang theo 'struct' cùng cải thiện tính cục bộ của lý luận, nếu phải trả thêm một vài ký tự cho mỗi định nghĩa hàm.
Bernd Jendrissek

4

Hãy bắt đầu với những điều cơ bản và tiến lên.

Dưới đây là một ví dụ về định nghĩa cấu trúc:

struct point
  {
    int x, y;
  };

Ở đây tên pointlà tùy chọn.

Một cấu trúc có thể được khai báo trong định nghĩa của nó hoặc sau đó.

Khai báo trong khi định nghĩa

struct point
  {
    int x, y;
  } first_point, second_point;

Tuyên bố sau khi định nghĩa

struct point
  {
    int x, y;
  };
struct point first_point, second_point;

Bây giờ, cẩn thận lưu ý trường hợp cuối cùng ở trên; bạn cần phải viết struct pointđể khai báo Cấu trúc của loại đó nếu bạn quyết định tạo loại đó tại một điểm sau trong mã của bạn.

Nhập typedef. Nếu bạn dự định tạo Cấu trúc mới (Cấu trúc là kiểu dữ liệu tùy chỉnh) sau này trong chương trình của bạn bằng cách sử dụng cùng một kế hoạch chi tiết, sử dụng typedeftrong định nghĩa của nó có thể là một ý tưởng tốt vì bạn có thể lưu một số thao tác gõ tiến lên.

typedef struct point
  {
    int x, y;
  } Points;

Points first_point, second_point;

Một lời cảnh báo trong khi đặt tên loại tùy chỉnh của bạn

Không có gì ngăn bạn sử dụng hậu tố _t ở cuối tên loại tùy chỉnh của bạn nhưng tiêu chuẩn POSIX bảo lưu việc sử dụng hậu tố _t để biểu thị tên loại thư viện chuẩn.


3

Tên bạn (tùy chọn) cung cấp cho struct được gọi là tên thẻ và, như đã được lưu ý, bản thân nó không phải là một loại. Để có được loại yêu cầu tiền tố struct.

Bỏ GTK + sang một bên, tôi không chắc tên thẻ được sử dụng bất cứ thứ gì thông thường như kiểu typedef cho kiểu cấu trúc, vì vậy, trong C ++ được nhận dạng và bạn cũng có thể bỏ qua từ khóa struct và sử dụng tên thẻ làm tên loại:


    struct MyStruct
    {
      int i;
    };

    // The following is legal in C++:
    MyStruct obj;
    obj.i = 7;


1

typedef sẽ không cung cấp một tập hợp các cấu trúc dữ liệu. Điều này bạn không thể làm với typdef:

struct bar;
struct foo;

struct foo {
    struct bar *b;
};

struct bar {
    struct foo *f;
};

Tất nhiên bạn luôn có thể thêm:

typedef struct foo foo_t;
typedef struct bar bar_t;

Chính xác thì điểm đó là gì?


1

A> một typdef hỗ trợ ý nghĩa và tài liệu của một chương trình bằng cách cho phép tạo ra các từ đồng nghĩa có ý nghĩa hơn cho các loại dữ liệu . Ngoài ra, chúng giúp tham số hóa một chương trình chống lại các vấn đề về tính di động (K & R, pg147, C prog lang).

B> một cấu trúc xác định một loại . Structs cho phép nhóm một vars thuận tiện để xử lý thuận tiện (K & R, pg127, C prog lang.) Như một đơn vị

C> typedef'ing một cấu trúc được giải thích trong A ở trên.

D> Đối với tôi, cấu trúc là các loại tùy chỉnh hoặc bộ chứa hoặc bộ sưu tập hoặc không gian tên hoặc loại phức tạp, trong khi một typdef chỉ là một phương tiện để tạo ra nhiều biệt danh.


0

Hóa ra trong C99 typedef là bắt buộc. Nó đã lỗi thời, nhưng rất nhiều công cụ (ala HackRank) sử dụng c99 làm triển khai C thuần túy. Và typedef là bắt buộc ở đó.

Tôi không nói rằng họ nên thay đổi (có thể có hai tùy chọn C) nếu yêu cầu thay đổi, những người trong chúng ta đang học để phỏng vấn trên trang web sẽ là SOL.


2
"Hóa ra trong C99 typedeflà bắt buộc." Ý anh là gì?
Julien Lopez

Câu hỏi là về C, không phải C++. Trong Ctypedefs là 'bắt buộc' (và rất có thể sẽ luôn luôn như vậy). 'Bắt buộc' như trong, bạn sẽ không thể khai báo một biến Point varName;và có loại đồng nghĩa với struct Point;không có a typedef struct Point Point;.
yyny

0

Trong ngôn ngữ lập trình 'C', từ khóa 'typedef' được sử dụng để khai báo tên mới cho một số đối tượng (kiểu struct, mảng, hàm..enum). Ví dụ: tôi sẽ sử dụng 'struct-s'. Trong 'C', chúng ta thường khai báo một 'struct' bên ngoài chức năng 'chính'. Ví dụ:

struct complex{ int real_part, img_part }COMPLEX;

main(){

 struct KOMPLEKS number; // number type is now a struct type
 number.real_part = 3;
 number.img_part = -1;
 printf("Number: %d.%d i \n",number.real_part, number.img_part);

}

Mỗi lần tôi quyết định sử dụng loại cấu trúc, tôi sẽ cần từ khóa này 'struct' cái gì đó '' tên '.' Typedef 'sẽ chỉ đổi tên loại đó và tôi có thể sử dụng tên mới đó trong chương trình của mình mỗi khi tôi muốn. Vì vậy, mã của chúng tôi sẽ là:

typedef struct complex{int real_part, img_part; }COMPLEX;
//now COMPLEX is the new name for this structure and if I want to use it without
// a keyword like in the first example 'struct complex number'.

main(){

COMPLEX number; // number is now the same type as in the first example
number.real_part = 1;
number.img)part = 5;
printf("%d %d \n", number.real_part, number.img_part);

}

Nếu bạn có một số đối tượng cục bộ (struct, mảng, có giá trị) sẽ được sử dụng trong toàn bộ chương trình của bạn, bạn có thể chỉ cần đặt tên cho nó bằng cách sử dụng 'typedef'.


-2

Nói chung, trong ngôn ngữ C, struct / union / enum là lệnh macro được xử lý bởi bộ tiền xử lý ngôn ngữ C (đừng nhầm với bộ tiền xử lý xử lý "#include" và khác)

vì thế :

struct a
{
   int i;
};

struct b
{
   struct a;
   int i;
   int j;
};

struct b được sử dụng như một cái gì đó như thế này:

struct b
{
    struct a
    {
        int i;
    };
    int i;
    int j;
}

và vì vậy, tại thời điểm biên dịch, nó phát triển trên stack như một thứ gì đó như: b: int ai int i int j

đó cũng là lý do tại sao nó có các cấu trúc tự phục hồi, vòng tiền xử lý C trong một vòng lặp không thể kết thúc mà không thể kết thúc.

typedef là trình xác định kiểu, có nghĩa là chỉ trình biên dịch C xử lý nó và nó có thể làm như anh ta muốn để tối ưu hóa việc thực thi mã trình biên dịch. Nó cũng không tiêu tốn thành viên loại par một cách ngu ngốc như préprocessor làm với các cấu trúc nhưng sử dụng thuật toán xây dựng tham chiếu phức tạp hơn, do đó, việc xây dựng như sau:

typedef struct a A; //anticipated declaration for member declaration

typedef struct a //Implemented declaration
{
    A* b; // member declaration
}A;

được cho phép và đầy đủ chức năng. Việc triển khai này cũng cung cấp quyền truy cập vào chuyển đổi loại trình biên dịch và loại bỏ một số hiệu ứng lỗi khi luồng thực thi rời khỏi trường ứng dụng của các hàm khởi tạo.

Điều này có nghĩa là trong C typedefs gần lớp C ++ hơn là các cấu trúc cô đơn.


3
Không khó để có các cấu trúc tự tham chiếu. struct foo {struct foo * tiếp theo; điều int; }
Bernd Jendrissek

4
...gì? Nói tiền xử lý để mô tả độ phân giải structstypedefs là đủ tệ, nhưng phần còn lại của văn bản của bạn rất khó hiểu đến nỗi tôi thấy khó nhận được bất kỳ thông điệp nào từ nó. Một điều tôi có thể nói, mặc dù, đó là ý tưởng của bạn rằng một phi typedefd structkhông thể được chuyển tiếp-tuyên bố hoặc sử dụng như một thành viên đục (con trỏ) là hoàn toàn sai sự thật. Trong ví dụ đầu tiên của bạn, struct bcó thể chứa một cách tầm thường một struct a *, không typedefbắt buộc. Các xác nhận structchỉ đơn thuần là những phần mở rộng vĩ mô, và điều đó typedefmang lại cho chúng sức mạnh mới mang tính cách mạng, là không chính xác
underscore_d
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.