Bao gồm một tệp nguồn C trong một tệp khác?


111

Có OK (hoặc thậm chí được khuyến nghị / thực hành tốt) với #includemột .ctệp trong .ctệp khác không?


4
Tôi đã tự do chỉnh sửa câu hỏi và cũng áp dụng một số thẻ hữu ích. Một số thẻ của tôi có thể cho thấy sự thiên vị, heh.
thư giãn

Câu trả lời:


113

Được sử dụng đúng cách, đây có thể là một kỹ thuật hữu ích.

Giả sử bạn có một hệ thống con quan trọng về hiệu suất, phức tạp với giao diện công khai khá nhỏ và rất nhiều mã triển khai không thể tái sử dụng. Mã chạy tới vài nghìn dòng, hàng trăm chức năng riêng tư và khá nhiều dữ liệu riêng tư. Nếu bạn làm việc với các hệ thống nhúng không tầm thường, bạn có thể đối phó với tình huống này đủ thường xuyên.

Giải pháp của bạn có thể sẽ được phân lớp, mô-đun và tách rời và những khía cạnh này có thể được biểu diễn và củng cố hữu ích bằng cách mã hóa các phần khác nhau của hệ thống con trong các tệp khác nhau.

Với C, bạn có thể mất rất nhiều khi làm điều này. Hầu như tất cả các toolchains đều cung cấp khả năng tối ưu hóa tốt cho một đơn vị biên dịch duy nhất, nhưng rất bi quan về bất kỳ thứ gì được tuyên bố bên ngoài.

Nếu bạn đặt mọi thứ vào một mô-đun nguồn C, bạn sẽ nhận được -

  • Cải thiện hiệu suất và kích thước mã - các lệnh gọi hàm sẽ được nội tuyến trong nhiều trường hợp. Ngay cả khi không có nội tuyến, trình biên dịch vẫn có cơ hội tạo ra mã hiệu quả hơn.

  • Dữ liệu mức liên kết & ẩn chức năng.

  • Tránh ô nhiễm không gian tên và hệ quả của nó - bạn có thể sử dụng các tên ít khó sử dụng hơn.

  • Biên dịch và liên kết nhanh hơn.

Nhưng bạn cũng gặp phải một mớ hỗn độn đáng sợ khi chỉnh sửa tệp này và bạn mất đi tính mô đun ngụ ý. Điều này có thể được khắc phục bằng cách chia nguồn thành nhiều tệp và bao gồm các tệp này để tạo ra một đơn vị biên dịch duy nhất.

Tuy nhiên, bạn cần phải áp đặt một số quy ước để quản lý việc này một cách hợp lý. Những điều này sẽ phụ thuộc vào chuỗi công cụ của bạn ở một mức độ nào đó, nhưng một số gợi ý chung là:

  • Đặt giao diện công khai trong một tệp tiêu đề riêng biệt - bạn vẫn nên làm điều này.

  • Có một tệp .c chính bao gồm tất cả các tệp .c phụ. Điều này cũng có thể bao gồm mã cho giao diện công cộng.

  • Sử dụng trình bảo vệ trình biên dịch để đảm bảo rằng các tiêu đề riêng và mô-đun nguồn không được các đơn vị biên dịch bên ngoài đưa vào.

  • Tất cả dữ liệu và chức năng riêng tư nên được khai báo tĩnh.

  • Duy trì sự khác biệt về khái niệm giữa các tệp .c và .h. Điều này thúc đẩy các quy ước hiện có. Sự khác biệt là bạn sẽ có rất nhiều khai báo tĩnh trong tiêu đề của mình.

  • Nếu chuỗi công cụ của bạn không áp đặt bất kỳ lý do gì, hãy đặt tên cho các tệp triển khai riêng tư là .c và .h. Nếu bạn sử dụng các bảo vệ bao gồm, chúng sẽ không tạo ra mã và không giới thiệu tên mới (bạn có thể kết thúc với một số phân đoạn trống trong quá trình liên kết). Ưu điểm rất lớn là các công cụ khác (ví dụ như IDE) sẽ xử lý các tệp này một cách thích hợp.


1
+1 điều này vẫn là thực tế, trong khi các trình biên dịch tốt hơn sẽ làm cho phương pháp này lỗi thời theo thời gian. GCC 4.5 với tối ưu hóa thời gian liên kết là một bước tiến lớn trên đường.
u0b34a0f6ae

Tôi đã thấy rất nhiều chương trình làm điều này và nó khiến tôi lo lắng khi tôi cố gắng sử dụng lại mã của chúng. Cảm ơn bạn đã giải thích tại sao họ làm điều đó và đề xuất sử dụng người bảo vệ (điều này thường không được thực hiện).
Joey Adams

Trong quá trình phát triển nhúng cho C51, việc sử dụng các tệp C extern & nhiều không gây ra vấn đề gì ngoài việc đau đầu. Tôi đang chuyển sang có một tệp C bao gồm tất cả các tệp khác để lấy lại thời gian của tôi.
mahesh

Đối với hồ sơ: GCC hỗ trợ tối ưu hóa trên các đơn vị dịch khác nhau, hãy xem luồng SO này và phần hướng dẫn sử dụng GCC về tối ưu hóa thời gian liên kết .
Twonky

60

Là nó ổn? vâng, nó sẽ biên dịch

nó được khuyến khích? không - tệp .c biên dịch thành tệp .obj, được liên kết với nhau sau khi biên dịch (bởi trình liên kết) thành tệp thực thi (hoặc thư viện), vì vậy không cần bao gồm tệp .c này vào tệp khác. Thay vào đó, điều bạn có thể muốn làm là tạo tệp .h liệt kê các hàm / biến có sẵn trong tệp .c khác và bao gồm tệp .h


14
Cũng cần lưu ý rằng ngay cả khi nó biên dịch, nó có thể không liên kết nếu tệp #included .c cũng được biên dịch và hai tệp đối tượng được liên kết với nhau - bạn có thể kết thúc bằng nhân các ký hiệu đã xác định.
Nick Meyer

1
Một câu hỏi nhỏ. Tôi có tệp tiêu đề khai báo phương thức struct + và tệp .c tương ứng để xác định chúng. Nếu phương thức đó lấy struct làm tham số, làm cách nào để tránh đưa tệp .c vào tệp .c khác nơi phương thức chính được xác định?
stdout

12

Không.

Tùy thuộc vào môi trường xây dựng của bạn (bạn không chỉ định), bạn có thể thấy rằng nó hoạt động chính xác theo cách bạn muốn.

Tuy nhiên, có nhiều môi trường (cả IDE và rất nhiều Makefiles được làm thủ công) mong muốn biên dịch * .c - nếu điều đó xảy ra, bạn có thể sẽ gặp phải lỗi trình liên kết do các ký hiệu trùng lặp.

Theo quy định, thực hành này nên tránh.

Nếu bạn hoàn toàn phải #include source (và nói chung là nên tránh), hãy sử dụng một hậu tố tệp khác cho tệp.


9

Tôi nghĩ rằng tôi sẽ chia sẻ một tình huống mà nhóm của tôi quyết định bao gồm các tệp .c. Hình ảnh của chúng tôi chủ yếu bao gồm các mô-đun được phân tách thông qua một hệ thống tin nhắn. Các trình xử lý thông báo này là công khai và gọi nhiều hàm static worker cục bộ để thực hiện công việc của chúng. Vấn đề đã xảy ra khi cố gắng đưa vào các trường hợp thử nghiệm đơn vị của chúng tôi, vì cách duy nhất để thực hiện mã triển khai riêng tư này là gián tiếp thông qua giao diện thông báo công khai. Với một số chức năng của công nhân nằm sâu trong ngăn xếp, điều này hóa ra là một cơn ác mộng để đạt được độ bao phủ thích hợp.

Bao gồm các tệp .c đã cho chúng tôi một cách để tiếp cận bánh răng trong chiếc máy mà chúng tôi rất thú vị khi thử nghiệm.


6

Bạn có thể sử dụng trình biên dịch gcc trong linux để liên kết hai tệp c trong một đầu ra. Giả sử bạn có hai tệp c, một là 'main.c' và một là 'support.c'. Vì vậy, lệnh để liên kết hai điều này là

gcc main.c support.c -o main.out

Bằng cách này, hai tệp sẽ được liên kết với một đầu ra main.out Để chạy đầu ra, lệnh sẽ

./main.out

Nếu bạn đang sử dụng hàm trong main.c được khai báo trong tệp support.c thì bạn nên khai báo nó trong main cũng bằng cách sử dụng lớp lưu trữ bên ngoài.


5

Phần mở rộng của tệp không quan trọng đối với hầu hết các trình biên dịch C, vì vậy nó sẽ hoạt động.

Tuy nhiên, tùy thuộc vào cài đặt makefile hoặc dự án của bạn, tệp c đi kèm có thể tạo một tệp đối tượng riêng biệt. Khi liên kết có thể dẫn đến các ký hiệu được xác định kép.


3

Bạn có thể bao gồm đúng cách các tệp .C hoặc .CPP vào các tệp nguồn khác. Tùy thuộc vào IDE của bạn, bạn thường có thể ngăn liên kết kép bằng cách xem các thuộc tính tệp nguồn mà bạn muốn đưa vào, thường bằng cách nhấp chuột phải vào nó và nhấp vào thuộc tính, và bỏ chọn / kiểm tra biên dịch / liên kết / loại trừ khỏi bản dựng hoặc bất kỳ tùy chọn nào. có lẽ. Hoặc bạn không thể bao gồm tệp trong chính dự án, do đó IDE thậm chí sẽ không biết nó tồn tại và sẽ không cố gắng biên dịch nó. Và với các tệp makefiles, bạn chỉ đơn giản là sẽ không đưa tệp vào đó để biên dịch và liên kết.

CHỈNH SỬA: Xin lỗi, tôi đã biến nó thành một câu trả lời thay vì một câu trả lời cho các câu trả lời khác :(


1

Ngôn ngữ C không cấm loại #include đó, nhưng đơn vị dịch kết quả vẫn phải là C.

Tôi không biết bạn đang sử dụng chương trình nào với tệp .prj. Nếu bạn đang sử dụng một cái gì đó như "make" hoặc Visual Studio hoặc bất cứ thứ gì, chỉ cần đảm bảo rằng bạn đặt danh sách tệp được biên dịch mà không có tệp không thể biên dịch độc lập.


0

Bao gồm tệp C vào một tệp khác là hợp pháp, nhưng điều không nên làm, trừ khi bạn biết chính xác tại sao bạn làm điều này và bạn đang cố gắng đạt được điều gì.
Tôi gần như chắc chắn rằng nếu bạn đăng ở đây lý do đằng sau câu hỏi của bạn, cộng đồng sẽ tìm cho bạn một cách khác thích hợp hơn để đạt được mục tiêu của bạn (vui lòng lưu ý "hầu như", vì có thể đây là giải pháp cho bối cảnh ).

Nhân tiện, tôi đã bỏ lỡ phần thứ hai của câu hỏi. Nếu tệp C được đưa vào tệp khác và đồng thời được đưa vào dự án, bạn có thể sẽ gặp phải vấn đề trùng lặp biểu tượng tại sao lại liên kết các đối tượng, tức là cùng một hàm sẽ được xác định hai lần (trừ khi tất cả chúng đều tĩnh).


-6

bạn nên thêm tiêu đề như thế này

#include <another.c>

lưu ý: cả hai tệp phải được đặt ở cùng một nơi

tôi sử dụng cái này trong codevison AVR cho bộ điều khiển vi mô ATMEGA và hoạt động thực sự nhưng nó không hoạt động trong tệp ngôn ngữ C bình thường

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.