Là tốt hơn để sử dụng chỉ thị tiền xử lý hoặc câu lệnh if (hằng)?


10

Giả sử chúng ta có một cơ sở mã được sử dụng cho nhiều người tiêu dùng khác nhau và chúng ta có một số mã trong đó chỉ phù hợp với người tiêu dùng loại X. Tốt hơn là sử dụng các chỉ thị tiền xử lý để chỉ bao gồm mã này trong chi phí loại X, hoặc để sử dụng nếu báo cáo? Để rõ ràng hơn:

// some code
#if TYPE_X_COSTUMER  = 1
// do some things
#endif
// rest of the code

hoặc là

if(TYPE_X_COSTUMER) {
    // do some things
}

Các đối số tôi có thể nghĩ là:

  • Chỉ thị tiền xử lý dẫn đến dấu chân mã nhỏ hơn và ít nhánh hơn (trên trình biên dịch không tối ưu hóa)
  • Nếu các câu lệnh dẫn đến mã luôn biên dịch, ví dụ nếu ai đó mắc lỗi sẽ gây hại cho mã không liên quan cho dự án mà anh ta làm việc, lỗi sẽ vẫn xuất hiện và anh ta sẽ không làm hỏng cơ sở mã. Nếu không anh ta sẽ không nhận thức được tham nhũng.
  • Tôi luôn được yêu cầu sử dụng bộ xử lý hơn là sử dụng bộ tiền xử lý (Nếu đây là một đối số ...)

Điều gì là thích hợp hơn - khi nói về một cơ sở mã cho nhiều người tiêu dùng khác nhau?


3
Tỷ lệ cược bạn sẽ gửi một bản dựng không được tạo bằng trình biên dịch tối ưu hóa là gì? Và nếu điều đó xảy ra, liệu tác động có thành vấn đề (đặc biệt là khi đặt trong bối cảnh với tất cả các tối ưu hóa khác mà bạn sẽ bỏ lỡ sau đó)?

@delnan - Mã được sử dụng cho nhiều nền tảng khác nhau với nhiều trình biên dịch khác nhau. Và liên quan đến tác động - nó có thể trong một số trường hợp nhất định.
MByD

Bạn được bảo là thích cái gì? Điều đó giống như việc ép ai đó ăn thức ăn từ KFC trong khi anh ta không thích thịt gà.
đúng vào

@WTP - không, tôi không muốn biết nhiều hơn, vì vậy tôi sẽ đưa ra quyết định tốt hơn trong tương lai.
MByD

Trong ví dụ thứ hai của bạn, TYPE_X_CUSTOMERvẫn là macro tiền xử lý?
gièm pha

Câu trả lời:


5

Tôi nghĩ rằng có một lợi thế của việc sử dụng #define mà bạn đã không đề cập và đó là thực tế là bạn có thể đặt giá trị trên dòng lệnh (do đó, đặt nó từ tập lệnh xây dựng một bước của bạn).

Ngoài ra, nói chung là tốt hơn để tránh các macro. Họ không tôn trọng bất kỳ phạm vi nào và điều đó có thể gây ra vấn đề. Chỉ các trình biên dịch rất câm không thể tối ưu hóa một điều kiện dựa trên hằng số thời gian biên dịch. Tôi không biết đó có phải là mối lo ngại cho sản phẩm của bạn không (ví dụ: việc giữ mã nhỏ trên các nền tảng nhúng có thể rất quan trọng).


Trên thực tế, đây dành cho các hệ thống nhúng
MByD

Vấn đề là các hệ thống nhúng thường sử dụng một số trình biên dịch cổ (như ví dụ gcc 2.95).
BЈовић

@VJo - GCC là trường hợp may mắn :)
MByD

Hãy thử nó sau đó. Nếu nó bao gồm mã không được sử dụng và đó là một kích thước đáng kể, thì hãy đi đến định nghĩa.
Tamás Szelei

Nếu bạn muốn có thể thiết lập nó thông qua dòng lệnh của trình biên dịch, tôi vẫn sử dụng một hằng số trong chính mã và chỉ # xác định giá trị được gán cho hằng số đó.
CodeInChaos

12

Đối với nhiều câu hỏi, câu trả lời của câu hỏi này là tùy thuộc . Thay vì nói cái nào tốt hơn tôi đã đưa ra những ví dụ và mục tiêu mà cái này tốt hơn cái kia.

Cả tiền xử lý và hằng có những nơi sử dụng riêng phù hợp.

Trong trường hợp bộ xử lý trước, mã được loại bỏ trước thời gian biên dịch. Do đó, nó phù hợp nhất cho các tình huống mà mã dự kiến ​​sẽ không được biên dịch . Điều này có thể ảnh hưởng đến cấu trúc mô-đun, các phụ thuộc và nó có thể cho phép chọn các phân đoạn mã tốt nhất cho các khía cạnh hiệu suất. Trong các trường hợp sau đây, người ta phải chia mã bằng cách chỉ sử dụng bộ tiền xử lý.

  1. Mã đa nền tảng:
    Ví dụ: khi mã được biên dịch dưới các nền tảng khác nhau, khi mã có sự phụ thuộc vào số phiên bản cụ thể của HĐH (hoặc thậm chí là phiên bản trình biên dịch - mặc dù điều này rất hiếm). Ví dụ, khi bạn đang làm việc với các bản sao mã cuối cùng nhỏ cuối cùng - chúng phải được tách biệt với các bộ tiền xử lý thay vì hằng số. Hoặc nếu bạn đang biên dịch mã cho Windows cũng như Linux và các cuộc gọi hệ thống nhất định rất khác nhau.

  2. Các bản vá thử nghiệm:
    Một trường hợp khác mà điều này được đưa ra là hợp lý là một số mã thử nghiệm có rủi ro hoặc một số mô-đun chính cần được bỏ qua sẽ có sự khác biệt đáng kể về liên kết hoặc hiệu suất. Lý do tại sao người ta muốn vô hiệu hóa mã thông qua bộ tiền xử lý thay vì ẩn dưới if () là vì chúng tôi có thể không chắc chắn về các lỗi được đưa ra bởi bộ thay đổi cụ thể này và chúng tôi đang chạy trong cơ sở thử nghiệm. Nếu thất bại, chúng ta không phải làm gì khác ngoài việc vô hiệu hóa mã đó trong sản xuất hơn là viết lại. Đôi khi nó là lý tưởng để sử dụng #if 0 để nhận xét toàn bộ mã.

  3. Xử lý các phụ thuộc:
    Một lý do khác mà bạn có thể muốn tạo Ví dụ: nếu bạn không muốn hỗ trợ hình ảnh JPEG, bạn có thể giúp thoát khỏi việc biên dịch mô-đun / sơ khai đó và cuối cùng thư viện sẽ không liên kết (tĩnh hoặc động) với điều đó mô-đun. Đôi khi các gói chạy ./configuređể xác định tính khả dụng của sự phụ thuộc đó và nếu không có thư viện, (hoặc người dùng không muốn bật) chức năng đó sẽ tự động bị tắt mà không liên kết với thư viện đó. Ở đây luôn có lợi nếu các chỉ thị này được tạo tự động.

  4. Cấp phép:
    Một ví dụ rất thú vị về chỉ thị tiền xử lý là ffmpeg . Nó có codec có khả năng vi phạm bằng sáng chế bằng cách sử dụng nó. Nếu bạn tải xuống nguồn và biên dịch để cài đặt, nó sẽ hỏi bạn nếu bạn muốn hoặc tránh xa những thứ đó. Giữ các mã ẩn dưới một số nếu điều kiện vẫn có thể đưa bạn ra tòa!

  5. Mã sao chép-dán:
    Aka macro. Đây không phải là một lời khuyên để sử dụng quá mức các macro - chỉ là các macro có cách mạnh mẽ hơn nhiều để áp dụng tương đương sao chép trong quá khứ . Nhưng sử dụng nó rất cẩn thận; và sử dụng nó nếu bạn biết những gì bạn đang làm. Hằng số tất nhiên, không thể làm điều này. Nhưng người ta cũng có thể sử dụng các inlinechức năng nếu điều đó dễ thực hiện.

Vậy khi nào bạn sử dụng hằng số?
Hầu như mọi nơi khác.

  1. Luồng mã gọn gàng hơn:
    Nói chung, khi bạn sử dụng các hằng, nó gần như không thể phân biệt được với các biến thông thường và do đó, nó là mã dễ đọc hơn. Nếu bạn viết thường trình là 75 dòng - có 3 hoặc 4 dòng sau mỗi 10 dòng với #ifdef là RẤT không thể đọc được . Có thể được cung cấp một hằng số chính được quản lý bởi #ifdef và sử dụng nó trong dòng chảy tự nhiên mọi nơi.

  2. Mã thụt lề tốt: Tất cả các chỉ thị tiền xử lý, không bao giờ hoạt động tốt với mã được thụt lề tốt . Ngay cả khi trình biên dịch của bạn không cho phép thụt lề #def, bộ tiền xử lý Pre-ANSI C không cho phép khoảng trống giữa đầu dòng và ký tự "#"; "#" hàng đầu phải luôn được đặt trong cột đầu tiên.

  3. Cấu hình:
    Một lý do khác khiến các hằng số / hoặc biến có ý nghĩa là chúng có thể dễ dàng phát triển từ việc được liên kết với toàn cầu hoặc trong tương lai có thể được mở rộng để được lấy từ các tệp cấu hình.

Một điều cuối cùng:
Không bao giờ sử dụng các chỉ thị tiền xử lý #ifdefđể #endif vượt qua phạm vi hoặc { ... }. tức là bắt đầu #ifdefhoặc kết thúc #endifở các mặt khác nhau của { ... }. Điều này là vô cùng tồi tệ; nó có thể gây nhầm lẫn, đôi khi có thể nguy hiểm.

Tất nhiên, đây không phải là một danh sách đầy đủ, nhưng cho bạn thấy sự khác biệt rõ ràng, nơi sử dụng phương pháp nào thích hợp hơn. Nó không thực sự là cái nào tốt hơn , nó luôn luôn được sử dụng nhiều hơn trong bối cảnh cụ thể.


Cảm ơn câu trả lời dài và chi tiết. Tôi đã nhận thức được hầu hết những điều bạn đề cập và chúng bị bỏ qua khỏi câu hỏi vì câu hỏi không phải là có nên sử dụng khả năng tiền xử lý hay không, mà là về một kiểu sử dụng cụ thể. Nhưng tôi nghĩ rằng điểm cơ bản bạn thực hiện là đúng.
MByD

1
"Không bao giờ sử dụng các chỉ thị tiền xử lý #ifdef thành #endif vượt qua phạm vi hoặc {...}" tùy thuộc vào IDE. Nếu IDE nhận ra các khối tiền xử lý không hoạt động và thu gọn chúng hoặc hiển thị với màu khác nhau, điều đó không gây nhầm lẫn.
Abyx

@Abyx đúng là IDE có thể giúp. Tuy nhiên, trong nhiều trường hợp mọi người phát triển dưới nền tảng * nix (linux, v.v.) - sự lựa chọn của các biên tập viên, v.v. rất nhiều (VI, emacs, v.v.). Vì vậy, người khác có thể đang sử dụng một trình soạn thảo khác với trình soạn thảo của bạn.
Dipan Mehta

4

Khách hàng của bạn có quyền truy cập vào mã của bạn không? Nếu có, thì tiền xử lý có thể là một lựa chọn tốt hơn. Chúng tôi có một cái gì đó tương tự và chúng tôi sử dụng cờ thời gian biên dịch cho các khách hàng khác nhau (hoặc các tính năng cụ thể của khách hàng). Sau đó, một tập lệnh có thể trích xuất mã cụ thể của khách hàng và chúng tôi gửi mã đó. Các khách hàng không nhận thức được các khách hàng khác hoặc các tính năng cụ thể của khách hàng khác.


3

Một vài điểm bổ sung đáng được đề cập:

  1. Trình biên dịch có thể xử lý một số loại biểu thức hằng mà bộ tiền xử lý không thể. Ví dụ, bộ tiền xử lý nói chung sẽ không có cách nào để đánh giá sizeof()các loại không nguyên thủy. Điều này có thể buộc sử dụng if()trong một số trường hợp.

  2. Trình biên dịch sẽ bỏ qua hầu hết các vấn đề cú pháp trong mã bị bỏ qua #if(một số vấn đề liên quan đến tiền xử lý vẫn có thể gây rắc rối) nhưng nhấn mạnh vào tính chính xác cú pháp cho mã bị bỏ qua if(). Do đó, nếu mã được bỏ qua cho một số bản dựng nhưng không phải là mã không hợp lệ do các thay đổi ở nơi khác trong tệp (ví dụ: được đổi tên định danh), nó sẽ thường vặn vẹo trên tất cả các bản dựng nếu bị vô hiệu hóa if()nhưng không bị vô hiệu hóa #if. Tùy thuộc vào lý do tại sao mã bị bỏ qua, đó có thể là một điều tốt.

  3. Một số trình biên dịch sẽ bỏ qua mã không thể truy cập trong khi những trình biên dịch khác sẽ không; khai báo về thời lượng lưu trữ tĩnh trong mã không thể truy cập, tuy nhiên, bao gồm cả chuỗi ký tự, có thể phân bổ không gian ngay cả khi không có mã nào có thể sử dụng không gian được phân bổ như vậy.

  4. Macro có thể sử dụng if()các thử nghiệm bên trong chúng, nhưng không có cơ chế nào để bộ xử lý trước thực hiện bất kỳ logic điều kiện nào trong macro [lưu ý rằng để sử dụng trong macro, ? :toán tử thường có thể tốt hơn if, nhưng áp dụng các nguyên tắc tương tự].

  5. Lệnh #ifnày có thể được sử dụng để kiểm soát các macro được xác định.

Tôi nghĩ rằng if()thường sạch hơn đối với hầu hết các trường hợp ngoại trừ những trường hợp liên quan đến việc đưa ra quyết định dựa trên trình biên dịch nào đang sử dụng hoặc macro nào sẽ được xác định; một số vấn đề trên có thể cần phải sử dụng #if, tuy nhiên, ngay cả trong trường hợp ifcó vẻ sạch hơn.


1

Câu chuyện dài ngắn: những lo ngại về các chỉ thị tiền xử lý về cơ bản là 2, nhiều thể vùi và có thể ít mã dễ đọc hơn và logic logic hơn.

Nếu dự án của bạn nhỏ mà hầu như không có kế hoạch lớn cho tương lai, tôi nghĩ rằng cả hai tùy chọn đều có cùng tỷ lệ giữa ưu và nhược điểm, nhưng nếu dự án của bạn sẽ rất lớn, hãy xem xét một cái gì đó khác như viết tệp tiêu đề riêng nếu bạn vẫn muốn sử dụng các chỉ thị tiền xử lý, nhưng hãy nhớ rằng cách tiếp cận này thường làm cho mã ít đọc hơn và chắc chắn rằng tên của các hằng số đó có nghĩa là logic của chính chương trình.


Đây là một cơ sở mã tuyệt vời và định nghĩa nằm trong một số tiêu đề riêng biệt thực sự.
MByD
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.