最有用的用户自制C-宏(在GCC中,也是C99)?

您认为哪种C宏最有用? 我找到了以下一个,我用它在C中进行矢量运算:

#define v3_op_v3(x, op, y, z) {z[0]=x[0] op y[0]; \ z[1]=x[1] op y[1]; \ z[2]=x[2] op y[2];} 

它的工作方式如下:

 v3_op_v3(vectorA, +, vectorB, vectorC); v3_op_v3(vectorE, *, vectorF, vectorJ); ... 

for C99中的每个循环:

 #define foreach(item, array) \ for(int keep=1, \ count=0,\ size=sizeof (array)/sizeof *(array); \ keep && count != size; \ keep = !keep, count++) \ for(item = (array)+count; keep; keep = !keep) int main() { int a[] = { 1, 2, 3 }; int sum = 0; foreach(int const* c, a) sum += *c; printf("sum = %d\n", sum); // multi-dim array int a1[][2] = { { 1, 2 }, { 3, 4 } }; foreach(int (*c1)[2], a1) foreach(int *c2, *c1) printf("c2 = %d\n", *c2); } 
 #define IMPLIES(x, y) (!(x) || (y)) #define COMPARE(x, y) (((x) > (y)) - ((x) < (y))) #define SIGN(x) COMPARE(x, 0) #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a)) #define SWAP(x, y, T) do { T tmp = (x); (x) = (y); (y) = tmp; } while(0) #define SORT2(a, b, T) do { if ((a) > (b)) SWAP((a), (b), T); } while (0) #define SET(d, n, v) do{ size_t i_, n_; for (n_ = (n), i_ = 0; n_ > 0; --n_, ++i_) (d)[i_] = (v); } while(0) #define ZERO(d, n) SET(d, n, 0) 

当然还有各种MIN,MAX,ABS等。

注意,顺便说一句,上述任何一个都不能用C中的函数实现。

PS我可能会将上面的IMPLIES宏作为最有用的宏之一。 其主要目的是促进编写更优雅和可读的断言,如

 void foo(int array[], int n) { assert(IMPLIES(n > 0, array != NULL)); ... 

C宏的关键是正确使用它们。 在我看来有三个类别(不考虑使用它们只是为常量提供描述性的名称)

  1. 作为一段代码的简写,人们不想重复
  2. 提供一般使用function
  3. 修改C语言的结构(显然)

在第一种情况下,您的宏将只存在于您的程序中(通常只是一个文件),因此您可以使用您发布的宏,这些宏不受保护,不会对参数进行双重评估并使用{...}; (有潜在危险!)

在第二种情况下(甚至更多在第三种情况下),您需要非常小心,您的宏行为正确,就像它们是真正的C构造一样。

你从GCC发布的宏(最小值和最大值)就是一个例子,他们使用全局变量_a_b来避免双重评估的风险(比如max(x++,y++) )(好吧,他们使用GCC扩展但是这个概念是一样的)。

我喜欢使用宏,它有助于使事情更清晰,但它们是一个敏锐的工具! 可能这就是给他们这么糟糕声誉的东西,我认为他们是一个非常有用的工具,如果他们不存在,C会更差。

我看到其他人提供了第2点(宏作为函数)的示例,让我举一个创建新C构造的示例:有限状态机。 (我已经发布了这个,但似乎无法找到它)

  #define FSM for(;;) #define STATE(x) x##_s #define NEXTSTATE(x) goto x##_s 

你用这种方式:

  FSM { STATE(s1): ... do stuff ... NEXTSTATE(s2); STATE(s2): ... do stuff ... if (k<0) NEXTSTATE(s2); /* fallthrough as the switch() cases */ STATE(s3): ... final stuff ... break; /* Exit from the FSM */ } 

您可以在此主题上添加变体,以获得所需的FSM风格。

有人可能不喜欢这个例子,但我发现它非常适合演示简单的宏如何使你的代码更易读和富有表现力。

如果需要在不同的上下文中多次定义数据,宏可以帮助您避免多次重复同一事物。

例如,假设您要定义一个颜色枚举和一个枚举到字符串函数,而不是列出所有颜色两次,您可以创建一个颜色文件( colors.def ):

 c(red) c(blue) c(green) c(yellow) c(brown) 

现在,您可以在c文件中定义枚举和字符串转换函数:

 enum { #define c(color) color, # include "colors.def" #undef c }; const char * color_to_string(enum color col) { static const char *colors[] = { #define c(color) #color, # include "colors.def" #undef c }; return (colors[col]); }; 
 #if defined NDEBUG #define TRACE( format, ... ) #else #define TRACE( format, ... ) printf( "%s::%s(%d)" format, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__ ) #endif 

请注意, "%s::%s(%d)"format之间缺少逗号是故意的。 它打印一个带有源位置的格式化字符串。 我在实时嵌入式系统中工作,所以我经常在输出中也包含一个时间戳。

GCC的Foreach循环,特别是带GNU扩展的C99。 适用于字符串和数组。 动态分配的数组可以通过将它们转换为指向数组的指针,然后解除引用它们来使用。

 #include  #include  #include  #include  #define FOREACH_COMP(INDEX, ARRAY, ARRAY_TYPE, SIZE) \ __extension__ \ ({ \ bool ret = 0; \ if (__builtin_types_compatible_p (const char*, ARRAY_TYPE)) \ ret = INDEX < strlen ((const char*)ARRAY); \ else \ ret = INDEX < SIZE; \ ret; \ }) #define FOREACH_ELEM(INDEX, ARRAY, TYPE) \ __extension__ \ ({ \ TYPE *tmp_array_ = ARRAY; \ &tmp_array_[INDEX]; \ }) #define FOREACH(VAR, ARRAY) \ for (void *array_ = (void*)(ARRAY); array_; array_ = 0) \ for (size_t i_ = 0; i_ && array_ && FOREACH_COMP (i_, array_, \ __typeof__ (ARRAY), \ sizeof (ARRAY) / sizeof ((ARRAY)[0])); \ i_++) \ for (bool b_ = 1; b_; (b_) ? array_ = 0 : 0, b_ = 0) \ for (VAR = FOREACH_ELEM (i_, array_, __typeof__ ((ARRAY)[0])); b_; b_ = 0) /* example's */ int main (int argc, char **argv) { int array[10]; /* initialize the array */ int i = 0; FOREACH (int *x, array) { *x = i; ++i; } char *str = "hello, world!"; FOREACH (char *c, str) printf ("%c\n", *c); /* Use a cast for dynamically allocated arrays */ int *dynamic = malloc (sizeof (int) * 10); for (int i = 0; i < 10; i++) dynamic[i] = i; FOREACH (int *i, *(int(*)[10])(dynamic)) printf ("%d\n", *i); return EXIT_SUCCESS; } 

此代码已经过测试,可与GNU / Linux上的GCC,ICC和Clang一起使用。

Lambda表达式(仅限GCC)

 #define lambda(return_type, ...) \ __extension__ \ ({ \ return_type __fn__ __VA_ARGS__ \ __fn__; \ }) int main (int argc, char **argv) { int (*max) (int, int) = lambda (int, (int x, int y) { return x > y ? x : y; }); return max (1, 2); } 
 #define COLUMNS(S,E) [ (E) - (S) + 1 ] struct { char firstName COLUMNS ( 1, 20); char LastName COLUMNS (21, 40); char ssn COLUMNS (41, 49); } 

节省一些容易出错的计数

有人提到了container_of() ,但没有提供这个非常方便的宏的解释。 假设您有一个如下所示的结构:

 struct thing { int a; int b; }; 

现在,如果我们有一个指向b的指针,我们可以使用container_of()以类型安全的方式获取指向thing的指针:

 int *bp = ...; struct thing *t = container_of(bp, struct thing, b); 

这在创建抽象数据结构时很有用。 例如,不是采用方法queue.h来创建SLIST(每个操作的大量疯狂宏)之类的东西,你现在可以编写一个看起来像这样的slist实现:

 struct slist_el { struct slist_el *next; }; struct slist_head { struct slist_el *first; }; void slist_insert_head(struct slist_head *head, struct slist_el *el) { el->next = head->first; head->first = el; } struct slist_el slist_pop_head(struct slist_head *head) { struct slist_el *el; if (head->first == NULL) return NULL; el = head->first; head->first = el->next; return (el); } 

哪个不是疯狂的宏代码。 它将为错误提供良好的编译器行号,并与调试器配合使用。 它也是相当类型安全的,除了结构使用多种类型的情况(例如,如果我们允许下面示例中的结构颜色位于更多链接列表而不仅仅是颜色 1)。

用户现在可以像这样使用您的库:

 struct colors { int r; int g; int b; struct slist_el colors; }; struct *color = malloc(sizeof(struct person)); color->r = 255; color->g = 0; color->b = 0; slist_insert_head(color_stack, &color->colors); ... el = slist_pop_head(color_stack); color = el == NULL ? NULL : container_of(el, struct color, colors); 

这个是来自linux内核(具体是gcc):

 #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) ); }) 

其他答案中缺少另一个:

 #define LSB(x) ((x) ^ ((x) - 1) & (x)) // least significant bit 

我也喜欢这个:

 #define COMPARE_FLOATS(a,b,epsilon) (fabs(a - b) <= epsilon * fabs(a)) 

你是如何对宏观者进行公平的浮点比较呢?

只是标准的:

 #define LENGTH(array) (sizeof(array) / sizeof (array[0])) #define QUOTE(name) #name #define STR(name) QUOTE(name) 

但那里没什么太漂亮的。

 #define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) 

找到最接近的大于x的32位无符号整数。 我用这个来加倍arrays的大小(即高水位线)。

将字节,单词,双字组合成单词,双字和qwords:

 #define ULONGLONG unsigned __int64 #define MAKEWORD(h,l) ((unsigned short) ((h) << 8)) | (l) #define MAKEDWORD(h,l) ((DWORD) ((h) << 16)) | (l) #define MAKEQWORD(h,l) ((ULONGLONG)((h) << 32)) | (l) 

括号括起来,避免对扩展产生副作用总是一种很好的做法。

还有多种类型的最小值和最大值

 //NOTE: GCC extension ! #define max(a,b) ({typeof (a) _a=(a); typeof (b) _b=(b); _a > _b ? _a:_b; }) #define min(a,b) ({typeof (a) _a=(a); typeof (b) _b=(b); _a < _b ? _a:_b; }) 

检查浮点x是否不是数字:

 #define ISNAN(x) ((x) != (x)) 

我经常使用的一个(极少数)是一个将参数或变量声明为未使用的宏。 需要注意的最兼容的解决方案(IMHO)因编译器而异。

这个很棒:

 #define NEW(type, n) ( (type *) malloc(1 + (n) * sizeof(type)) ) 

我用它像:

 object = NEW(object_type, 1); 

TRUE和FALSE似乎很受欢迎。