ber*_*rdn 141 c hidden-features
我知道所有C编译器实现背后都有一个标准,所以应该没有隐藏的功能.尽管如此,我确信所有C开发人员都有他们一直使用的隐藏/秘密技巧.
ton*_*ylo 116
更多GCC编译器的技巧,但您可以给编译器提供分支指示提示(在Linux内核中很常见)
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
Run Code Online (Sandbox Code Playgroud)
见:http://kerneltrap.org/node/4705
我喜欢这个,它也增加了一些功能的表现力.
void foo(int arg)
{
if (unlikely(arg == 0)) {
do_this();
return;
}
do_that();
...
}
Run Code Online (Sandbox Code Playgroud)
Ben*_*ins 77
int8_t
int16_t
int32_t
uint8_t
uint16_t
uint32_t
Run Code Online (Sandbox Code Playgroud)
这些是标准中的可选项,但它必须是隐藏的功能,因为人们不断重新定义它们.我曾经使用过的一个代码库(目前仍在使用)具有多个重定义,所有重定义都具有不同的标识符.大部分时间都是使用预处理器宏:
#define INT16 short
#define INT32 long
Run Code Online (Sandbox Code Playgroud)
等等.这让我想把头发拉出来. 只需使用怪异的标准整数typedef!
Ben*_*ins 73
逗号运算符未被广泛使用.它当然可以被滥用,但它也非常有用.这种用法是最常见的:
for (int i=0; i<10; i++, doSomethingElse())
{
/* whatever */
}
Run Code Online (Sandbox Code Playgroud)
但您可以在任何地方使用此运算符.注意:
int j = (printf("Assigning variable j\n"), getValueFromSomewhere());
Run Code Online (Sandbox Code Playgroud)
将评估每个语句,但表达式的值将是最后一个语句的值.
mik*_*511 63
将结构初始化为零
struct mystruct a = {0};
Run Code Online (Sandbox Code Playgroud)
这将使所有结构元素归零.
zvr*_*rba 62
函数指针.您可以使用函数指针表来实现,例如,快速间接线程代码解释器(FORTH)或字节码调度程序,或模拟类似OO的虚拟方法.
然后标准库中有隐藏的宝石,例如qsort(),bsearch(),strpbrk(),strcspn()[后两者对于实现strtok()替换很有用].
C的错误特征是带符号的算术溢出是未定义的行为(UB).因此,每当您看到一个表达式(如x + y)都是有符号的整数时,它可能会溢出并导致UB.
Fer*_*cio 52
多字符常量:
int x = 'ABCD';
Run Code Online (Sandbox Code Playgroud)
这设置x为0x41424344(或0x44434241取决于架构).
编辑:这种技术不可移植,特别是如果你序列化int.但是,创建自记录枚举功能非常有用.例如
enum state {
stopped = 'STOP',
running = 'RUN!',
waiting = 'WAIT',
};
Run Code Online (Sandbox Code Playgroud)
如果你正在查看原始内存转储并且需要确定枚举的值而不必查找它,这会使它变得更加简单.
Mot*_*tti 44
我从来没有使用过bit字段,但是对于超低级的东西来说它们听起来很酷.
struct cat {
unsigned int legs:3; // 3 bits for legs (0-4 fit in 3 bits)
unsigned int lives:4; // 4 bits for lives (0-9 fit in 4 bits)
// ...
};
cat make_cat()
{
cat kitty;
kitty.legs = 4;
kitty.lives = 9;
return kitty;
}
Run Code Online (Sandbox Code Playgroud)
这意味着sizeof(cat)可以小到sizeof(char).
Com*_*Vie 37
交错结构,如Duff的设备:
strncpy(to, from, count)
char *to, *from;
int count;
{
int n = (count + 7) / 8;
switch (count % 8) {
case 0: do { *to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
} while (--n > 0);
}
}
Run Code Online (Sandbox Code Playgroud)
Rem*_*o.D 37
C有一个标准,但不是所有的C编译器都完全兼容(我还没有看到任何完全兼容的C99编译器!).
也就是说,我更喜欢的技巧是那些非常明显且可以跨平台移植的技巧,因为它们依赖于C语义.它们通常是关于宏或位算术的.
例如:在不使用临时变量的情况下交换两个无符号整数:
...
a ^= b ; b ^= a; a ^=b;
...
Run Code Online (Sandbox Code Playgroud)
或"扩展C"代表有限状态机,如:
FSM {
STATE(x) {
...
NEXTSTATE(y);
}
STATE(y) {
...
if (x == 0)
NEXTSTATE(y);
else
NEXTSTATE(x);
}
}
Run Code Online (Sandbox Code Playgroud)
可以使用以下宏实现:
#define FSM
#define STATE(x) s_##x :
#define NEXTSTATE(x) goto s_##x
Run Code Online (Sandbox Code Playgroud)
但总的来说,我不喜欢聪明的技巧,但是让代码不必要地复杂化(作为交换示例),我喜欢使代码更清晰并直接传达意图的代码(如FSM示例) .
DGe*_*try 33
我非常喜欢指定的初始化程序,在C99中添加(并且在gcc中支持了很长时间):
#define FOO 16
#define BAR 3
myStructType_t myStuff[] = {
[FOO] = { foo1, foo2, foo3 },
[BAR] = { bar1, bar2, bar3 },
...
Run Code Online (Sandbox Code Playgroud)
数组初始化不再依赖于位置.如果更改FOO或BAR的值,阵列初始化将自动对应于其新值.
小智 28
C99有一些很棒的任意顺序结构初始化.
struct foo{
int x;
int y;
char* name;
};
void main(){
struct foo f = { .y = 23, .name = "awesome", .x = -38 };
}
Run Code Online (Sandbox Code Playgroud)
Pyp*_*ros 27
匿名结构和数组是我最喜欢的.(参见http://www.run.montefiore.ulg.ac.be/~martin/resources/kung-f00.html)
setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, (int[]){1}, sizeof(int));
Run Code Online (Sandbox Code Playgroud)
要么
void myFunction(type* values) {
while(*values) x=*values++;
}
myFunction((type[]){val1,val2,val3,val4,0});
Run Code Online (Sandbox Code Playgroud)
它甚至可以用来实现链表...
Gia*_*sti 24
好吧......我认为C语言的一个优点是它的可移植性和标准性,所以每当我在我正在使用的实现中找到一些"隐藏的技巧"时,我尽量不使用它,因为我试图保持我的C代码尽可能标准和便携.
小智 24
gcc有很多我喜欢的C语言扩展,可以在这里找到.我最喜欢的一些是功能属性.一个非常有用的例子是format属性.如果您定义一个采用printf格式字符串的自定义函数,则可以使用此方法.如果启用此函数属性,gcc将对您的参数进行检查,以确保您的格式字符串和参数匹配,并在适当时生成警告或错误.
int my_printf (void *my_object, const char *my_format, ...)
__attribute__ ((format (printf, 2, 3)));
Run Code Online (Sandbox Code Playgroud)
kol*_*vra 24
我第一次见到时"震惊"我的(隐藏)功能是关于printf.此功能允许您使用变量格式化格式说明符本身.寻找代码,你会看到更好的:
#include <stdio.h>
int main() {
int a = 3;
float b = 6.412355;
printf("%.*f\n",a,b);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
*角色达到了这个效果.
phi*_*ant 19
编译时断言,如此处所述.
//--- size of static_assertion array is negative if condition is not met
#define STATIC_ASSERT(condition) \
typedef struct { \
char static_assertion[condition ? 1 : -1]; \
} static_assertion_t
//--- ensure structure fits in
STATIC_ASSERT(sizeof(mystruct_t) <= 4096);
Run Code Online (Sandbox Code Playgroud)
kri*_*iss 16
常量字符串连接
我很惊讶没有在答案中看到它,因为我所知道的所有编译器都支持它,但许多程序员似乎忽略了它.有时它非常方便,而且不仅仅是在编写宏时.
我在当前代码中使用的用例:我#define PATH "/some/path/"在配置文件中有一个(实际上它是由makefile设置的).现在我想构建包含文件名的完整路径来打开资源.它只是去:
fd = open(PATH "/file", flags);
Run Code Online (Sandbox Code Playgroud)
而不是可怕的,但很常见的:
char buffer[256];
snprintf(buffer, 256, "%s/file", PATH);
fd = open(buffer, flags);
Run Code Online (Sandbox Code Playgroud)
请注意,常见的可怕解决方案是:
unw*_*ind 12
结构分配很酷.许多人似乎并没有意识到结构也是值,并且可以被分配,没有必要使用memcpy(),当一个简单的赋值就可以了.
例如,考虑一些虚构的2D图形库,它可能会定义一个表示(整数)屏幕坐标的类型:
typedef struct {
int x;
int y;
} Point;
Run Code Online (Sandbox Code Playgroud)
现在,你做的事情可能看起来"错误",比如编写一个创建从函数参数初始化的点的函数,并返回它,如下所示:
Point point_new(int x, int y)
{
Point p;
p.x = x;
p.y = y;
return p;
}
Run Code Online (Sandbox Code Playgroud)
这是安全的,当然很长(因为)使用struct赋值通过值复制返回值:
Point origin;
origin = point_new(0, 0);
Run Code Online (Sandbox Code Playgroud)
通过这种方式,您可以编写非常干净且面向对象的代码,所有代码都采用简单的标准C.
Fer*_*cio 12
初始化数组或枚举时,可以在初始化列表中的最后一项之后放置逗号.例如:
int x[] = { 1, 2, 3, };
enum foo { bar, baz, boom, };
Run Code Online (Sandbox Code Playgroud)
这样做是为了让你自动生成代码,你不必担心删除最后一个逗号.
INS*_*INS 10
奇怪的矢量索引:
int v[100]; int index = 10;
/* v[index] it's the same thing as index[v] */
Run Code Online (Sandbox Code Playgroud)
C编译器实现了几个标准之一.但是,拥有标准并不意味着语言的所有方面都已定义. 例如,Duff的设备是最受欢迎的"隐藏"功能,它已经变得如此受欢迎,以至于现代编译器具有特殊用途的识别代码,以确保优化技术不会破坏这种常用模式的预期效果.
一般情况下,当您在编译器使用的任何C标准的剃刀边缘上运行时,不鼓励隐藏功能或语言技巧.许多这样的技巧从一个编译器到另一个编译器不起作用,并且这些类型的功能通常会从给定制造商的一个版本的编译器套件失败到另一个版本.
破坏C代码的各种技巧包括:
当程序员对大多数C标准中指定为"编译器相关"行为的执行模型做出假设时,会出现其他问题和问题.
使用sscanf时,您可以使用%n找出您应该继续阅读的位置:
sscanf ( string, "%d%n", &number, &length );
string += length;
Run Code Online (Sandbox Code Playgroud)
显然,你不能添加另一个答案,所以我会在这里添加第二个答案,你可以使用"&&"和"||" 作为条件:
#include <stdio.h>
#include <stdlib.h>
int main()
{
1 || puts("Hello\n");
0 || puts("Hi\n");
1 && puts("ROFL\n");
0 && puts("LOL\n");
exit( 0 );
}
Run Code Online (Sandbox Code Playgroud)
此代码将输出:
Hi ROFL
使用INT(3)在代码中设置断点是我一直以来的最爱
使用枚举进行编译时假设检查:愚蠢的例子,但对于具有编译时可配置常量的库非常有用.
#define D 1
#define DD 2
enum CompileTimeCheck
{
MAKE_SURE_DD_IS_TWICE_D = 1/(2*(D) == (DD)),
MAKE_SURE_DD_IS_POW2 = 1/((((DD) - 1) & (DD)) == 0)
};
Run Code Online (Sandbox Code Playgroud)
我最近发现了0个位域.
struct {
int a:3;
int b:2;
int :0;
int c:4;
int d:3;
};
Run Code Online (Sandbox Code Playgroud)
这将给出一个布局
000aaabb 0ccccddd
Run Code Online (Sandbox Code Playgroud)
而不是没有:0;
0000aaab bccccddd
Run Code Online (Sandbox Code Playgroud)
0宽度字段表示应在下一个原子实体(char)上设置以下位域
C99风格的可变参数宏,又名
#define ERR(name, fmt, ...) fprintf(stderr, "ERROR " #name ": " fmt "\n", \
__VAR_ARGS__)
Run Code Online (Sandbox Code Playgroud)
哪个会像
ERR(errCantOpen, "File %s cannot be opened", filename);
Run Code Online (Sandbox Code Playgroud)
在这里我还使用stringize运算符和字符串常量连接,其他功能我真的很喜欢.
在某些情况下,可变大小的自动变量也很有用.这些是在nC99中添加的,并且已经在gcc中支持了很长时间.
void foo(uint32_t extraPadding) {
uint8_t commBuffer[sizeof(myProtocol_t) + extraPadding];
Run Code Online (Sandbox Code Playgroud)
最终在堆栈上有一个缓冲区,其中包含固定大小的协议头和可变大小数据的空间.您可以使用alloca()获得相同的效果,但此语法更紧凑.
在调用此例程之前,您必须确保extraPadding是一个合理的值,否则您最终会破坏堆栈.你必须在调用malloc或任何其他内存分配技术之前检查参数是否健全,所以这并不是很不寻常.
我喜欢你可以制作的可变大小的结构:
typedef struct {
unsigned int size;
char buffer[1];
} tSizedBuffer;
tSizedBuffer *buff = (tSizedBuffer*)(malloc(sizeof(tSizedBuffer) + 99));
// can now refer to buff->buffer[0..99].
Run Code Online (Sandbox Code Playgroud)
还有offsetof宏,现在是ANSI C,但是在我第一次看到它的时候是一段魔法.它基本上使用address-of运算符(&)将空指针重铸作为结构变量.
GCC中的Lambda(例如匿名函数):
#define lambda(return_type, function_body) \
({ return_type fn function_body fn })
Run Code Online (Sandbox Code Playgroud)
这可以用作:
lambda (int, (int x, int y) { return x > y; })(1, 2)
Run Code Online (Sandbox Code Playgroud)
这扩展到:
({ int fn (int x, int y) { return x > y } fn; })(1, 2)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
79754 次 |
| 最近记录: |