Điểm của macro PROTOTYPE chỉ đơn thuần mở rộng đến các đối số của nó là gì?


82

Tôi có một tệp tiêu đề chứa

#define PROTOTYPE(s) s

quan điểm đó là gì? Có vẻ như nó sẽ chỉ thay thế đầu vào bằng chính nó.

Có TẤN chỉ thị khác xung quanh nó, nhưng chỉ có một mà dường như có bất kỳ mang chỉ cần kiểm tra nếu nó được định nghĩa: #ifndef PROTOTYPE. Tôi tìm thấy một số nơi trong các tập tin tiêu đề HDF4 rằng làm điều này: #define PROTOTYPE. Vì vậy, không ai trong số đó thực sự làm rõ câu hỏi của tôi. Vẫn có vẻ khá vô dụng.

Đây là cách nó được sử dụng:

CS_RETCODE clientmsg_callback PROTOTYPE((
CS_CONTEXT * context,
CS_CONNECTION *connection,
CS_CLIENTMSG *clientmsg));

Đây là một phần của dự án sử dụng Sybase Open Client. clientmsg_callback sau đó được sử dụng tại đây:

ct_callback(context, NULL, CS_SET, CS_CLIENTMSG_CB,
                  (CS_VOID *)clientmsg_callback);

Tôi sẽ rời khỏi một chương trình mẫu từ đây:

http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.dc35570.1570/html/clcprgde/clcprgde10.htm

clientmsg_callback được triển khai sau đó. Tôi nghĩ rằng mẫu ban đầu được viết bằng C trong tâm trí, thay vì C ++. Có lẽ điều đó có liên quan đến nó?


6
Có lân cận #if/ #ifdef/ #ifndef/ #elsechỉ thị, nơi nó có thể có một định nghĩa khác nhau để thay thế? Nó có thể tạo ra sự khác biệt khi được sử dụng trong các macro khác, đặc biệt là gần #hoặc ##. Nó có thể chỉ dành cho một phong cách bình luận. Không đủ ngữ cảnh để thực sự trả lời.
aschepler

Như một câu trả lời chung: bởi vì ai đó có thể có lý do để muốn thay đổi PROTOTYPE. Nếu bạn thấy các định nghĩa kỳ lạ trong mã có vẻ vô dụng, hãy nghĩ đến tính linh hoạt tiềm ẩn nếu ai đó muốn thay đổi điều gì đó một cách thuận tiện.
Apollys hỗ trợ Monica vào

Câu trả lời:


130

Quay trở lại những ngày xa xưa của C thực sự, rất sớm, không có cái gọi là nguyên mẫu. Danh sách đối số của hàm xuất hiện sau dấu ngoặc đơn của hàm, như sau :

square(x)
int x;
{
int y = x * x;
return y;
}

Ngày nay, tất nhiên, các đối số nằm trong dấu ngoặc đơn:

square(int x)
{
int y = x * x;
return y;
}

Lưu ý kiểu trả về "thiếu"; Các hàm C được sử dụng để trả về một cách ngầm định intvà chỉ khi bạn cần một kiểu trả về khác thì bạn mới phải nói nó là gì.

Khai báo hàm có một bộ quy tắc khác. Một khai báo hàm trong K&R C (phiên bản cổ) không có đối số:

int square();

Và các nguyên mẫu hàm trong ANSI C có danh sách các đối số:

int square(int x);

Trong quá trình chuyển đổi, mọi người đã sử dụng các macro lập dị để họ có thể biên dịch theo cả hai cách:

int square(PROTOTYPE(int x));

Với

#define PROTOTYPE(s)

sẽ mở rộng đến phiên bản đầu tiên.

Với

#define PROTOTYPE(s) s

nó sẽ mở rộng sang thứ hai.

Liên quan đến dấu ngoặc đơn "bổ sung" trong mã trong câu hỏi, chúng cần thiết khi có nhiều hơn một đối số trong danh sách đối số. Nếu không có chúng, lệnh gọi macro có nhiều hơn một đối số, vì vậy sẽ không khớp với một macro được xác định chỉ với một đối số:

PROTOTYPE(int x, int y)   // error: too many arguments
PROTOTYPE((int x, int y)) // ok: only one argument (enclosed in parentheses)

10
Chà. Vụ nổ từ quá khứ. Một trong những công việc đầu tiên của tôi trong lĩnh vực phần mềm là tách công cụ này ra khỏi cơ sở mã hiện có. Xây dựng sự tôn trọng lành mạnh đối với mảng các chương trình thay đổi văn bản một lớp lót của Unix.
user4581301

15
Tôi không hiểu những định nghĩa đó hoạt động như thế nào, làm thế nào #define PROTOTYPE(s)với đầu vào int x;được chuyển thành x? Có vẻ như nó kết thúc tốt đẹp cho một chuỗi rỗng với tôi
Ferrybig

3
@Ferrybig - xin lỗi, tôi đã nhầm lẫn mọi thứ. Đó là nguyên mẫu được xác định theo cách này. Trong K&R C, một nguyên mẫu không có đối số và trong ANSI C, nó có kiểu danh sách đối số mà chúng ta thường thấy.
Pete Becker

1
Thực hành này cũng có thể được nhìn thấy trong các zlib.htiêu đề từ thư viện zlib với OF()vĩ mô: github.com/madler/zlib/blob/master/zlib.h
Paul Belanger

@PaulBelanger Định nghĩa thực tế là zconf.h.
SS Anne

16

Các macro như thế này sẽ được sử dụng trong các nguyên mẫu trong tệp tiêu đề để cho phép một cái gì đó như sau:

int foo PROTOTYPE((int bar));

Nếu ANSI C được phát hiện ( __STDC__được định nghĩa là 1), điều này sẽ mở rộng thành:

int foo(int bar);

Nếu ANSI C không được phát hiện, điều này sẽ mở rộng thành:

int foo();

vốn phổ biến trước khi C được chuẩn hóa.

Một số thư viện vẫn làm điều này; nếu bạn nhìn vào tcpd.h(nếu bạn có sẵn), bạn sẽ thấy:

/* someone else may have defined this */
#undef  __P

/* use prototypes if we have an ANSI C compiler or are using C++ */
#if defined(__STDC__) || defined(__cplusplus)
#define __P(args)       args
#else
#define __P(args)       ()
#endif

Điều này giải thích nó tốt.

Đối với các dấu ngoặc kép, __P(arg1, arg2)sẽ gây ra lỗi cú pháp (truyền quá nhiều đối số cho macro), trong khi __P((arg1, arg2))sẽ không sao (chỉ một trong các dấu ngoặc đơn).

Điều này tương tự như __extension__((...))trong GNU C. Trong các trình biên dịch không phải GNU, chỉ cần #define __extension__(unused)có mã bán di động, vì chỉ một "đối số" được đưa ra, được bao bọc trong dấu ngoặc đơn.

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.