我知道在所有C编译器实现背后都有一个标准,因此不应该有任何隐藏功能。尽管如此,我相信所有的C开发人员都有他们经常使用的隐藏/秘密技巧。
我知道在所有C编译器实现背后都有一个标准,因此不应该有任何隐藏功能。尽管如此,我相信所有的C开发人员都有他们经常使用的隐藏/秘密技巧。
我喜欢__LINE__
和__FILE__
。请参见:http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
在GCC中使用Lambda表达式(例如匿名函数):
#define lambda(return_type, function_body) \
({ return_type fn function_body fn })
这可以用作:
lambda (int, (int x, int y) { return x > y; })(1, 2)
这可以扩展为:
({ int fn (int x, int y) { return x > y } fn; })(1, 2)
我喜欢你能够创建可变大小的结构:
typedef struct {
unsigned int size;
char buffer[1];
} tSizedBuffer;
tSizedBuffer *buff = (tSizedBuffer*)(malloc(sizeof(tSizedBuffer) + 99));
// can now refer to buff->buffer[0..99].
还有一个在ANSI C中现在已经存在的宏offsetof,但第一次看到时它就像是一种魔法。它基本上使用取地址运算符(&)将空指针重新解释为一个结构变量。
1
以使用灵活数组成员。) - Jonathan Leffler清除输入缓冲区时不能使用 fflush(stdin)
。正确的方法如下:scanf("%*[^\n]%*c")
,这将丢弃输入缓冲区中的所有内容。
在进行了15年以上的C编程之后,我才发现这一点:
struct SomeStruct
{
unsigned a : 5;
unsigned b : 1;
unsigned c : 7;
};
位域!冒号后面的数字是成员所需的位数,成员被打包到指定类型中,因此如果unsigned是16位,则上述内容将如下所示:
xxxc cccc ccba aaaa
Skizz
有一次我在代码中看到了这个,然后问它是做什么的:
hexDigit = "0123456789abcdef"[someNybble];
unsigned char bar[100];
unsigned char *foo = bar;
unsigned char blah = 42[foo];
使用不寻常的类型转换进行类型转换。虽然不是隐藏功能,但它相当棘手。
示例:
如果您想知道编译器如何存储浮点数,请尝试以下操作:
uint32_t Int;
float flt = 10.5; // say
Int = *(uint32_t *)&flt;
printf ("Float 10.5 is stored internally as %8X\n", Int);
或者
float flt = 10.5; // say
printf ("Float 10.5 is stored internally as %8X\n", *(uint32_t *)&flt);
注意巧妙使用类型转换。将变量的地址(这里是&flt)转换为所需类型(这里是(uint32_t *)),并提取其内容(应用“*”)。
同样地,此方法也适用于表达式的另一侧:
*(float *)&Int = flt;
也可以使用union来实现:
typedef union
{
uint32_t Int;
float flt;
} FloatInt_type;
intptr_t用于声明指针类型的变量。它是C99特定的类型,需要在stdint.h中声明。
Steve Webb提到了__LINE__
和__FILE__
宏。这让我想起了在我的上一份工作中,我曾经利用它们来进行内存日志记录。
当时我正在开发一个设备,该设备没有可用的端口将日志信息从设备传递到用于调试的PC上。可以使用断点来停止程序并使用调试器了解程序状态,但是系统跟踪上没有任何信息。
由于所有调试日志调用实际上都是单个全局宏,因此我们更改了该宏以将文件名和行号转储到全局数组中。该数组包含一系列文件名和行号,显示哪些调试调用被调用,从而给出执行跟踪的公平想法(虽然不是实际的日志消息)。可以通过调试器暂停执行,将这些字节转储到本地文件,然后使用脚本将此信息映射到代码库。这是可能的,因为我们有严格的编码指南,所以我们可以在一个文件中对日志记录机制进行更改。