例如,我最近在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的倍数,则根本不生成运行时代码 .
我所知道的另一个技巧来自“图形宝石”一书,它允许单个头文件在一个模块中声明和初始化变量,而在使用该模块的其他模块中,仅将它们声明为外部 .
#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编程技巧是什么?
30 回答
while(0);对程序没有任何影响,但是编译器会发出一个关于“这无所事事”的警告,这足以让我去查看有问题的行,然后看看我想引起注意的真正原因 .
这类东西的两本好的源书是The Practice of Programming和Writing Solid Code . 其中一个(我不记得哪个)说:首选enum到#define你可以,因为枚举被编译器检查 .
我不会用它,但提到Duff的设备让我想起了this article关于在C中实现Coroutines的问题 . 它总是让我笑一笑,但我相信它可能会有用一段时间 .
我们的代码库有一个类似的技巧
它允许在调试模式下跟踪内存泄漏 . 我一直认为这很酷 .
代替
使用
这一本书来自“足够的绳索射击自己的脚”:
在 Headers 声明中
在您的代码位置测试语句,例如:
do / while有助于宏的内容扩展到多个语句 .
只有在未使用编译器的'-D RELEASE'标志时才会打印该语句 .
你可以,例如 . 将标志传递给您的makefile等 .
不知道这在Windows中是如何工作的,但在* nix中效果很好
位移仅定义为移位量31(在32位整数上) .
如果你想要一个需要使用更高移位值的计算移位,你会怎么做?以下是Theora视频编解码器的用法:
或者更具可读性:
以上面显示的方式执行任务比使用这样的分支更快:
我是xor hacks的粉丝:
交换2个没有第三个临时指针的指针:
或者我真的很喜欢只有一个指针的xor链表 . (http://en.wikipedia.org/wiki/XOR_linked_list)
链表中的每个节点都是前一节点和下一节点的Xor . 要遍历,可以通过以下方式找到节点的地址:
等等
或者向后移动:
等等
虽然不是非常有用(你不能从任意节点开始遍历),但我发现它非常酷 .
我认为userdata指针的使用非常简洁 . 时尚潮流正在逐渐消失 . 它不是一个C功能,但在C中很容易使用 .
下面是一个示例,说明如何让C代码完全不知道运行应用程序的HW实际使用情况 . main.c进行设置,然后可以在任何编译器/ arch上实现自由层 . 我认为抽象C代码有点简洁,所以它不是特定的 .
在这里添加一个完整的可编译示例 .
填写空白,以便输出中既不会出现hello也不会出现hi .
ans:
fclose(stdout)
我使用X-Macros来让预编译器生成代码 . 它们对于在一个地方定义错误值和相关的错误字符串特别有用,但它们可以远远超出它 .
另一个不错的预处理器“技巧”是使用“#”字符来打印调试表达式 . 例如:
edit: 以下代码仅适用于C . 感谢smcameron和Evan Teran .
是的,编译时断言始终很好 . 它也可以写成:
Rusty实际上在ccan中生成了一整套构建条件,检查构建断言模块:
实际 Headers 中还有许多其他有用的宏,很容易就位 .
我尽我所能通过坚持内联函数来抵制黑暗的一面(以及预处理器滥用),但我确实喜欢你所描述的聪明,有用的宏 .
声明用于实现有限状态机的函数的指针数组 .
最令人愉悦的优点是强制每个都很简单刺激/状态来检查所有代码路径 .
在嵌入式系统中,我经常将ISR映射到指向这样的表并根据需要(在ISR之外)进行监视 .
不是特定于C,但我一直很喜欢XOR运算符 . 它可以做的一件很酷的事情是“没有临时值的交换”:
结果:
C99使用匿名数组提供了一些非常酷的东西:
Removing pointless variables
变
Passing a Variable Amount of Arguments
Static linked lists
任何我肯定还有很多其他很酷的技术我都没有想过 .
使用
__FILE__
和__LINE__
进行调试见"Hidden features of C"问题 .
我喜欢使用
= {0};
初始化结构而无需调用memset .这会将struct(或数组)的所有成员初始化为零(但不是任何填充字节 - 如果你需要将memset归零,则使用memset) .
但你应该知道有some issues with this for large, dynamically allocated structures .
我喜欢"struct hack",因为它有一个动态大小的对象 . This site也很好地解释了(虽然它们引用了C99版本,你可以将"str[]"写为结构的最后一个成员) . 你可以像这样制作一个字符串"object":
在这里,我们在堆上分配了一个类型为X的结构,其大小为int(对于len),加上"hello world"的长度加1(因为str 1包含在sizeof(X)中) .
当您希望在同一块中的某些可变长度数据之前有一个“ Headers ”时,它通常很有用 .
用于创建在所有模块中只读的变量,但声明的变量除外:
我喜欢列表中使用 container_of 的概念 . 基本上,您不需要为列表中的每个结构指定
next
和last
字段 . 而是将列表结构标头附加到实际链接的项目 .查看
include/linux/list.h
了解真实案例 .在阅读Quake 2源代码时,我提出了类似这样的事情:
(或多或少,我现在没有代码检查它) .
从那时起,一个创造性地使用预处理器的新世界在我眼前展开 . 我不再只包含 Headers ,而是包括整个代码块(它可以提高可重用性):-p
谢谢John Carmack!的xD
一旦我的配偶和我重新定义返回找到一个棘手的堆栈损坏错误 .
就像是:
如果我们谈论c技巧,我最喜欢的是循环展开Duff's Device!我只是在等待合适的机会来帮我实际使用它...
在C99
面向对象的代码与C,通过模拟类 .
只需创建一个结构和一组函数,这些函数将指向该结构的指针作为第一个参数 .
使用愚蠢的宏技巧使记录定义更容易维护 .
有趣的宏: