例如,我最近在linux内核中遇到过这个问题:
/* Force a compilation error if condition is true */ #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
所以,在你的代码中,如果你有一些必须的结构,比如8个字节的大小,可能是因为一些硬件限制,你可以这样做:
BUILD_BUG_ON((sizeof(struct mystruct) % 8) != 0);
除非struct mystruct的大小是8的倍数,否则它将不会编译,如果它是8的倍数,则根本不会生成运行时代码.
我知道的另一个技巧是"Graphics Gems"一书,它允许单个头文件在一个模块中声明和初始化变量,而在使用该模块的其他模块中,只是将它们声明为externs.
#ifdef DEFINE_MYHEADER_GLOBALS #define GLOBAL #define INIT(x, y) (x) = (y) #else #define GLOBAL extern #define INIT(x, y) #endif GLOBAL int INIT(x, 0); GLOBAL int somefunc(int a, int b);
有了它,定义x和somefunc的代码可以:
#define DEFINE_MYHEADER_GLOBALS #include "the_above_header_file.h"
而仅使用x和somefunc()的代码可以:
#include "the_above_header_file.h"
所以你得到一个头文件,它声明了需要它们的全局变量和函数原型的实例,以及相应的extern声明.
那么,你最喜欢的C编程技巧是什么?
Eva*_*ran 81
C99使用匿名数组提供了一些非常酷的东西:
删除无意义的变量
{
int yes=1;
setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
}
Run Code Online (Sandbox Code Playgroud)
变
setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, (int[]){1}, sizeof(int));
Run Code Online (Sandbox Code Playgroud)
传递可变数量的参数
void func(type* values) {
while(*values) {
x = *values++;
/* do whatever with x */
}
}
func((type[]){val1,val2,val3,val4,0});
Run Code Online (Sandbox Code Playgroud)
静态链表
int main() {
struct llist { int a; struct llist* next;};
#define cons(x,y) (struct llist[]){{x,y}}
struct llist *list=cons(1, cons(2, cons(3, cons(4, NULL))));
struct llist *p = list;
while(p != 0) {
printf("%d\n", p->a);
p = p->next;
}
}
Run Code Online (Sandbox Code Playgroud)
任何我确定我还没有想到的许多其他很酷的技术.
for*_*ran 68
在阅读Quake 2源代码时,我想到了这样的事情:
double normals[][] = {
#include "normals.txt"
};
Run Code Online (Sandbox Code Playgroud)
(或多或少,我现在没有代码检查它).
从那时起,一个创造性地使用预处理器的新世界在我眼前展开.我不再只包含标题,而是包括整个代码块(它可以提高可重用性):-p
谢谢John Carmack!的xD
Joh*_*ter 50
我喜欢使用= {0};
初始化结构而不需要调用memset.
struct something X = {0};
Run Code Online (Sandbox Code Playgroud)
这会将struct(或数组)的所有成员初始化为零(但不是任何填充字节 - 如果你需要将memset归零,则使用memset).
但是你应该知道,对于大型动态分配的结构,存在一些问题.
Pie*_*rre 42
使用 __FILE__
和__LINE__
调试
#define WHERE fprintf(stderr,"[LOG]%s:%d\n",__FILE__,__LINE__);
Run Code Online (Sandbox Code Playgroud)
Jas*_*ers 31
在C99
typedef struct{
int value;
int otherValue;
} s;
s test = {.value = 15, .otherValue = 16};
/* or */
int a[100] = {1,2,[50]=3,4,5,[23]=6,7};
Run Code Online (Sandbox Code Playgroud)
And*_*ett 28
一旦我的配偶和我重新定义返回找到一个棘手的堆栈损坏错误.
就像是:
#define return DoSomeStackCheckStuff, return
Run Code Online (Sandbox Code Playgroud)
Eva*_*ran 22
我喜欢拥有动态大小对象的"struct hack".这个网站也很好地解释了它(尽管它们引用了C99版本,你可以在其中编写"str []"作为结构的最后一个成员).你可以像这样创建一个字符串"object":
struct X {
int len;
char str[1];
};
int n = strlen("hello world");
struct X *string = malloc(sizeof(struct X) + n);
strcpy(string->str, "hello world");
string->len = n;
Run Code Online (Sandbox Code Playgroud)
在这里,我们在堆上分配了一个类型为X的结构,其大小为int(对于len),加上"hello world"的长度加1(因为str 1包含在sizeof(X)中).
当您希望在同一块中的某些可变长度数据之前有一个"标题"时,它通常很有用.
Bri*_*ndy 17
面向对象的代码用C,通过模拟类.
只需创建一个结构和一组函数,这些函数将指向该结构的指针作为第一个参数.
小智 16
代替
printf("counter=%d\n",counter);
Run Code Online (Sandbox Code Playgroud)
使用
#define print_dec(var) printf("%s=%d\n",#var,var);
print_dec(counter);
Run Code Online (Sandbox Code Playgroud)
Evi*_*ach 14
使用愚蠢的宏技巧使记录定义更容易维护.
#define COLUMNS(S,E) [(E) - (S) + 1]
typedef struct
{
char studentNumber COLUMNS( 1, 9);
char firstName COLUMNS(10, 30);
char lastName COLUMNS(31, 51);
} StudentRecord;
Run Code Online (Sandbox Code Playgroud)
Ste*_*off 11
用于创建一个在所有模块中只读的变量,除了它声明的变量:
// Header1.h:
#ifndef SOURCE1_C
extern const int MyVar;
#endif
Run Code Online (Sandbox Code Playgroud)
// Source1.c:
#define SOURCE1_C
#include Header1.h // MyVar isn't seen in the header
int MyVar; // Declared in this file, and is writeable
Run Code Online (Sandbox Code Playgroud)
// Source2.c
#include Header1.h // MyVar is seen as a constant, declared elsewhere
Run Code Online (Sandbox Code Playgroud)
位移仅定义为移位量31(在32位整数上).
如果你想要一个需要使用更高移位值的计算移位,你会怎么做?以下是Theora视频编解码器的用法:
unsigned int shiftmystuff (unsigned int a, unsigned int v)
{
return (a>>(v>>1))>>((v+1)>>1);
}
Run Code Online (Sandbox Code Playgroud)
或者更具可读性:
unsigned int shiftmystuff (unsigned int a, unsigned int v)
{
unsigned int halfshift = v>>1;
unsigned int otherhalf = (v+1)>>1;
return (a >> halfshift) >> otherhalf;
}
Run Code Online (Sandbox Code Playgroud)
以上面显示的方式执行任务比使用这样的分支更快:
unsigned int shiftmystuff (unsigned int a, unsigned int v)
{
if (v<=31)
return a>>v;
else
return 0;
}
Run Code Online (Sandbox Code Playgroud)
声明用于实现有限状态机的函数的指针数组.
int (* fsm[])(void) = { ... }
Run Code Online (Sandbox Code Playgroud)
最令人高兴的是,强制每个刺激/状态检查所有代码路径都很简单.
在嵌入式系统中,我经常将ISR映射到指向这样的表并根据需要对其进行监视(在ISR之外).
另一个不错的预处理器"技巧"是使用"#"字符来打印调试表达式.例如:
#define MY_ASSERT(cond) \
do { \
if( !(cond) ) { \
printf("MY_ASSERT(%s) failed\n", #cond); \
exit(-1); \
} \
} while( 0 )
Run Code Online (Sandbox Code Playgroud)
编辑:下面的代码仅适用于C++.感谢smcameron和Evan Teran.
是的,编译时断言总是很好.它也可以写成:
#define COMPILE_ASSERT(cond)\
typedef char __compile_time_assert[ (cond) ? 0 : -1]
Run Code Online (Sandbox Code Playgroud)
#if TESTMODE == 1
debug=1;
while(0); // Get attention
#endif
Run Code Online (Sandbox Code Playgroud)
while(0); 对程序没有任何影响,但是编译器会发出一个关于"这无所事事"的警告,这足以让我去查看有问题的行,然后看看我想引起注意的真正原因.
我是xor hacks的粉丝:
交换2个没有第三个临时指针的指针:
int * a;
int * b;
a ^= b;
b ^= a;
a ^= b;
Run Code Online (Sandbox Code Playgroud)
或者我真的很喜欢只有一个指针的xor链表.(http://en.wikipedia.org/wiki/XOR_linked_list)
链表中的每个节点都是前一节点和下一节点的Xor.要遍历,可以通过以下方式找到节点的地址:
LLNode * first = head;
LLNode * second = first.linked_nodes;
LLNode * third = second.linked_nodes ^ first;
LLNode * fourth = third.linked_nodes ^ second;
Run Code Online (Sandbox Code Playgroud)
等等
或向后移动:
LLNode * last = tail;
LLNode * second_to_last = last.linked_nodes;
LLNode * third_to_last = second_to_last.linked_nodes ^ last;
LLNode * fourth_to_last = third_to_last.linked_nodes ^ second_to_last;
Run Code Online (Sandbox Code Playgroud)
等等
虽然不是非常有用(你不能从任意节点开始遍历),但我发现它非常酷.
这一本书来自"足够的绳索射击自己的脚":
在标题声明中
#ifndef RELEASE
# define D(x) do { x; } while (0)
#else
# define D(x)
#endif
Run Code Online (Sandbox Code Playgroud)
在您的代码位置测试语句,例如:
D(printf("Test statement\n"));
Run Code Online (Sandbox Code Playgroud)
do/while有助于宏的内容扩展到多个语句.
只有在未使用编译器的'-D RELEASE'标志时才会打印该语句.
你可以,例如.将标志传递给makefile等.
不知道这在Windows中是如何工作的,但在*nix中效果很好
小智 3
Rusty 实际上在ccan中生成了一整套构建条件,查看构建断言模块:
#include <stddef.h>
#include <ccan/build_assert/build_assert.h>
struct foo {
char string[5];
int x;
};
char *foo_string(struct foo *foo)
{
// This trick requires that the string be first in the structure
BUILD_ASSERT(offsetof(struct foo, string) == 0);
return (char *)foo;
}
Run Code Online (Sandbox Code Playgroud)
实际标题中还有许多其他有用的宏,很容易放置到位。
我尝试通过主要坚持内联函数来尽全力抵制阴暗面(和预处理器滥用)的影响,但我确实喜欢像您所描述的那样聪明、有用的宏。
归档时间: |
|
查看次数: |
111999 次 |
最近记录: |