很酷的C语言技巧

转载 2012年03月29日 14:06:34

转自:http://blog.jobbole.com/16035/

C语言常常让人觉得它所能表达的东西非常有限。它不具有类似第一级函数和模式匹配这样的高级功能。但是C非常简单,并且仍然有一些非常有用的语法技巧和功能,只是没有多少人知道罢了。

指定的初始化

很多人都知道像这样来静态地初始化数组:

1
int fibs[] = {1, 1, 2, 3, 5};

C99标准实际上支持一种更为直观简单的方式来初始化各种不同的集合类数据(如:结构体,联合体和数组)。

 

数组

我们可以指定数组的元素来进行初始化。这非常有用,特别是当我们需要根据一组#define来保持某种映射关系的同步更新时。来看看一组错误码的定义,如:

1
2
3
4
5
6
7
8
9
10
/* 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
/* ... */

现在,假设我们想为每个错误码提供一个错误描述的字符串。为了确保数组保持了最新的定义,无论头文件做了任何修改或增补,我们都可以用这个数组指定的语法。

1
2
3
4
5
6
7
8
9
10
11
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"
    /* ... */
};

这样就可以静态分配足够的空间,且保证最大的索引是合法的,同时将特殊的索引初始化为指定的值,并将剩下的索引初始化为0。

 

结构体与联合体

1
用结构体与联合体的字段名称来初始化数据是非常有用的。假设我们定义:
1
2
3
4
5
struct point {
    int x;
    int y;
    int z;
}
1
然后我们这样初始化struct point:
1
struct point p = {.x = 3, .y = 4, .z = 5};

当我们不想将所有字段都初始化为0时,这种作法可以很容易的在编译时就生成结构体,而不需要专门调用一个初始化函数。

对联合体来说,我们可以使用相同的办法,只是我们只用初始化一个字段。

 

宏列表

C中的一个惯用方法,是说有一个已命名的实体列表,需要为它们中的每一个建立函数,将它们中的每一个初始化,并在不同的代码模块中扩展它们的名字。这在Mozilla的源码中经常用到,我就是在那时学到这个技巧的。例如,在我去年夏天工作的那个项目中,我们有一个针对每个命令进行标记的宏列表。其工作方式如下:

1
2
3
4
5
6
7
8
#define FLAG_LIST(_)                   \
    _(InWorklist)                      \
    _(EmittedAtUses)                   \
    _(LoopInvariant)                   \
    _(Commutative)                     \
    _(Movable)                         \
    _(Lowered)                         \
    _(Guard)

它定义了一个FLAG_LIST宏,这个宏有一个参数称之为 _ ,这个参数本身是一个宏,它能够调用列表中的每个参数。举一个实际使用的例子可能更能直观地说明问题。假设我们定义了一个宏DEFINE_FLAG,如:

1
2
3
4
5
6
7
#define DEFINE_FLAG(flag) flag,
   enum Flag {
       None = 0,
       FLAG_LIST(DEFINE_FLAG)
       Total
   };
#undef DEFINE_FLAG

对FLAG_LIST(DEFINE_FLAG)做扩展能够得到如下代码:

1
2
3
4
5
6
7
8
9
10
11
enum Flag {
        None = 0,
        DEFINE_FLAG(InWorklist)
        DEFINE_FLAG(EmittedAtUses)
        DEFINE_FLAG(LoopInvariant)
        DEFINE_FLAG(Commutative)
        DEFINE_FLAG(Movable)
        DEFINE_FLAG(Lowered)
        DEFINE_FLAG(Guard)
        Total
    };

接着,对每个参数都扩展DEFINE_FLAG宏,这样我们就得到了enum如下:

1
2
3
4
5
6
7
8
9
10
11
enum Flag {
        None = 0,
        InWorklist,
        EmittedAtUses,
        LoopInvariant,
        Commutative,
        Movable,
        Lowered,
        Guard,
        Total
    };

接着,我们可能要定义一些访问函数,这样才能更好的使用flag列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#define FLAG_ACCESSOR(flag) \
bool is##flag() const {\
    return hasFlags(1 << flag);\
}\
void set##flag() {\
    JS_ASSERT(!hasFlags(1 << flag));\
    setFlags(1 << flag);\
}\
void setNot##flag() {\
    JS_ASSERT(hasFlags(1 << flag));\
    removeFlags(1 << flag);\
}
 
FLAG_LIST(FLAG_ACCESSOR)
#undef FLAG_ACCESSOR

一步步的展示其过程是非常有启发性的,如果对它的使用还有不解,可以花一些时间在gcc –E上。

 

编译时断言

这其实是使用C语言的宏来实现的非常有“创意”的一个功能。有些时候,特别是在进行内核编程时,在编译时就能够进行条件检查的断言,而不是在运行时进行,这非常有用。不幸的是,C99标准还不支持任何编译时的断言。

但是,我们可以利用预处理来生成代码,这些代码只有在某些条件成立时才会通过编译(最好是那种不做实际功能的命令)。有各种各样不同的方式都可以做到这一点,通常都是建立一个大小为负的数组或结构体。最常用的方式如下:

1
2
3
4
5
6
7
8
9
/* Force a compilation error if condition is false, but also produce a result
 * (of value 0 and type size_t), so it can be used e.g. in a structure
 * initializer (or wherever else comma expressions aren't permitted). */
/* Linux calls these BUILD_BUG_ON_ZERO/_NULL, which is rather misleading. */
#define STATIC_ZERO_ASSERT(condition) (sizeof(struct { int:-!(condition); })    )
#define STATIC_NULL_ASSERT(condition) ((void *)STATIC_ZERO_ASSERT(condition)    )
 
/* Force a compilation error if condition is false */
#define STATIC_ASSERT(condition) ((void)STATIC_ZERO_ASSERT(condition))

如果(condition)计算结果为一个非零值(即C中的真值),即! (condition)为零值,那么代码将能顺利地编译,并生成一个大小为零的结构体。如果(condition)结果为0(在C真为假),那么在试图生成一个负大小的结构体时,就会产生编译错误。

它的使用非常简单,如果任何某假设条件能够静态地检查,那么它就可以在编译时断言。例如,在上面提到的标志列表中,标志集合的类型为uint32_t,所以,我们可以做以下断言:

1
STATIC_ASSERT(Total <= 32)

它扩展为:

1
(void)sizeof(struct { int:-!(Total <= 32) })

现在,假设Total<=32。那么-!(Total <= 32)等于0,所以这行代码相当于:

1
(void)sizeof(struct { int: 0 })

这是一个合法的C代码。现在假设标志不止32个,那么-!(Total <= 32)等于-1,所以这时代码就相当于:

1
(void)sizeof(struct { int: -1 } )

因为位宽为负,所以可以确定,如果标志的数量超过了我们指派的空间,那么编译将会失败。

很酷的C语言技巧

很酷的C语言技巧 2012/03/29 | 分类: 程序员 | 2 条评论 | 来源: 伯乐在线     | 标签: C/C++, C语言 分享到:19 英文原...
  • lionzl
  • lionzl
  • 2013年08月19日 08:38
  • 530

很酷的C语言技巧

C语言常常让人觉得它所能表达的东西非常有限。它不具有类似第一级函数和模式匹配这样的高级功能。但是C非常简单,并且仍然有一些非常有用的语法技巧和功能,只是没有多少人知道罢了。 指定的初始化 很多人都知...
  • tcx19900712
  • tcx19900712
  • 2014年01月25日 23:25
  • 612

很酷的C语言技巧

英文原文:Ryan    编译:伯乐在线 – 肖翔 C语言常常让人觉得它所能表达的东西非常有限。它不具有类似第一级函数和模式匹配这样的高级功能。但是C非常简单,并且仍然有一些非常有用的语法技...
  • xlzheng
  • xlzheng
  • 2013年12月12日 10:45
  • 308

C语言中一些很酷的技巧(cool tricks)

C语言中一些很酷的技巧(cool tricks) 来自Quora,觉得不错,就实践了一下。 1.  #if 0 ...... #endif 块中的内容不会被编译,因为注释不允许嵌套,我们可以把暂...
  • vonzhoufz
  • vonzhoufz
  • 2014年07月12日 11:32
  • 2042

让 C 程序更高效的 10 个建议及很酷的 C 语言技巧及编写高效的C程序与C代码优化

代码之美,不仅在于为一个给定问题找到解决方案,而且还在代码的简单性、有效性、紧凑性和效率(内存)。代码设计比实际执行更难 。因此,每一个程序员当用C语言编程时,都应该记着这些东西。本文向你介绍规范你的...
  • benpaobagzb
  • benpaobagzb
  • 2016年03月06日 22:01
  • 802

C语言的艺术:强大的宏

这次不讲算法了,讲一讲C语言里面一个很强大的功能:宏。 宏,是一个大家都很熟悉的概念,很多人也经常使用宏,但是用的都不怎么深,我所知道的最常见的用法就是用宏抽象数组长度。#define N 99 a...
  • restlessssh
  • restlessssh
  • 2016年05月31日 00:06
  • 2597

C 语言 switch case 优化和小技巧一发

有一种使用switch case 的情况,就是更具不同的枚举常量,去case设置一个对象不同的属性值。 比如: struct A { int p1; int p2; int p3; } a...
  • tom_221x
  • tom_221x
  • 2015年08月13日 22:51
  • 834

c语言编程一些小技巧

这编博客是为了记录自己编程的心路历程,固件编程一直是自己的短板,身为一个硬件工程师偏偏经常接到写固件的活,其实我的内心是拒绝的。但是,这想让自己的短板变成平板并不冲突~所以该好好学就好好用功。总结的只...
  • sky_ting
  • sky_ting
  • 2017年06月09日 14:06
  • 502

很酷的C语言技巧

2013/05/03 | 分类: C/C++, 开发 | 2 条评论 | 标签: C语言 分享到:42 本文由 伯乐在线 - Michael.X 翻译自 endofunctor。欢...
  • pi9nc
  • pi9nc
  • 2014年05月29日 16:28
  • 909

VC6.0调试技巧 (一)

本文来自:http://blog.csdn.net/custqi/article/details/6452970 说明: 文 章的名字可能取的比较大,毕竟本人能力有限,掌握的知识比较肤浅...
  • dddd0216
  • dddd0216
  • 2016年05月06日 11:21
  • 362
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:很酷的C语言技巧
举报原因:
原因补充:

(最多只允许输入30个字)