Định nghĩa cấu trúc nên đi trong tệp .h hoặc .c?


102

Tôi đã xem cả định nghĩa đầy đủ của structs trong tiêu đề và chỉ khai báo — phương thức này có lợi thế nào hơn phương thức kia không?

Nếu nó tạo ra sự khác biệt, tôi thường gõ một cấu trúc như vậy trong .h

typedef struct s s_t;

Biên tập

Nói rõ hơn, các tùy chọn là khai báo trong tệp tiêu đề và định nghĩa trong lớp, hoặc cả khai báo và định nghĩa trong tệp tiêu đề. Cả hai sẽ dẫn đến khả năng sử dụng giống nhau, ngay cả khi một là do liên kết, phải không?


Tôi thấy nhiều bản sao gần như trùng lặp, ví dụ ở đây nhưng không có kết quả phù hợp chính xác. Vui lòng sửa cho tôi nếu tôi sai trong vấn đề này.


2
Bạn muốn cấu trúc không trong suốt hoặc không mờ đục?

4
Lưu ý, số nhận dạng với _tđược đặt trước bởi POSIX, vì vậy đây thường là một ý tưởng tồi. Bạn chỉ có thể làm typedef struct toto toto.
Jens Gustedt

Tôi đã thấy rất nhiều nơi _tsử dụng khác (ví dụ: lighttp, linux) ... và tôi đặt tiền tố cho mọi thứ bằng projident_ vì vậy, đó không phải là một vấn đề phải không?
Aaron Yodaiken

Và @WTP, tôi nghĩ rằng không rõ ràng thường được coi là tốt hơn và Cish hơn , không (những gì với FILEví dụ, v.v.). Vì vậy, không mờ đục.
Aaron Yodaiken

Nếu đó là một cấu trúc không mờ, nó phải nằm trong tệp tiêu đề hoặc mã của bạn không KHÔ (đừng lặp lại chính bạn).

Câu trả lời:


107

Các cấu trúc riêng cho tệp đó phải đi trong tệp .c, với khai báo trong tệp .h nếu chúng được sử dụng bởi bất kỳ hàm nào trong .h.

Các cấu trúc công khai sẽ nằm trong tệp .h.


4
Tôi nghĩ rằng tôi đồng ý với câu trả lời này nhiều hơn. Không phải là sử dụng cấu trúc thông qua bất kỳ tệp .c nào khác hay không, mà là liệu cấu trúc có được coi là công khai (và do đó, có thể truy cập) hay không.
c00kiemon5ter

@ τεκ Ý bạn là globallocalkhả năng hiển thị? publickhông có ý nghĩa trong một cấu trúc. Tất cả các cấu trúc đều được công khai theo mặc định.
BugShotGG

3
@Geo Papas Đây là câu hỏi về C. publickhông phải là từ khóa trong C. Nếu bạn xem câu trả lời của Matthew Slattery bên dưới, bạn có thể thấy cách chỉ sử dụng khai báo chuyển tiếp trong tiêu đề gây ra lỗi trình biên dịch khi người dùng cố gắng sử dụng các thành viên của struct riêng tư (không rõ ràng).
τεκ

68

Cả hai sẽ dẫn đến khả năng sử dụng giống nhau, ngay cả khi một là do liên kết, phải không?

Không, không phải khi bạn xem xét các tệp .c khác bao gồm cùng một tiêu đề. Nếu định nghĩa của cấu trúc không hiển thị cho trình biên dịch, thì không thể sử dụng chi tiết của định nghĩa đó. Một khai báo không có định nghĩa (ví dụ: chỉ struct s;) khiến trình biên dịch không thành công nếu có bất kỳ thứ gì cố gắng tìm kiếm bên trong struct s, trong khi vẫn cho phép nó biên dịch, ví dụ struct s *foo;như foosau này không được tham chiếu đến).

So sánh các phiên bản này của api.hapi.c:

Definition in header:                 Definition in implementation:
+---------------------------------+   +---------------------------------+
| struct s {                      |   | struct s;                       |
|     int internal;               |   |                                 |
|     int other_stuff;            |   | extern void                     |
| };                              |   | api_func(struct s *foo, int x); |
|                                 |   +---------------------------------+
| extern void                     |   +---------------------------------+
| api_func(struct s *foo, int x); |   | #include "api.h"                |
+---------------------------------+   |                                 |
+---------------------------------+   | struct s {                      |
| #include "api.h"                |   |     int internal;               |
|                                 |   |     int other_stuff;            |
| void                            |   | };                              |
| api_func(struct s *foo, int x)  |   |                                 |
| {                               |   | void                            |
|     foo->internal = x;          |   | api_func(struct s *foo, int x)  |
| }                               |   | {                               |
+---------------------------------+   |     foo->internal = x;          |
                                      | }                               |
                                      +---------------------------------+

Ứng dụng khách này của API hoạt động với một trong hai phiên bản:

#include "api.h"

void good(struct s *foo)
{
    api_func(foo, 123);
}

Cái này xoay quanh chi tiết triển khai:

#include "api.h"

void bad(struct s *foo)
{
    foo->internal = 123;
}

sẽ hoạt động với phiên bản "định nghĩa trong tiêu đề", nhưng không hoạt động với phiên bản "định nghĩa trong triển khai", như trong trường hợp sau, trình biên dịch không hiển thị được bố cục của cấu trúc:

$ gcc -Wall -c bad.c
bad.c: In function 'bad':
bad.c:5: error: dereferencing pointer to incomplete type
$

Vì vậy, phiên bản "định nghĩa trong triển khai" bảo vệ chống lại việc sử dụng sai một cách vô tình hoặc cố ý các chi tiết triển khai riêng tư.


3
chỉ muốn biết bạn đã tạo các cửa sổ mã đó như thế nào và vẫn có mã được đánh dấu bên trong chúng ... theo cách thủ công? OP này dường như đã rời khỏi bằng cách sử dụng stackoverflow: '(Ai khác có thể cho tôi biết ....
Mahesha999

Ví dụ đẹp! Cảm ơn!
Victor Haine

Cảm ơn bạn vì ví dụ như vậy! dereferencing pointer to incomplete typechính xác là trường hợp của tôi!
Timur Fayzrakhmanov

Tôi chỉ muốn nói thêm rằng không phải tất cả công khai cấu trúc truy cập là xấu: bạn có thể ví dụ muốn cho phép người dùng của API của bạn để điền vào dữ liệu và gửi nó trong.
Alexander Torstling

@ Mahesha999, không có phép thuật nào ở đó cả. SO làm nổi bật mã ngay cả khi bạn bỏ rác vào đó. Lưu ý rằng nó đang cố gắng làm nổi bật đầu ra dòng lệnh sau này trong bài đăng.
Winger Sendon

8

Nếu cấu trúc được sử dụng bởi các đơn vị biên dịch khác (tệp .c), hãy đặt nó vào tệp tiêu đề để bạn có thể đưa tệp tiêu đề đó vào bất cứ nơi nào cần.

Nếu cấu trúc chỉ được sử dụng trong một đơn vị biên dịch (tệp .c), bạn đặt nó vào tệp .c đó.


3

Vấn đề là, việc đặt nó trong tệp tiêu đề cho phép bạn sử dụng cấu trúc (hoặc bất kỳ định nghĩa nào khác) từ nhiều tệp nguồn, chỉ bằng cách bao gồm tệp tiêu đề đó.

Nhưng nếu bạn chắc chắn rằng nó sẽ chỉ được sử dụng từ một tệp nguồn, thì nó thực sự không tạo ra bất kỳ sự khác biệt nào.



-4

Nói chung, tôi không nghĩ rằng nó tạo ra sự khác biệt lớn cho dù bạn đặt chúng trong tiêu đề hay tệp nguồn. Tuy nhiên, nếu bạn cần truy cập các thành viên của một cấu trúc từ nhiều tệp nguồn, thì việc đặt cấu trúc vào tệp tiêu đề và bao gồm nó từ bất kỳ tệp nào khác mà cấu trúc cần thiết sẽ dễ dàng hơn.


8
-1: nếu bạn quan tâm đến kỹ thuật phần mềm tốt (tính trừu tượng, tính mô-đun, v.v.) thì nó thực sự quan trọng khi bạn đặt định nghĩa cấu trúc ở đâu
Paul R
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.