开源c开发环境_使用C99进行开源开发

开源c开发环境

随开源操作系统一起发行的gcc版本并不支持所有C99新功能。 但是,现在已经有足够数量的产品可供使用,因此您可以开始认真考虑在新开发中采用C99功能,尤其是在它们在效率或清晰度方面产生重大差异的地方。

本文回顾了Linux和BSD的最新版本中C99语言和库功能的可用性。 由于这些功能中的许多功能都是gcc的标准功能,因此最新版本的gcc在大多数其他平台上都可以执行相同的操作。 当然,库支持从一种发行版到另一种发行版,或者从一种操作系统到另一种发行版都不同。

用语言标准调用gcc

GNU C编译器支持许多不同版本的C编程语言。 您可以使用-std选项选择要在命令行上使用的C标准版本。 默认不是标准的任何版本,而是“ GNU C”语言,它具有自己的扩展集。 您可以使用以下选项选择C标准的通用版本:

  • -std=c89-std=iso9899:1990
    原始C89标准
  • -std=iso9899:199409
    C89,以及规范性附录1中的更改
  • -std=c99-std=iso9899:1999
    C99修订标准

要强制完全遵守该标准的版本,请使用-pedantic选项。 该选项主要用于确保您的代码在过渡到其他编译器后仍能生存。 例如,如果您要与不使用gcc的人共享一个代码库,那么您可能一直都希望使用它。 注意, -pedantic标志偶尔会弄错给定标准的一些细节。 例如,它可能尝试在C99程序上强制执行C89规则,或者可能无法强制执行晦涩的规则。 仍然值得进行测试。 如果您尝试编写可移植的代码,那么-std=c99 -pedantic -Wall可以说很多。

C89标准引入了一个新概念:独立环境和托管环境之间的区别。 大多数人都习惯使用托管环境 。 它提供了完整的标准库,执行始终从main()开始。

如果要在独立环境中暗含一些稍有不同的警告和行为,请使用-ffreestanding选项。

默认设置为托管环境。 为了解决常见的FAQ,是的,gcc故意警告main()声明的参数或返回类型不是标准中列出的参数。 尽管C99标准允许实现提供替代声明,但它们永远不可移植。 尤其是,使用返回类型为void声明main()的常见做法是完全错误的。 (这就是为什么NetBSD的内核使用-ffreestanding标志进行编译的-ffreestanding 。)

语言特征

C编程语言包含两部分。 令人困惑的是,这些被称为“语言”和“库”。 从历史上看,有一堆常用的实用程序代码,每个人都倾向于重用。 最终将其标准化为标准C库。 最初的区别很容易理解:如果编译器做到了,那就是语言。 如果在附加代码中,则为库。

但是,随着时间的流逝,这种区分变得模糊了。 例如,某些编译器将生成对用于64位算术的外部库的调用,而某些库函数可能会被编译器神奇地处理。 为了本文的目的,该部分遵循标准的术语:标准“库”部分中的功能是库功能,并将在本文的下一部分中进行讨论。 本节介绍其他所有内容。

C99语言引入了许多软件开发人员可能感兴趣的新功能。 其中许多功能类似于GNU C对C扩展的功能。 不幸的是,在某些情况下,它们并不完全兼容。

C ++普及了一些功能。特别是//注释,混合声明和代码已成为C99的标准功能。 这些已经存在于GNU C中,并且可以在每个平台上使用。 通常,C和C ++仍然是独立的语言。 实际上,C99与C ++的兼容性要比C89小。 与往常一样,尝试编写混合代码不是一个好主意。 好的C代码将是不好的C ++代码。

C99在字符串文字和标识符中都增加了对Unicode字符的支持。 实际上,对于大多数用户而言,系统支持可能并不需要。 不要期望使用此功能的源还可以被其他人访问。 通常,编译器中大部分都支持宽字符和unicode,但是文本处理工具还没有达到标准。

新的可变长度数组(VLA)功能部分可用。 简单的VLA将起作用。 但是,这纯属巧合。 实际上,GNU C具有自己的可变长度数组支持。 结果,虽然使用可变长度数组的简单代码将起作用,但是许多代码将碰到旧的GNU C对VLA的支持与C99定义之间的差异。 声明长度为局部变量的数组,但不要尝试更进一步。

复合文字和指定的初始化程序是一个出色的代码可维护性功能。 比较这两个代码片段:

清单1.在C89中延迟n微秒
/* C89 */
    {
        struct timeval tv = { 0, n };
        select(0, 0, 0, 0, &tv);
    }
清单2.在C99中延迟n微秒
// C99
    select(0, 0, 0, 0, & (struct timeval) { .tv_usec = n });

复合文字的语法允许使用大括号括起来的一系列值来初始化适当类型的自动对象。 每次到达对象的声明时都会重新初始化该对象,因此使用可以修改相应对象的函数(例如select某些版本)是安全的。 指定的初始化程序语法使您可以按名称初始化成员,而无需考虑它们在对象中出现的顺序。 这对于仅初始化了几个成员的大型复杂对象特别有用。 与普通的聚合初始化程序一样,缺少的值将被视为初始值设定为0。 其他初始化规则有所变化。 例如,现在允许您在enum声明的最后一个成员之后使用尾部逗号,以使编写代码生成器稍微容易一些。

多年来,人们一直在争论C类型系统的扩展,例如long long 。 C99引入了一些新的整数类型。 使用最广泛的是long long 。 标准流程引入的另一种类型是intmax_t 。 这两种类型都可以在gcc中使用。 但是,整数提升规则不适用于大于long的类型。 最好使用显式强制转换。

还有很多类型,可以对所需质量进行更具体的描述。 例如,有些类型具有名称,例如int_least8_t (至少具有8位)和int32_t (具有恰好32位)。 该标准保证可以访问至少8位,16位,32位和64位的类型。 不能保证会提供任何精确宽度类型。 除非您真的完全确定不能接受更大的类型,否则请不要使用此类。 另一个可选类型是新的intptr_t类型,该类型是一个足以容纳指针的整数。 并非所有系统都提供这种类型(尽管当前所有Linux和BSD实现都可以提供)。

C预处理器具有许多新功能。 它允许空参数,并且支持具有不同数量参数的宏。 有一个_Pragma运算符可用于生成宏的编译指示,还有一个__func__宏,该宏始终包含当前函数的名称。 这些功能在当前版本的gcc中可用。

C99添加了inline关键字以建议函数内联。 GNU C也支持该关键字,但是语义略有不同。 如果您使用的是gcc,则如果希望获得与C99相同的代码行为,则应始终在内联函数上使用static关键字。 以后的修订中可能会解决这个问题; 同时,您可以将inline用作编译器提示,但不必依赖于确切的语义。

C99引入了一个限定, restrict ,它可以指点一下编译器优化提示。 因为不需要编译器对此做任何事情,所以gcc接受了它。 优化的程度有所不同。 它是安全的,但不要指望它会带来巨大的变化。 与此相关的是,gcc完全支持新的类型别名规则。 这主要意味着您必须更加小心类型修剪,这几乎总是会调用未定义的行为,除非您用来访问错误类型数据的类型是unsigned char

数组声明符作为函数参数现在与指针声明符有显着的区别:您可以放入类型限定符。 特别令人感兴趣的是非常奇怪的优化器提示,即为数组声明器提供了static类型修饰符。 给定此声明: int foo(int a[static 10]);

用不指向至少10个int类型的对象的指针调用foo()是未定义的行为。 这是一个优化器提示。 您所做的只是向编译器保证,传递给函数的参数至少会那么大。 一些机器可能会使用它进行循环展开。 老手会很清楚,这不是没有static关键字全新含义的新C标准。

最后要提到的功能是灵活的数组成员。 一个常见的问题是要声明一个结构,该结构本质上是一个标头,后跟一些数据字节。 不幸的是,如果不给结构提供指向单独分配的区域的指针,C89并没有提供好的方法。 两种常见的解决方案包括:声明一个成员仅具有一个字节的存储空间;然后分配额外的空间并超出数组的边界;声明一个成员具有比您可能需要的更多的存储空间;分配不足;并谨慎使用仅可用的存储空间。 对于某些编译器而言,这两个都是有问题的,因此C99为此引入了新的语法:

清单3.具有灵活数组的结构
struct header {
        size_t len;
        unsigned char data[];
    };

此结构具有有用的属性,如果您为(sizeof(struct header) + 10)个字节分配空间,则可以将数据视为10个字节的数组。 gcc支持此新语法。

图书馆特色

对编译器来说很好。 标准库呢? C99中添加的许多库功能都是基于现有的实践,尤其是在BSD和Linux社区中发现的实践。 因此,其中许多功能都是Linux和BSD标准库中已经存在的功能。 其中许多功能都是简单的实用程序功能。 几乎所有这些原则上都可以用可移植的代码完成,但是其中许多将极其困难。

C99中添加的一些最方便的功能在printf系列功能中。 首先, v*scanf函数已经标准化; 对于scanf系列的每个成员,都有一个对应的v*scanf函数,该函数采用va_list参数而不是可变参数列表。 这些函数与v*printf函数具有相同的作用,允许用户定义的函数使用可变参数列表并最终从printfscanf系列中调用函数来完成艰苦的工作。

其次,已导入4.4BSD snprintf函数系列。 snprintf函数使您可以安全地打印到固定大小的缓冲区中。 当要求打印不超过n个字节时, snprintf保证它创建的字符串长度不超过n-1 ,并且在字符串末尾使用空终止符。 但是,其返回码是如果n足够大,它将要写入的字符数。 因此,您可以可靠地找出你需要多少缓冲区空间完全格式化的东西。 该功能随处可用,您应该几乎一直使用它。 很多安全漏洞都是基于sprintf缓冲区溢出造成的,这可以防止这些漏洞。

新标准中包含许多新的数学功能,包括复杂的数学功能和旨在帮助优化特定浮点芯片的编译器的特殊功能,但并非在所有地方都能可靠实现。 如果需要这些功能,最好检查一下您要定位的确切平台。 并非总是支持浮点环境功能,并且某些平台将不支持IEEE算术。 现在不要指望这些新功能。

C99中对strftime()函数进行了扩展,以提供一些更常用的格式化字符。 这些新字符似乎可以在最新的Linux和BSD系统上使用。 但是,它们在某些较旧的系统上并不总是广泛可用。 在使用新格式之前,请检查文档。

如上所述,大多数国际化代码尚未可靠实现。

其他新的库功能通常并不通用。 数学函数可能在超级计算机编译器中可用,国际化函数可能在美国以外开发的编译器中可用。 编译器供应商实现了他们所要求的功能。

期待

通常最好在采用新功能时保持保守。 但是,许多C99功能现在已经足够广泛,以至于新的开发项目可以合理地利用它们。 gcc编译器套件足够广泛地可用,大多数项目可以合理地假定它将成为各种目标平台上的一个选择。 如果您主要针对Linux或BSD系统,或同时针对这两者,则可以指望至少部分地支持大量的C99新功能。 这些功能是基于感知的需求和实际的实施经验而采用的,它们应该为您提供良好的服务。

在确定您愿意依赖的功能时,不要只看正在键入的计算机上的可用功能; 考虑目标系统。 是否要要求人们升级到较新的操作系统发行版? 您的目标市场头脑是否必须获得新的编译器? 在承诺使用某个功能之前,请在可能的目标系统上测试该功能。


翻译自: https://www.ibm.com/developerworks/opensource/library/l-c99/index.html

开源c开发环境

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值