2012-01-06 31 views
10

Tôi có một chức năng mà tôi cần để macro'ize. Hàm này chứa các biến tạm thời và tôi không thể nhớ nếu có bất kỳ quy tắc nào về việc sử dụng các biến tạm thời trong các thay thế macro.Macro C có thể chứa các biến tạm thời không?

long fooAlloc(struct foo *f, long size) 
{ 
    long  i1, i2; 
    double *data[7]; 

    /* do something */ 
    return 42; 
} 

Mẫu MACRO:

#define ALLOC_FOO(f, size) \ 
{\ 
    long  i1, i2;\ 
    double *data[7];\ 
\ 
    /* do something */ \ 
} 

là ok này? (tức là không có tác dụng phụ khó chịu - ngoại trừ những tác dụng phụ thông thường: không phải "loại an toàn", v.v.). BTW, tôi biết "macro là ác" - tôi chỉ đơn giản là phải sử dụng nó trong trường hợp này - không có nhiều sự lựa chọn.

+3

Tại sao bạn cần phải macroize nó? Nếu bạn lo lắng về hiệu suất, thì (a) cơ hội là bạn đang lo lắng vô ích (trừ khi bạn đã thực sự đo nó và kết luận rằng đó là một nút cổ chai đáng kể) và (b) bạn có thể đạt được hiệu quả tương tự, trong một loại cách an toàn, bằng cách làm cho hàm 'inline' (một số trình biên dịch cũ hơn hoặc Microsoft có thể không hỗ trợ điều này). –

+3

@KeithThompson hiệu suất không phải là vấn đề ở đây. Tôi đang viết một thư viện mở rộng và tôi cần một hàm wrapper để làm một số dữ liệu marshalling. Macro chỉ đơn giản là cung cấp một cách dễ bị lỗi do tự động tạo ra rất nhiều mã "keo". –

+0

'return' bên trong macro không làm những gì bạn nghĩ. –

Trả lời

24

Chỉ có hai điều kiện theo đó nó hoạt động theo bất kỳ cách nào "hợp lý".

  1. Macro không có câu lệnh trả về. Bạn có thể sử dụng thủ thuật do while.

    #define macro(x) do { int y = x; func(&y); } while (0) 
    
  2. Bạn chỉ nhắm mục tiêu GCC.

    #define min(x,y) ({ int _x = (x), _y = (y); _x < _y ? _x : _y; }) 
    

Nó sẽ giúp đỡ nếu bạn giải thích lý do tại sao bạn phải sử dụng một macro (không văn phòng của bạn có "Thứ Hai vĩ mô" hoặc một cái gì đó?). Nếu không, chúng tôi không thể giúp được gì.

6

Macro C chỉ thay thế văn bản (tương đối đơn giản).

Vì vậy, câu hỏi mà bạn có thể hỏi là: tôi có thể tạo các khối (còn gọi là các câu lệnh ghép) trong một hàm như trong ví dụ dưới đây không?

void foo(void) 
{ 
    int a = 42; 
    { 
     int b = 42; 
     { 
      int c = 42; 
     } 
    } 
} 

và câu trả lời là có.

Bây giờ như @DietrichEpp đã đề cập đến nó trong câu trả lời của mình, nếu macro là một câu lệnh phức hợp như trong ví dụ của bạn, thực hành tốt là kèm theo các câu lệnh macro với do { ... } while (0) thay vì chỉ { ... }. Các liên kết dưới đây giải thích những gì tình hình do { ... } while (0) trong một cố gắng vĩ mô để ngăn chặn:

http://gcc.gnu.org/onlinedocs/cpp/Swallowing-the-Semicolon.html

Ngoài ra khi bạn viết một macro chức năng giống như luôn luôn tự hỏi mình nếu bạn có một lợi thế thực sự làm như vậy bởi vì thường xuyên nhất viết một thay vào đó là chức năng tốt hơn.

0

Có nó sẽ hoạt động khi bạn sử dụng cấu trúc khối và các biến tạm thời được khai báo trong phạm vi bên trong của khối này.

Lưu ý lần cuối \ sau} là dư thừa.

0

Chúng có thể. Họ thường không nên.

Tại sao chức năng này cần phải là macro? Thay vào đó, bạn có thể đặt nội tuyến không?

+1

Trừ khi bạn sẽ tìm thấy khi chương trình của bạn phát triển và hỗ trợ các vấn đề phát sinh, việc duy trì mã được tạo bởi macro KHÔNG ít bị lỗi hơn. Đó là một mớ hỗn độn. – Marvo

0

Nếu bạn đang sử dụng C++ sử dụng nội tuyến hoặc sử dụng -o3 với gcc, nó sẽ điền tất cả các chức năng cho bạn. Tôi vẫn không hiểu tại sao bạn cần phải macro hóa chức năng này.

+1

C cũng có 'nội tuyến'. –

5

này chủ yếu là OK, ngoại trừ macro thường được kèm theo do { ... } while(0) (hãy nhìn vào this question cho lời giải thích):

#define ALLOC_FOO(f, size) \ 
    do { \ 
     long  i1, i2;\ 
     double *data[7];\ 
     /* do something */ \ 
    } while(0) 

Ngoài ra, theo như ban đầu fooAlloc chức năng của bạn trả về long bạn phải thay đổi của bạn macro để lưu trữ kết quả bằng cách nào đó khác. Hoặc, nếu bạn sử dụng GCC, bạn có thể thử compound statement mở rộng:

#define ALLOC_FOO(f, size) \ 
    ({ \ 
     long  i1, i2;\ 
     double *data[7];\ 
     /* do something */ \ 
     result; \ 
    }) 

Cuối cùng, bạn nên quan tâm đến các tác dụng phụ có thể mở rộng tranh luận vĩ mô. Các mô hình thông thường được xác định một biến tạm thời cho mỗi đối số bên trong một khối và sử dụng chúng để thay thế: Câu trả lời

#define ALLOC_FOO(f, size) \ 
    ({ \ 
     typeof(f) __f = (f);\ 
     typeof(size) __size = (size);\ 
     long  i1, i2;\ 
     double *data[7];\ 
     /* do something */ \ 
     result; \ 
    }) 
+0

Mắt tôi đang chảy máu – paulm

2

Eldar cho thấy bạn hầu hết những cạm bẫy của chương trình vĩ mô và một số (tiêu chuẩn nhưng không) có ích mở rộng gcc.

Nếu bạn muốn tuân theo tiêu chuẩn, kết hợp các macro (cho tính tổng quát) và các chức năng inline (đối với các biến cục bộ) có thể hữu ích.

inline 
long fooAlloc(void *f, size_t size) 
{ 
    size_t  i1, i2; 
    double *data[7]; 

    /* do something */ 
    return 42; 
} 


#define ALLOC_FOO(T) fooAlloc(malloc(sizeof(T)), sizeof(T)) 

Trong trường hợp này sử dụng sizeof chỉ đánh giá sự biểu hiện cho các loại tại thời gian biên dịch và không cho giá trị của nó, vì vậy đây sẽ không đánh giá F hai lần.

BTW, "kích thước" thường phải được nhập bằng size_t chứ không phải với long hoặc tương tự.

Edit: Như cho câu hỏi của Jonathan về inline chức năng, tôi đã viết lên một cái gì đó về mô hình inline của C99, here.

+0

Nếu đó là 'nội tuyến tĩnh ...'? –

+0

@ Jonathan, tại sao lại như vậy? –

+0

Bạn cần phải cực kỳ cẩn thận với 'inline' trong C (IMO). Ba câu hỏi có liên quan nhất mà tôi tìm thấy trên SO là ["Định nghĩa nội tuyến?"] (Http://stackoverflow.com/questions/8454690/inline-definitions), ["Nội tuyến không có tĩnh hoặc extern có hữu ích trong C99 không?" ] (http://stackoverflow.com/questions/6312597/is-inline-without-static-or-extern-ever-useful-in-c99), và ["extern inline?"] (http: // stackoverflow. com/questions/216510/extern-inline), không theo bất kỳ thứ tự cụ thể nào. (Và, để ghi lại, tôi không có ý kiến ​​gì về vấn đề này; tôi đã hỏi một câu hỏi.) –

7

Trước tiên, tôi thực sự khuyên bạn nên sử dụng các hàm nội dòng. Có rất ít thứ mà macro có thể làm và chúng không thể, và chúng có nhiều khả năng làm những gì bạn mong đợi hơn.

Một điểm yếu của các macro mà tôi không thấy trong các câu trả lời khác, là che khuất các tên biến.
Giả sử bạn định nghĩa:

#define A(x) { int temp = x*2; printf("%d\n", temp); } 

Và một người nào đó sử dụng nó theo cách này:

int temp = 3; 
A(temp); 

Sau khi tiền xử lý, mã này là:

int temp = 3; 
{ int temp = temp*2; printf("%d\n", temp); } 

này không làm việc, bởi vì nội bộ temp đổ bóng bên ngoài.
Giải pháp chung là gọi biến số __temp, giả sử không ai sẽ xác định biến bằng tên này (đây là giả định lạ, cho rằng bạn vừa làm nó).

0

Một giải pháp không hoàn hảo: (không làm việc với macro đệ quy, ví dụ nhiều vòng bên nhau)

#define JOIN_(X,Y) X##Y 
#define JOIN(X,Y) JOIN_(X,Y) 
#define TMP JOIN(tmp,__LINE__) 

#define switch(x,y) int TMP = x; x=y;y=TMP 

int main(){ 
    int x = 5,y=6; 
    switch(x,y); 
    switch(x,y); 
} 

sẽ trở thành sau khi chạy Preprocessor:

int main(){ 
    int x=5,y=6; 
    int tmp9 = x; x=y; y=tmp9; 
    int tmp10 = x; x=y; y=tmp10; 
} 
Các vấn đề liên quan