C99标准介绍(转载)

本文转自:http://tb.blog.youkuaiyun.com/TrackBack.aspx?PostId=795910

 

新的C语言: C99标准介绍

此篇文章摘取与即将登载于《Dr.Dobb's 软件研发》》第二期(2003年9月)的《新的C语言,C99标准介绍》,文章主要是介绍了C99的新特性,在得到作者Randy Meyers以及《Dr.Dobb's 软件研发》》负责人刘江先生的应允下,把全文的前面的一部分作为文档发表,希望能对大家有所帮助。

译注2:C语言的产生源于失败的项目---Multics。从70年代初期的早期C语言到后来的K&R C, ANSI C,C89,在将近20年中C语言多次发展演化,一直到1999年C语言又重新定案,成为新的C语言标准。这篇文章发表在 CUJ Octorber 2000 V18 N10,当时C99标准刚刚公布一年,C社区正急需正统的声音。本文作者Randy Meyers作为标准委员会委员,在CUJ杂志开了一个专题系列The New C用来讨论新标准的新特性,本文就是其中的第一篇。本文从全局上介绍了新标准C,尤其是一些增加的特性,期望本文能给关心C语言和使用C语言的用户带来帮助。在翻译上,所有译者在翻译过程中有疑惑的术语或者其他一切都以括号形式把原文直接给出,诚心不想给读者半点误导,但是否如愿还需读者的评判,关于本文的一切可以用amstrongest@hotmail.com与译者联系和讨论。

C99很像C89,很多地方是一致的,但更多的却是不同。

简介

你可能没有注意到,针对ANSI/ISO   C的主要的修订版[1] 在去年12月已经被核准通过,那是就C99。同样的,你可能也没注意到,其实你已经在使用这个新的C语言了,或者至少用到它的一部分。这需要归功于标准委员会在接受新特性到C语言的过程中采取了恰当而保守的方式。差不多所有的新特性早已经被实现并且在现存的一些C编译器(impletmentations)中证明了其存在的价值。虽然没有编译器能保证全部的C99特性,但其中许多在很多年前就实现了C99中不同的部分。这对于C程序员来说将是个好消息。或许你曾经为了保证程序的可移植性而在你喜爱的编译器里避免使用一些独立的特性,但现在如果这些特性是C99中的一部分的话,你可以放心的使用这些特性,因为他们将在大部分遵守C99标准的编译器中被保证。毫无疑问,新标准是向上兼容旧的,当然也会有些不兼容地方,但这些都是非常少而次要的。标准委员会非常努力地工作就是为了将和老版本的兼容性问题所带来的影响减少到最小。从后面讨论到的关键字你可以看到这方面的例证。

命名和历史

程序设计语言随着时间在不断演进,语言的命名方式不仅仅依赖于语言本身的名字,还结合了它们被定义的年代。(回到五年前,ALGOL 68, C89, Fortran 77还有Fortran 4,我对这些名字真的感到好笑,如今这些令人讨厌的命名方式还带来了千年虫问题,04将无法区别眼前的2004或者久远的1904)。新的C语言和定义它的新标准被称为C99,而原来的C语言标准[2]被称为C89或者C90(ANSI在1989年公布了标准文档而ISO在1990年重新编排了标准文档章节后才公布)。如果你在自己的程序中处理过日语,韩语,或者中文的文字问题你也许会知道对于C89来说曾经有一次小的升级[3]被称为C95,这次升级主要是加入了更多的用来处理多字节和宽字节字符(wide and multibyte characters)的库函数(Java的倡导者曾经错误的宣布Java是第一个支持大字符集合的语言,其实这样的支持在C89中就已经存在了)。

对于C99施加影响最大的或许就是数值C语言扩展小组(Numerical C Extensions Group, or NCEG.)。NCEG是ANSI C标准委员会J11的一个子委员会,他们主要在C89完成后从事技术报告工作[4]。NCEG的技术报告不是标准,它只是号召编译器实现者来体会并且得到那些一系列描叙清楚的语言特性的经验。这些扩展中大的部分是用来处理C语言数值编程的(IEEE arithmetic, complex numbers),但是还有一些是为了其他一般目标或者性能的提升和优化(变长数组,并行处理,restrict关键字)。

NCEG的扩展一些是由子标准委员会首次定义的,而其他一些则是根据编译器厂商对于已经实现的特性的反馈而重新定义的。所以技术报告不是标准,编译器厂商可以自由的选取并且实现这些扩展,甚至可以根据用户的经验更改这些扩展。

真实世界的需求是非常有价值的。语言的不同特性在内部会以一种令人吃惊的方式相互作用,而有的特性将会给程序的运行效率带来不良影响,即使这些特性永远都不会被用到(举例来说,在一些C++的实现中,使用少量多继承的程序将会比只使用单继承的程序慢一些)。NCEG的扩展实验不仅仅是为了改进扩展本身,同时是为了改进它们的规格,并且让标准委员会对于那些已知的语言特性的内部作用和价值保持信心。

哪些不属于C99

并不是所有的NCEG扩展都被C99所接受,其中最大的例子也许就是NCEG对并行处理的支持并没有被C99所接受。这些并行处理是基于 Thinking Machines上的C*语言(读作C-Star)的。并行计算机制造商为了能让程序员写出清晰的并行程序代码而做出许多不同的扩展特性,而NCEG技术报告对此却没有做出改进,所以仍然没有一致的并且是最好的方式在并行计算机上编程。因此,这样的特性是不适合被标准C语言接受的。另外一些NCEG扩展在被加入到C99之前做了一些更改。NCEG支持的复数包含分开的虚数数据类型(separate imaginary datatypes)。比如,double_ imaginary数据类型。但是在C99中虚数数据类型却是可选择的。

然而,被考虑得最多到最后却没有被C99采纳的特性不是来自NCEG,而是C++。在将近一年的时间里,标准委员会一直在从事C ++中的子集--面向对象特性的研究工作。这个子集有点像80年代末的C++特性的混合,包括单继承(非多继承),虚函数,成员访问控制(公有,私有,保护),构造函数,析构函数。如果新的C语言与早期的C++相似可以说是既有进步也有退步。积极的一面是,这些特性对于初期C++的流行起到了巨大作用,到现在这些特性的价值和它们之间的内在作用已经被大家很好的理解,而且已经证明它们是可以共存的。然而,消极的一面是“90年代的C++是否可以看作是80 年代的C++的自然演化?如果是, C采用80年代的C++的特性价值何在?要知道我们已经拥有90年代的C++了。”最终,多方面的原因,包括一些逻辑上的,让标准委员会拒绝把面向对象特性加入到C语言中去。

1 ISO (the International Organization for Standardization) and IEC (the International Electrotechnical Commission) form the specialized system for worldwide standardization. National bodies that are member of ISO or IEC participate in the development of International Standards through technical committees established by the respective org anization to deal with particular fields of technical activity. ISO and IEC technical committees collaborate in fields of mutual interest. Other international organizations, governmental and non-governmental, in liaison with ISO and IEC, also take part in the work. 2 International Standards are drafted in accordance with the rules given in the ISO/IEC Directives, Part 3. 3 In the field of information technology, ISO and IEC have established a joint technical committee, ISO/IEC JTC 1. Draft International Standards adopted by the joint technical committee are circulated to national bodies for voting. Publication as an International Standard requires approval by at least 75% of the national bodies casting a vote. 4 International Standard ISO/IEC 9899 was prepared by Joint Technical Committee ISO/IEC JTC 1, Information technology, Subcommittee SC 22, Programming languages, their environments and system software interfaces. The Working Group responsible for this standard (WG 14) maintains a site on the World Wide Web at http://www.dkuug.dk/JTC1/SC22/WG14/ containing additional information relevant to this standard such as a Rationale for many of the decisions made during its preparation and a log of Defect Reports and Responses. 5 This second edition cancels and replaces the first edition, ISO/IEC 9899:1990, as amended and corrected by ISO/IEC 9899/COR1:1994, ISO/IEC 9899/AMD1:1995, and ISO/IEC 9899/COR2:1996. Major changes from the previous edition include: — restricted character set support via digraphs and (originally specified in AMD1) — wide character library support in and (originally specified in AMD1) — more precise aliasing rules via effective type — restricted pointers — variable-length arrays
<think>嗯,用户问的是在C语言中,用struct ListNode dummy = { ... };这样的方式显式初始化成员是否正确。我需要先回忆一下C语言的结构体初始化语法。 首先,C语言中的结构体初始化可以使用两种方式:一种是按照成员顺序的列表初始化,另一种是指定成员的初始化。比如,对于结构体struct ListNode,如果有val和next两个成员,正确的初始化应该是怎样的呢? 用户给出的例子是: struct ListNode dummy = { dummy.val = 0, dummy.next = NULL }; 这看起来像是在使用指定成员的初始化方式,也就是C99引入的 designated initializers。不过,正确的语法应该是用点号加上成员名,比如.val = 0,而不是通过结构体变量名dummy来指定。因为在初始化的时候,变量dummy还没有被完全创建,所以不能通过dummy.val这样的方式去指定成员。正确的写法应该是: struct ListNode dummy = { .val = 0, .next = NULL }; 这样的话,编译器就知道这是对结构体成员的指定初始化。而用户例子中的dummy.val和dummy.next的写法是错误的,因为此时dummy还没有被完全初始化,无法引用自己的成员。这可能会导致编译错误,比如“expected identifier”之类的提示,因为语法不正确。 接下来,我需要确认用户提供的代码中的错误点在哪里。原作者的代码是使用了一个空的初始化列表,即struct ListNode dummy = {};,这在C语言中是不允许的,除非结构体所有成员都有默认值(但C语言的结构体成员不会自动初始化,除非使用静态存储期)。不过,如果编译器支持C99或更高版本,空的初始化列表会将所有成员初始化为零,但更常见的是显式初始化每个成员,或者使用memset。 不过,用户的问题是关于他们自己写的显式初始化是否正确。他们尝试用dummy.val = 0的形式,这在语法上是错误的。正确的designated initializer语法是使用点号加成员名,而不是结构体变量名。所以正确的写法应该是用.val和.next,而不是dummy.val和dummy.next。 总结一下,用户提供的写法是错误的,正确的应该是使用点号加成员名的方式。需要指出语法错误,并给出正确的示例。同时,可能还需要提到C语言中结构体初始化的不同方法,比如顺序初始化或者designated initializers,以及不同C标准(C89、C99)的支持情况。 另外,用户提到的原作者的代码中使用了struct ListNode dummy = {};,这在C89中是不允许的,但在C99及更高版本中,允许使用空的初始化列表,此时所有成员会被初始化为零。不过,为了代码的可移植性和清晰性,显式初始化可能更好,尤其是在成员较多或需要特定初始值时。 因此,用户的写法存在语法错误,正确的初始化方式应使用点号指定成员,而不是结构体变量名。需要明确说明这一点,并给出正确和错误的对比示例,帮助用户理解问题所在。</think>在C语言中,你提供的写法存在语法错误。下面分步骤解释: --- ### **错误原因分析** 1. **成员初始化语法错误** ```c struct ListNode dummy = { dummy.val = 0, // ❌ 错误写法 dummy.next = NULL // ❌ 错误写法 }; ``` - **错误点**:在结构体初始化时,不能通过`变量名.成员名`的方式显式指定成员。 - **正确写法**:应使用`.成员名 = 值`的语法(C99标准引入的"designated initializers"): ```c struct ListNode dummy = { .val = 0, // ✅ 显式初始化 val .next = NULL // ✅ 显式初始化 next }; ``` 2. **空初始化`{}`的合法性** - 原作者的`struct ListNode dummy = {};`是合法的(C99及以上支持),会将所有成员初始化为`0`或`NULL`。 - 但若需显式控制初始化值,建议使用`.成员名 = 值`语法。 --- ### **正确写法对比** | 场景 | 正确代码示例 | |----------------------|----------------------------------------------------------------------------| | 空初始化(C99+) | `struct ListNode dummy = {};` | | 显式初始化成员(C99+)| `struct ListNode dummy = { .val = 0, .next = NULL };` | | 传统顺序初始化 | `struct ListNode dummy = {0, NULL};` // 依赖成员定义顺序 | --- ### **为何要使用哨兵节点(Dummy Node)?** - **作用**:在合并链表等操作中,哨兵节点作为临时头节点,可避免处理头节点的特殊情况,简化代码逻辑。 - **代码示例**: ```c struct ListNode dummy = { .next = NULL }; // 哨兵节点初始化 struct ListNode* cur = &dummy; // cur指向新链表的末尾 ``` --- ### **总结** - ✅ 使用`.成员名 = 值`语法显式初始化。 - ❌ 避免`变量名.成员名`的写法。 - 空初始化`{}`合法,但需注意编译器对C标准的支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值