1 有一个鲜为人知的运算符叫”趋向于”, 写作“-->”。比如说如果要实现一个倒数的程序,我们可以定义一个变量x,然后让它趋向于0...C++有另一个更鲜为人知的运算符叫做“快速趋向于”,比如同样是从10到0,这里这么写
#include <stdio.h>
int main(void)
{
int x = 10;
while (0 <---- x) {
printf("%d ", x);
}
return 0;
}
8 6 4 2
作者:Geek An
链接:https://www.zhihu.com/question/27417946/answer/36617347
来源:知乎
2 以下等式皆成立:
a[5] == 5[a];
"ABCD"[2] == 2["ABCD"] == 'C';
*(a + 5) == *(5 + a)
下面这个打印是否让你惊奇?
#include <stdio.h>
int main(void)
{
int x = 5;
printf("%d and ", sizeof(x++)); // note 1
printf("%d\n", x); // note 2
return 0;
}
它会打印出:
4 and 5
因为 sizeof 是编译时行为,运行时不会执行
作者:Geek An
链接:https://www.zhihu.com/question/27417946/answer/36617347
来源:知乎
3 代码来自参与过的开源工程
ovs,OpenFlow的业界标杆)
#define OFPACTS \
/* Output. */ \
OFPACT(OUTPUT, ofpact_output, ofpact, "output") \
OFPACT(GROUP, ofpact_group, ofpact, "group")
#define OFPACT(ENUM, STRUCT, MEMBER, NAME) \
BUILD_ASSERT_DECL(offsetof(struct STRUCT, ofpact) == 0); \
\
enum { OFPACT_##ENUM##_RAW_SIZE \
= (offsetof(struct STRUCT, MEMBER) \
? offsetof(struct STRUCT, MEMBER) \
: sizeof(struct STRUCT)) }; \
\
static inline struct STRUCT * \
ofpact_get_##ENUM(const struct ofpact *ofpact) \
{ \
ovs_assert(ofpact->type == OFPACT_##ENUM); \
return ALIGNED_CAST(struct STRUCT *, ofpact); \
}
OFPACTS
#undef OFPACT
看懂以上代码的功能了吗?它定义了一些字段,然后再通过宏来批量生成函数和enum,狂拽酷炫!
学名叫做x macro,是节省冗余代码利器,好处是非常好用,跟机关枪一样;坏处是懂的人不多,大家看到一个没有被索引的ofpact_get_GROUP很容易就进入痴呆状态。
完整版的黑魔法五可以点这里
cowry/x_macro.c at master · geekan/cowry · GitHub
4
C语言还有另一个更加鲜为人知的运算符,叫做蝌蚪运算符(tadpole operator),用于实现单目的加一、减一运算。
语法 含义 助记
-~y y + 1 蝌蚪游向一个值让它变大
~-y y - 1 蝌蚪离开一个值让它变小
有了这两个运算符,我们可以改写如下的代码
x = (y + 1) % 10;
x = (y + 1) * (z - 1);
x = (double)(f(y) + 1);
变为
x = -~y % 10;
x = -~y * ~-z;
x = (double)-~f(y);
减少了括号的使用,使代码更简单。
-----------
作者:武振伟
链接:https://www.zhihu.com/question/27417946/answer/49040520
来源:知乎
6
作者:justjavac
链接:https://www.zhihu.com/question/27417946/answer/36585519
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
摘自《C专家编程》 (如果你正在学习C/C++,建议你多读几遍这本书。网上有电子版,我就不给链接了。)
根据位模式构建图形图标(icon)或者图形(glyph),是一种小型的位模式映射于屏幕产生的图像。一个位代表图像上的一个像素。如果一个位被设置,那么它所代表的像素就是“亮”的。如果一个位被清除,那么它所代表的像素就是“暗”的。所以,一系列的整数值能够用于为图像编码。
类似Iconedit这样的工具就是用于绘图的,他们所输出的是一个包含一系列整型数的ASCII文件,可以被一个窗口程序所包含。它所存在的问题是程序中的图标只是一串十六进制数。
在C语言中,典型的16X16的黑白图形可能如下:
static unsigned short stopwatch[] = {
0x07C6,
0x1FF7,
0x383B,
0x600C,
0x600C,
0xC006,
0xC006,
0xDF06,
0xC106,
0xC106,
0x610C,
0x610C,
0x3838,
0x1FF0,
0x07C0,
0x0000
};
正如所看到的那样,这些C语言常量并未有提供有关图形实际模样的任何线索。
这里有一个惊人的#define定义的优雅集合,允许程序建立常量使它们看上去像是屏幕上的图形。
#define X )*2+1
#define _ )*2
#define s ((((((((((((((((0 /* For building glyphs 16 bits wide */
定义了它们之后,只要画所需要的图标或者图形等,程序会自动创建它们的十六进制模式。使用这些宏定义,程序的自描述能力大大加强,上面这个例子可以转变为:
static unsigned short stopwatch[] =
{
s _ _ _ _ _ X X X X X _ _ _ X X _ ,
s _ _ _ X X X X X X X X X _ X X X ,
s _ _ X X X _ _ _ _ _ X X X _ X X ,
s _ X X _ _ _ _ _ _ _ _ _ X X _ _ ,
s _ X X _ _ _ _ _ _ _ _ _ X X _ _ ,
s X X _ _ _ _ _ _ _ _ _ _ _ X X _ ,
s X X _ _ _ _ _ _ _ _ _ _ _ X X _ ,
s X X _ X X X X X _ _ _ _ _ X X _ ,
s X X _ _ _ _ _ X _ _ _ _ _ X X _ ,
s X X _ _ _ _ _ X _ _ _ _ _ X X _ ,
s _ X X _ _ _ _ X _ _ _ _ X X _ _ ,
s _ X X _ _ _ _ X _ _ _ _ X X _ _ ,
s _ _ X X X _ _ _ _ _ X X X _ _ _ ,
s _ _ _ X X X X X X X X X _ _ _ _ ,
s _ _ _ _ _ X X X X X _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
};
显然,与前面的代码相比,它的意思更为明显。标准的C语言具有八进制、十进制和十六进制常量,但没有二进制常量,否则的话倒是一种更为简单的绘制图形模式的方法。
7
作者:Shanicky
链接:https://www.zhihu.com/question/27417946/answer/36583624
来源:知乎
当你需要将错误码对应到错误信息时
/* Entries may not correspond to actual numbers. Some entries omitted. */
#define EINVAL 1
#define ENOMEM 2
#define EFAULT 3
/* ... */
#define E2BIG 7
#define EBUSY 8
/* ... */
#define ECHILD 12
/* ... */
你可以使用
char *err_strings[] = {
[0] = "Success",
[EINVAL] = "Invalid argument",
[ENOMEM] = "Not enough memory",
[EFAULT] = "Bad address",
/* ... */
[E2BIG ] = "Argument list too long",
[EBUSY ] = "Device or resource busy",
/* ... */
[ECHILD] = "No child processes"
/* ... */
};
这样的代码初始化
err_strings时,指定的元素会被初始化为对应的字符串,其余元素默认初始化为0
8
作者:we1we1
链接:https://www.zhihu.com/question/27417946/answer/36627464
来源:知乎
不知道这个「奇技淫巧」是怎么定义的,但在用C设计模块接口的时候,有很多非常实用小tricks还是值得提一下的。
...
Redis里的
sds(简易动态字符串库)
struct sdshdr {
int len;
int free;
char buf[];
};
sds sdsnewlen(const void *init, size_t initlen) {
struct sdshdr *sh;
if (init) {
sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
} else {
sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
}
if (sh == NULL) return NULL;
sh->len = initlen;
sh->free = 0;
if (initlen && init)
memcpy(sh->buf, init, initlen);
sh->buf[initlen] = '\0';
return (char*)sh->buf;
//返回了char *
}
而新申请的sds返回了char *,而不是struct sdshdr *。直接使用的也是char *。这样做的目的是为了兼容一些类似strcmp、strcat的库函数。
而要使用到sds的信息时,只需要将字符串地址减去sds的大小就可以的到sds的地址。
例如:
static inline size_t sdslen(const sds s) {
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); //获取sds的地址
return sh->len; //返回长度
}