C 语言中的一些技巧

指定初始化


除了我们常用的静态初始化数组:

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

C99 标准支持一种更为直观的方式来初始化:结构体、联合体、数组;

初始化数组

当我们需要根据一组 #define 来保持某种映射关系的同步更新时,我们可以如此初始化数组:

// 一组错误码的定义
#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"
    /* ... */
};

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


结构体和联合体初始化

用结构体与联合体的字段名称来初始化数据是非常有用的,假设我们定义如下:

struct point {
    int x;
    int y;
    int z;
}

我们可以如此初始化:

struct point p = {x = 3, y = 4, z = 5};

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


利用预处理进行编译断言


我们通常希望程序在编译的时候能够进行条件检查的断言,而不是在运行时。但是,C99 标准还不支持任何编译时的断言。我们可以利用预处理生成代码,实现编译阶段的断言。这些端木只有在某些条件成立时,才会通过编译。
通常的做法是建立一个大小为负大小的数组或结构体。

/*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)为零值,那么试图生成一个负大小的结构图,就会产生编译错误;

如果任何某假设条件能够静态的编译,那么它就可以在编译的时候断言;
假设,我们要判断 Total 的数量是否小于 32,可以如下使用:

STATIC_ASSERT(Total <= 32)

扩展为:

(void)sizeof(struct { int:-!(Total <= 32) })
  • Total 确实小于 32:那么 -!(Total <= 32) 等于0,所以这行代码相当于:
    ```(void)sizeof(struct { int: 0 }) // `这是合法的``;
  • Total 大于 32:那么 -!(Total <= 32) 等于 -1,所以这行代码为:
    (void)sizeof(struct { int: -1 } )
    因为位宽位负数,编译肯定会失败;

宏列表


C 中的一个惯用方法:有一个已命名的实体列表,需要为它们中的每个建立一个函数,将它们中的每个初始化,并在不同的代码模块中扩展它们的名字。其工作方式如下:

#define FLAG_LIST(_)      \
    _(InWorklist)                  \
    _(EmittedAtUses)          \
    _(LoopInvariant)            \
    _(Commutative)             \
    _(Movable)                     \
    _(Lowered)                     \
    _(Guard)

定义了一个带参的宏 FLAG_LIST(_),这个参数本身也是一个宏名,上述的列表如何使用呢?

宏列表在枚举中的使用

假设我们定义了一个带参的宏名:

#define DEFINE_FLAG(flag) flag,
   enum Flag {
       None = 0,
       FLAG_LIST(DEFINE_FLAG)
       Total
   };
#undef DEFINE_FLAG

对于 enum Flag 做扩展如下:

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 如下:

enum Flag {
        None = 0,
        InWorklist,
        EmittedAtUses,
        LoopInvariant,
        Commutative,
        Movable,
        Lowered,
        Guard,
        Total
    };

利用宏列表生成函数

#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
  • 第一层扩展:
FLAG_ACCESSOR(InWorklist)
FLAG_ACCESSOR(EmittedAtUses)
FLAG_ACCESSOR(LoopInvariant)
FLAG_ACCESSOR(Commutative)
FLAG_ACCESSOR(Movable)
FLAG_ACCESSOR(Lowered)
FLAG_ACCESSOR(Guard)
  • 第二次扩展:只扩展第一个宏:
bool is##InWorklist() const {
    return hasFlags(1 << InWorklist);
}
void set##InWorklist() {
    JS_ASSERT(!hasFlags(1 << InWorklist));
    setFlags(1 << InWorklist);
}
void setNot##InWorklist() {
    JS_ASSERT(hasFlags(1 << InWorklist));
    removeFlags(1 << InWorklist);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值