psi*_*lia 28 c macros c99 c-preprocessor
您认为哪种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];}
Run Code Online (Sandbox Code Playgroud)
它的工作方式如下:
v3_op_v3(vectorA, +, vectorB, vectorC);
v3_op_v3(vectorE, *, vectorF, vectorJ);
...
Run Code Online (Sandbox Code Playgroud)
AnT*_*AnT 34
#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)
Run Code Online (Sandbox Code Playgroud)
当然还有各种MIN,MAX,ABS等.
注意,顺便说一下,上述任何一个都不能用C中的函数实现.
PS我可能会将上面的IMPLIES
宏作为最有用的宏之一.它的主要目的是促进编写更优雅和可读的断言,如
void foo(int array[], int n) {
assert(IMPLIES(n > 0, array != NULL));
...
Run Code Online (Sandbox Code Playgroud)
Rem*_*o.D 22
C宏的关键是正确使用它们.在我看来有三个类别(不考虑使用它们只是为常量提供描述性名称)
在第一种情况下,您的宏将只存在于您的程序中(通常只是一个文件),因此您可以使用您发布的宏,这些宏不受保护,不会对参数和使用进行双重评估{...};
(有潜在危险!).
在第二种情况下(甚至更多在第三种情况下),您需要非常小心,您的宏行为正确,就像它们是真正的C构造一样.
您从GCC发布的宏(最小值和最大值)就是一个例子,他们使用全局变量_a
并_b
避免双重评估的风险(如in max(x++,y++)
)(好吧,他们使用GCC扩展但概念是相同的).
我喜欢使用宏,它有助于使事情更清晰,但它们是一个敏锐的工具!可能这就是给他们带来如此糟糕声誉的东西,我认为他们是一个非常有用的工具,如果他们不存在,C会更差.
我看到其他人提供了第2点(宏作为函数)的示例,让我举一个创建新C构造的示例:有限状态机.(我已经在SO上发布了这个,但我似乎无法找到它)
#define FSM for(;;)
#define STATE(x) x##_s
#define NEXTSTATE(x) goto x##_s
Run Code Online (Sandbox Code Playgroud)
你用这种方式:
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 */
}
Run Code Online (Sandbox Code Playgroud)
您可以在此主题上添加变体,以获得所需的FSM风格.
有人可能不喜欢这个例子,但我发现它非常适合演示简单的宏如何使你的代码更易读和富有表现力.
Joh*_*itb 14
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);
}
Run Code Online (Sandbox Code Playgroud)
srp*_*ish 11
如果需要在不同的上下文中多次定义数据,宏可以帮助您避免多次重复同一事物.
例如,假设您要定义颜色枚举和枚举到字符串函数,而不是列出所有颜色两次,您可以创建颜色文件(colors.def):
c(red)
c(blue)
c(green)
c(yellow)
c(brown)
Run Code Online (Sandbox Code Playgroud)
现在您可以在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]);
};
Run Code Online (Sandbox Code Playgroud)
#if defined NDEBUG
#define TRACE( format, ... )
#else
#define TRACE( format, ... ) printf( "%s::%s(%d)" format, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__ )
#endif
Run Code Online (Sandbox Code Playgroud)
请注意,之间缺少逗号"%s::%s(%d)"
并且format
是故意的.它打印一个带有源位置的格式化字符串.我在实时嵌入式系统中工作,所以我经常在输出中也包含一个时间戳.
GCC的Foreach循环,特别是带GNU扩展的C99.使用字符串和数组.动态分配的数组可以通过将它们转换为指向数组的指针,然后取消引用它们来使用.
#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;
}
Run Code Online (Sandbox Code Playgroud)
此代码已经过测试,可与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);
}
Run Code Online (Sandbox Code Playgroud)
#define COLUMNS(S,E) [ (E) - (S) + 1 ]
struct
{
char firstName COLUMNS ( 1, 20);
char LastName COLUMNS (21, 40);
char ssn COLUMNS (41, 49);
}
Run Code Online (Sandbox Code Playgroud)
保存自己一些容易出错的计数
有人提到了container_of(),但没有提供这个非常方便的宏的解释.假设您有一个如下所示的结构:
struct thing {
int a;
int b;
};
Run Code Online (Sandbox Code Playgroud)
现在,如果我们有一个指向b的指针,我们可以使用container_of()以类型安全的方式获取指向thing的指针:
int *bp = ...;
struct thing *t = container_of(bp, struct thing, b);
Run Code Online (Sandbox Code Playgroud)
这在创建抽象数据结构时很有用.例如,不是采用方法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);
}
Run Code Online (Sandbox Code Playgroud)
哪个不是疯狂的宏代码.它将为错误提供良好的编译器行号,并与调试器配合使用.它也是相当类型安全的,除了结构使用多种类型的情况(例如,如果我们允许下面示例中的结构颜色位于更多链接列表而不仅仅是颜色之一).
用户现在可以像这样使用您的库:
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);
Run Code Online (Sandbox Code Playgroud)