最有用的用户制作的 C 宏(在 GCC 中,还有 C99)?[关闭]
-
21-09-2019 - |
题
什么 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-each在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中的函数来实现。
P.S。我可能会挑出上述IMPLIES
宏作为最有用的一个。其主要目的是为了便于更优雅和可读断言的写入,如在
void foo(int array[], int n) {
assert(IMPLIES(n > 0, array != NULL));
...
关键的一点与C宏正确地使用它们。在我心中有三类(不考虑到使用他们得到的描述性名称,常数)
- 作为一个简写一块代码一个都不想重复
- 提供一般性使用功能
- 修改结构的C语言(显然)
在第一种情况下,您的宏将生活在你的节目(通常只是一个文件),所以你可以使用的宏一样,你已经发布,是不受保护的防止双重评价参数和使用 {...};
(潜在的危险!).
在第二种情况下(甚至更多的第三),你需要 非常 小心你的宏正确的行为,如果他们是真正的C构造。
宏发布来自海湾合作委员会(最小和最大)是这方面的一个例子,他们使用全球变量 _a
和 _b
为了避免风险的双重评价(如在 max(x++,y++)
)(以及他们使用的海湾合作委员会的扩展,但这个概念是相同)。
我喜欢使用宏它有助于使事情更清楚但是他们被一个锋利的工具!可能那是什么给了他们这样一个不好的名声,我想他们是一个非常有用的工具和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 */
}
你可以增加上的变化这个主题得到的味道密克罗尼西亚联邦你需要的。
有人可能不喜欢这样的例子,但我找到它的完美演示如何简单的宏可以让你的代码的更清晰和表现力。
如果您需要在不同的上下文来定义数据多次,宏可以帮助您避免必须多次重新登录同样的事情。
举例来说,假设你要定义一个颜色枚举和枚举到字符串的函数,而不是列出所有的颜色两次,您可以创建颜色的文件( 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
之间逗号的是故意的。它打印一个格式化字符串源位置预谋。我在实时嵌入式系统工作,所以经常我还包括在输出的时间戳,以及
Foreach循环为GCC,特别C99与GNU扩展。适用于字符串和数组。动态分配数组可以通过将其转换成一指向数组的指针,然后解引用它们来使用。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#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;
}
此代码已经过测试与GCC,ICC和锵在GNU / Linux的工作。
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);
}
保存自己一些易错计数
还有人提到过 容器_of(), ,但没有提供这个非常方便的宏的解释。假设您有一个如下所示的结构:
struct thing {
int a;
int b;
};
现在如果我们有一个指向 乙, , 我们可以用 容器_of() 得到一个指向 事物 以类型安全的方式:
int *bp = ...;
struct thing *t = container_of(bp, struct thing, b);
这对于创建抽象数据结构很有用。例如,您现在可以编写一个如下所示的 slist 实现,而不是采用queue.h 所采用的方法来创建 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);
}
这不是疯狂的宏代码。它将在错误时提供良好的编译器行号,并且与调试器配合良好。它也是相当类型安全的,除了结构使用多种类型的情况(例如,如果我们允许 结构颜色 在下面的例子中,要在更多的链接列表上,而不仅仅是 颜色 一)。
用户现在可以像这样使用您的库:
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))
寻找最近的32位无符号整数,它是大于x。我使用它来数组的大小(即,高水位标记)的两倍。
包字节,字,双字成字,双字和四字:
#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似乎是流行的。