阅读更多

3顶
0踩

编程语言

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

2015-03-12 15:12 by 副主编 mengyidan1988 评论(2) 有8041人浏览
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整形变量的最好方法是: ...

  • 96_基于Android的美食推荐 APP-源码.zip

    提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

  • 南京大学分布式系统课程实验.zip

    南京大学分布式系统课程实验.zip

  • mysql开发教程&案例&相关项目概要介绍.docx

    MySQL作为一款广泛使用的开源关系型数据库管理系统,在软件开发中扮演着核心角色。以下是对MySQL开发教程、案例及相关项目概要的综合介绍: ### MySQL开发教程概览 **黑马程序员MySQL全套教程** - **目标群体**:适合零基础至进阶水平的学习者,特别是希望从事IT行业并期望通过MySQL技能提高薪资的学员。 - **课程内容**: - **学前须知**:要求学员具备Python基础知识及网络多线程知识。 - **开发工具**:使用Navicat(MySQL的图形化界面客户端)及MySQL自身的客户端与服务端(推荐版本5.0左右)。 - **课程重点**:涵盖MySQL的图形界面操作、终端CRUD操作,以及通过Python与MySQL交互实现黑马订单管理系统的开发,包括查询、增加、删除、修改订单等功能。 - **课程目标**: - 理解数据库分类、MySQL的应用场景。 - 安装并使用Navicat进行数据操作。 - 安装MySQL并进行终端的数据库操作。 - Python与MySQL的交互编程。 - 完成黑马订单管理系统的实战项目。

  • 儿童节 python庆祝代码简要介绍-20240601.docx

    为了庆祝儿童节,我们可以用Python编写一个简单而有趣的程序,比如一个猜数字的小游戏,这非常适合孩子们玩耍。游戏规则很简单:计算机随机生成一个1到50之间的数字,玩家尝试猜测这个数字,计算机会根据玩家的输入给出提示(太高、太低或猜对了),直到猜中为止。下面是一个实现这个小游戏的Python代码示例: ```python import random print("我想了一个1到50之间的数字,你能猜到是多少吗?") # 生成一个1到50之间的随机数作为答案 secret_number = random.randint(1, 50) guesses_taken = 0 # 开始猜测循环 while True: try: # 获取玩家的输入并确保它是一个有效的整数 guess = int(input("请输入你的猜测:")) guesses_taken += 1 if guess < secret_number: print

  • MySQLTuner,一个用Perl编写的脚本,它允许你快速审查MySQL安装情况,并进行调整以提高性能和稳定性

    MySQLTuner是一个用Perl编写的脚本,它允许你快速审查MySQL安装情况,并进行调整以提高性能和稳定性。它会检索当前的配置变量和状态数据,并以简洁的格式呈现,同时提供一些基本的性能建议。

  • JAVA+SQL办公自动化系统(源代码+论文+外文翻译)

    JAVA+SQL办公自动化系统(源代码+论文+外文翻译)

  • VBA-N级弹出下拉菜单【选择一次输出结果】.xlsm

    VBA-N级弹出下拉菜单【选择一次输出结果】.xlsm 直接选择一级菜单后弹出下一级菜单,一直到最后一级,一次输出结果

  • Berkeley CS61B Data Structures 数据结构 课程实验和项目.zip

    Berkeley CS61B Data Structures 数据结构 课程实验和项目.zip

  • 基于 VC++实现的完整串口通信程序,TCPIP协议 UDP协议 短连接,长连接,定时器 等

    【作品名称】:基于 VC++实现的完整串口通信程序,TCPIP协议 UDP协议 短连接,长连接,定时器 等 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【项目介绍】:基于 VC++实现的完整串口通信程序,TCPIP协议 UDP协议 短连接,长连接,定时器 等

  • 五洲商城小程序前端源码/小程序模板

    五洲商城小程序前端源码/小程序模板。

  • PPT模板-通用模板-多场景应用-精品PPT通用模板:适用于演讲情感故事分享、商业提案教育培训、品牌推广、激励演讲、艺术创意展示

    本资源提供了一套精品PPT通用模板,设计优雅且功能全面,适合各种演讲和展示场景,包括但不限于商业提案、教育培训、个人品牌推广、激励演讲以及艺术和创意展示。这套模板的多功能性使其成为商务人士、教育者、艺术家及所有需要高质量演示材料的用户的理想选择。 模板特点: 多场景适用性:无论是商业环境下的提案、教室内的教育演讲,还是个人情感故事的分享,此模板均能提供合适的布局和设计。 设计元素:包括精心设计的幻灯片背景、图表、图形和文本框等元素,所有设计均旨在提升信息传达的清晰度和视觉吸引力。 易于定制:模板设计考虑到用户可能需要进行个性化调整,因此提供了高度可定制的布局和样式选项,使用户能够根据自己的需要轻松修改。 高质量视觉效果:采用现代设计理念,结合精美的色彩搭配和高分辨率图片,确保每一次演示都能给观众留下深刻印象。 使用场景详解: 商业提案:模板提供专业的商业图表和数据展示方案,帮助阐述复杂的商业逻辑和数据。 情感故事分享:通过情感化的背景设计和图片展示功能,让个人故事表达更加生动感人。 教育和培训:模板中的教育专用幻灯片设计,适合用于课堂教学和在线培训课程。 个人品牌推广:

  • 基于人工智能模型U-net神经网络医学图像分割

    这个完整工程给出基于人工智能模型U-net神经网络医学图像分割功能实现,适合基于机器学习和深度学习模型分析的学习资料,并有详细程序说明书。

  • 基于微信小程序的人脸识别的签到系统的设计与实现源码.zip

    提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

  • QT数据库大作业-登录页面

    QT数据库大作业-登录页面

  • 知识领域: 网络编程 技术关键词: socket、网络编程、异步IO、TCP/IP 内容关键词: 客户端、服务器、网络协议

    知识领域: 网络编程 技术关键词: socket、网络编程、异步IO、TCP/IP 内容关键词: 客户端、服务器、网络协议 用途: 学习使用Python构建网络应用和通信系统 资源描述: Python官方文档中提供了全面的网络编程指南和示例,涵盖了socket编程、异步IO和网络协议等方面的内容。

Global site tag (gtag.js) - Google Analytics