阅读更多

3顶
0踩

编程语言

转载新闻 很酷的C语言技巧

2015-03-12 15:12 by 副主编 mengyidan1988 评论(2) 有8003人浏览
C语言常常让人觉得它所能表达的东西非常有限。它不具有类似第一级函数和模式匹配这样的高级功能。但是C非常简单,并且仍然有一些非常有用的语法技巧和功能,只是没有多少人知道罢了。

指定的初始化

很多人都知道像这样来静态地初始化数组:
int fibs[] = {1, 1, 2, 3, 5};

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

数组

我们可以指定数组的元素来进行初始化。这非常有用,特别是当我们需要根据一组#define来保持某种映射关系的同步更新时。来看看一组错误码的定义,如:
/* 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"
    /* ... */
};

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



结构体与联合体

用结构体与联合体的字段名称来初始化数据是非常有用的。假设我们定义:
struct point {
    int x;
    int y;
    int z;
}

然后我们这样初始化struct point:
struct point p = {.x = 3, .y = 4, .z = 5};

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

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

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

它定义了一个FLAG_LIST宏,这个宏有一个参数称之为 _ ,这个参数本身是一个宏,它能够调用列表中的每个参数。举一个实际使用的例子可能更能直观地说明问题。假设我们定义了一个宏DEFINE_FLAG,如:
#define DEFINE_FLAG(flag) flag,
   enum Flag {
       None = 0,
       FLAG_LIST(DEFINE_FLAG)
       Total
   };
#undef DEFINE_FLAG

对FLAG_LIST(DEFINE_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
    };

接着,我们可能要定义一些访问函数,这样才能更好的使用flag列表:
#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标准还不支持任何编译时的断言。

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

它扩展为:
(void)sizeof(struct { int:-!(Total <= 32) })

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

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

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

英文出处:endofunctor
本文由 伯乐在线 - Michael.X 翻译
  • 大小: 78.6 KB
来自: 伯乐在线
3
0
评论 共 2 条 请登录后发表评论
2 楼 xmind 2015-05-14 15:51
assert.c: In function ‘main’:
assert.c:18: error: ‘uint32_t’ undeclared (first use in this function)
assert.c:18: error: (Each undeclared identifier is reported only once
assert.c:18: error: for each function it appears in.)
assert.c:18: error: expected ‘;’ before ‘Total’
assert.c:19: error: ‘Total’ undeclared (first use in this function)
assert.c:19: error: bit-field ‘<anonymous>’ width not an integer constant
1 楼 xmind 2015-05-14 15:51
编译时断言 应该怎么写啊?

/* 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))

void main(void) {

//(void)sizeof(struct { int:-1; });
uint32_t Total = 333;
STATIC_ASSERT(Total <= 32);

}

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • 超级炫酷的C语言技巧!

    但是C非常简单,并且仍然有一些非常有用的语法技巧和功能,只是没有多少人知道罢了。一、指定的初始化很多人都知道像这样来静态地初始化数组:int fibs[] = {1, 1, 2, 3, 5};C99标准实际上支持一种更为直观简单的...

  • C99中很酷的C语言技巧

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

  • c语言里的fn,很酷的C语言技巧 - fnnn99 - OSCHINA - 中文开源技术交流社区

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

  • 超炫酷技巧!C语言代码优化的技巧

    有些处理器处理无符号unsigned 整形数的效率远远高于有符号signed整形数(这是一种很好的做法,也有利于代码具体类型的自解释)。 因此,在一个紧密循环中,声明一个int整形变量的最好方法是: ...

  • 基于STM32通过PWM驱动直流电机

    工程代码基于STM32F103C8T6,使用PWM输出驱动电机,电机驱动使用TB6612,通过按键控制电机速度,并且速度通过OLED显示屏进行显示 使用到的硬件:STM32F103C8T6最小系统板,四针脚OLED显示屏,直流电机,按键,TB6612电机驱动模块

  • 最新微信文章编辑器排版工具程序源码.rar

    最新微信文章编辑器排版工具程序源码.rar最新微信文章编辑器排版工具程序源码.rar最新微信文章编辑器排版工具程序源码.rar

  • 信息办公电信计费系统完整代码-netctossconformity.rar

    这个压缩包 "netctossconformity.rar" 包含了一套电信计费系统的完整代码,它是针对计算机专业学生或开发者的JSP源码资料。这套系统的设计旨在为电信运营商提供一个可靠、高效的计费解决方案。通常,这种系统会涉及到用户账户管理、费用计算、账单生成、支付处理以及数据报告等功能模块。在内容上,该资料包可能包括了前端用户界面和后端服务器逻辑的源代码,使用JSP(Java Server Pages)技术实现。前端可能会涵盖用户注册、登录、查看账单和支付历史等操作的用户界面,而后端则包含数据库交互、计费算法、用户验证和安全性措施等关键功能。对于学习者来说,这个资料包是一个宝贵的实践资源,可以帮助他们理解电信计费系统的工作原理,以及如何运用JSP技术开发复杂的商业应用。通过分析这些代码,可以加深对Java Web技术栈的理解,包括但不限于Servlet API、JDBC(Java Database Connectivity)、HTML/CSS/JavaScript,以及可能涉及的框架如Spring或Struts。此外,这个资料包也可能含有一些文档,例如系统设计说明、代码结构介绍、部

  • 交流电桥实验(95).zip

    交流电桥实验(95).zip

  • matlab基于四自由度机械臂的轨迹规划源码.zip

    优秀源码设计,详情请查看资源内容

  • MRU3-2保护技术简化电压继电器对称分量评估 SEG

    MRU3-2保护技术简化电压继电器对称分量评估 MRU3-2保护技术简单电压继电器,具有对称分量评估功能

  • 图3-7.zip

    图3-7.zip

  • 基于matlab开发的多元散射校正和变量标准化Matlab处理程序,可以对建模前的原始数据进行校正、处理.rar

    基于matlab开发的多元散射校正和变量标准化Matlab处理程序,可以对建模前的原始数据进行校正、处理.rar

  • 新建 文本文档.txt

    新建 文本文档.txt

  • 最新获取QQ微信头像橘头像阁PHP源码下载.rar

    最新获取QQ微信头像橘头像阁PHP源码下载.rar最新获取QQ微信头像橘头像阁PHP源码下载.rar

  • 3D模型004,可用于建模、GIS、BIM、CIM学习

    3D模型004,可用于建模、GIS、BIM、CIM学习

  • node-v6.7.0.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

  • 其他类别动态显示JSP服务器内存的Ajax程序 图像版-systemjc.rar

    在当今的Web开发中,实时监控服务器性能是至关重要的。其中,动态显示JSP服务器内存的Ajax程序图像版_systemjc.rar是一个为开发者和系统管理员提供便利的计算机专业JSP源码资料包。这个资料包的核心是一个高效的Ajax程序,它能够实时获取并展示JSP服务器的内存使用情况。通过使用这一工具,用户可以在一个直观的界面上看到服务器内存的使用率、已使用内存、可用内存等关键信息,而这一切都无需刷新页面。这得益于Ajax技术的强大功能,它允许在后台与服务器进行异步通信,从而获取最新的数据并更新前端界面。这个资料包不仅包含了完整的源代码,还提供了详细的文档和注释,使得即使是初级的JSP开发者也能够轻松地部署和使用。此外,它的图像版设计使得数据的展示更加直观和友好,帮助用户快速识别任何潜在的问题。总的来说,动态显示JSP服务器内存的Ajax程序图像版_systemjc.rar是一个强大、实用且易于使用的JSP源码资料包,它为实时监控服务器性能提供了一个有效的解决方案。重新回答||

  • 引领AI视频生成技术新潮流之Sora AI

    Sora AI,作为OpenAI继ChatGPT之后的又一重磅力作,以其独特的文本到视频模型技术,在AI内容创作领域掀起了一场革命性的风暴。本文将详细探讨Sora AI的技术特点、应用场景以及未来发展趋势,展现其在视频制作、广告、教育和娱乐产业中的巨大潜力。 Sora AI作为OpenAI的又一力作,以其独特的文本到视频模型技术引领了AI视频生成技术的新潮流。通过深入了解Sora AI的技术特点、应用场景和未来发展趋势,我们可以看到它在视频制作、广告、教育和娱乐产业中的巨大潜力和广阔前景。然而,我们也应清醒地认识到,技术的发展总是伴随着挑战和问题,只有不断探索和解决这些问题,才能让Sora AI更好地服务于人类社会。

  • 新闻文章NTsky新闻发布系统 v1.0稳定版-18655.rar

    NTsky新闻发布系统 v1.0稳定版_18655.rar是一款专为计算机专业人士设计的JSP源码资料包。这款资料包的主要功能是帮助用户快速、高效地管理和发布新闻信息。它采用了先进的JSP技术,结合了数据库管理系统,使得新闻的发布和管理变得简单而直观。该资料包包含了完整的源代码,用户可以根据自己的需求进行修改和优化。同时,它还提供了详细的使用说明和技术文档,即使是对JSP技术不太熟悉的用户,也能快速上手。此外,该资料包还具有良好的用户界面设计,使得操作更加人性化。NTsky新闻发布系统 v1.0稳定版_18655.rar的另一个优点是其稳定性。经过多次测试和优化,该系统已经达到了稳定运行的状态,可以满足用户在各种环境下的使用需求。无论是在个人电脑上,还是在服务器上,都能稳定运行。总的来说,NTsky新闻发布系统 v1.0稳定版_18655.rar是一款功能强大、操作简便、稳定性高的JSP源码资料包。无论是对于专业的计算机人士,还是对于初学者,都是一个很好的学习和使用工具。重新回答||

Global site tag (gtag.js) - Google Analytics