GCC所支持的C语言版本
标准来自实践。C语言原本没有标准,用得人多了便有了标准。
C语言的第一次发展在1969年到1973年之间。C之所以被称为C是因为C语言的很多特性是由一种更早的被称为B语言的编程语言中发展而来的。
到了1973年,C语言已经可以用来编写Unix操作系统的内核。这是第一次用C语言来编写操作系统的内核。Dennis Richie和Brian Kernighan在1978年出版了《C程序设计语言》
1980年以后,贝尔实验室使得C变得更为广泛的流行,使得C一度成为了操作系统和应用程序编程的首选。甚至到今天,它仍被广泛用于编写操作系统以及作为广泛的计算机教育的语言。好像Linux程序员普遍信任C语言。ESR极为赞美C语言,认为它是一个清晰、简洁的最简化设计,是经典硬件体系之上一个薄而灵活的胶合层。“完美之道,不在无可增加,而在无可删减”。
C不断的从它的第一版本进行改进。在1978年,Kernighan和Richie的《C程序设计语言》第一版出版。在以后的几年里,《C程序设计语言》一直被广泛作为C语言事实上的规范。在这本书中,C语言通常被表述成“K&R C”(第二版的包括了ANSI C标准)。
K&R C通常被作为C编译器所支持的最基本的C语言部分。虽然现在的编译器并不一定都完全遵循ANSI标准,但K&R C作为C语言的最低要求仍然要编程人员掌握。但是无论怎样,现在使用广泛的C语言版本都已经与K&R C相距甚远了,因为这些编译器都使用ANSI C标准。
1989年,C语言被ANSI标准化。(ANSI X3.159-1989)。标准化的一个目的是扩展K&R C。这个标准包括了一些新的特性。在K&R出版后,一些新的特征被“非官方”的加到C语言中。在ANSI标准化自己的过程中,一些新的特征被加了进去。ANSI也标准化了函数库。ANSI C标准被ISO(国际标准化组织)采纳成为ISO 9899。ISO的第一个版本文件在1990年出版。
在ANSI标准化后,C语言的标准在一段相当的时间内都保持不变,尽管C++继续在改进。(实际上,Normative Amendment1在1995年已经开发了一个新的C语言版本。但是这个版本很少为人所知。)标准在90年代才经历了改进,这就是ISO9899: 1999(1999年出版)。这个版本就是通常提及的C99。它被ANSI于2000年三月采用。
gcc支持传统C语言(也就是K&R C),包括一些处于规范之外但常见且使用的C语法。在编译时,指定'-traditional'选项即可。另外该选项还支持GNU对C语言的扩展。
gcc的'-ansi'选项'-ansi'支持符合ANSI标准的C程序,这样就会关闭GNU C中某些不兼容ANSI C的特性,例如asm, inline和 typeof关键字,以及诸如unix和vax这些表明当前系统类型的预定义宏,同时开启 不受欢迎和极少使用的ANSI trigraph特性,以及禁止`$'成为标识符的一部分。
尽管使用了'-ansi'选项,下面这些可选的关键字, __asm__, __extension__, __inline__和__typeof__仍然有效.你当然不会把 他们用在ANSI C程序中,但可以把他们放在头文件里,因为编译包含这些头文件的程序时,可能会指定 `-ansi'选项。另外一些预定义宏,如__unix__和__vax__,无论有没有使用 ‘-ansi’选项,,始终有效。
使用‘-ansi'选项不会自动拒绝编译非ANSI程序,除非增加`-pedantic'选项作为 `-ansi'选项的补充。
使用’-ansi'选项的时候,预处理器会预定义一个__STRICT_ANSI__宏,有些头文件 关注此宏,以避免声明某些函数,或者避免定义某些宏,这些函数和宏不被ANSI标准调用;这样就不会干扰在其他地方 使用这些名字的程序了。
gcc的‘-pedantic‘选项可打开完全服从ANSI C标准所需的全部警告诊断;拒绝接受采用了被禁止的语法扩展的程序。无论有没有这个选项,符合ANSI C标准的程序应该能够被正确编译(虽然极少数程序需要‘-ansi' 选项)。然而,如果没有这个选项,某些GNU扩展和传统C特性也得到支持。使用这个选项可以拒绝这些程序。没有理由 使用这个选项,他存在只是为了满足一些书呆子(pedant)。对于替选关键字(他们以’__'开始和结束) ,‘-pedantic'不会产生警告信息。Pedantic 也不警告跟在__extension__后面的表达式,不过只应该在系统头文件中使用这种转义措施,应用程序最好 避免。
当我打算结束这篇小文章之后,随便google一下,结果搜到了如下文字,姑且让它作为一个小结了。
目前主要的C语言规范有c89(c90), c95(94)和c99。C89是最早的C语言规范,于89年提出,90年先由美国国家标准局推出ANSI版本,后来被接纳为ISO国际标准 (ISO/IEC 9899:1990),因而有时也称为c90。但在94和96年分别对c90进行了两次错误修正,gcc支持的是修正后的c89(90)版本的C语言规范。在95年提出过对90版规范的修订案,称为 C95或者AMD1。gcc也支持c95规范。最新的一次C规范修订在99年制定(ISO/IEC 9899:1999),即常称的C99规范。在2001年对C99的错误进行了修正,gcc支持的修正后的c99规范,但是到目前为止,gcc还没有完成对c99规范的完全支持。
在默认设置下,gcc对c语言进行了一些自己的扩展。在不加语言设置参数的情况下,gcc使用c89规范和自己的一些扩展。在将来如果gcc完成了对c99的全面支持,可能默认会使用c99规范加gcc自己的扩展。
gcc下的语言规范设置:
-std=iso9899:1990,-ansi或-std=c89 (三者完全等同)来指定完全按照c89规范,而禁止gcc对c语言的扩展。
-std=iso9899:199409 使用C95规范
-std=c99 或者 -std=iso9899:1999 使用C99规范。
-std=gnu89 使用c89规范加上gcc自己的扩展(目前默认)
-std=gnu99 使用c99规范加上gcc自己的扩展
C语言有国际标准,ANSI于1989制定,成为C89,在1990被国际标准化组织接纳(ISO),所以C语言的标准是ANSI/ISO标准,新的编译器基本满足C89.
后来在1999年对C89进行了改进,称为ANSI/ISO C99标准。
现在还没一个编译器完全支持C99,部分功能还没实现。
linux下面的gcc是比较符合标准的,gcc的文档就有说明,他说他还没完全符号c99。
如果要用c99的特征,比如变长数组,inline,c++风格的数据声明方式,就要添加-std=c99参数,比如gcc -std=c99 main.c -o main这样编译程序。
你的程序之所以没能编译通过是因为语法错了。
char *str = "abcd"这样的字符串里面的值是不能更改的,用*str='b';这样去更改会出错,因为字符串数据在编译好的程序中是存储在只读的地方。
如果要修改,就要声明为数组。比如
char str[] = "abcd";
str[0] = 'b';
这样就可以。
书本可以参考<
void main()是一种不良习惯,尽量不要用,gcc中就禁止的。而应该用int main(),因为这样可以方便检查程序是正常结束,还是发生错误了。
main()这样写,大部分编译器会替换成int main().
>>The C programming language看过了,涉及到标准的东西太少
这本书说的语法就是标准的语法啊,你还要怎样的标准。如果你英文好,而且又能读懂那些专家写的东西可以看看C标准的文档<
>>void main()在gcc可以通过
我的表述有问题,通常我认为程序中不应该做的事就是应该禁止的,你应该可以看到gcc给出的警告信息吧。
>>另外我说的那个不是说编译不能通过,是出现异常,我压根就没说不能通过
致命和低级的错误,我通常会认为语法错误,对这些要求比较严,不要见怪。呵呵~~写入只读区域是运行时错误,你没学汇编吧,在汇编中会提到一些概念,字符串就存在于文字常量区,值不能改的,一改就出错,而数组的数据是存在于stack,malloc出来的是存在于heap,大致如下,:
一个由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。
给你一个很简单的程序:
#include
int main()
{
char *p = "hello";
char *t = "hello";
return 0;
}
多数编译器会让p和t指向同一个地址的,因为编译的时候会合并相同的字符串。而且编译器会设置"hello"的值为const char *型的,也就是指针指向的数据是不能更改的。
127条C语言编码标准
Rule 1. 严格遵循ANSI C89标准,不允许任何扩展。
Rule 3. 如果要嵌入汇编语言,则必须将所有汇编语句包装在C函数里,而且这些函数中只有汇编语句,没有常规C语句。
Rule 7. 不得使用三元操作符(? : )
Rule 10. 不得残留被注释掉的废代码。
Rule 11. 所有标识符不超过31字符。
Rule 12. 不同名空间中的变量名不得相同。
例如:
typedef struct MyStruct {... } MyStruct; (违规)
struct Person {
char* name;
...
};
char name[32]; (违规)
Rule 13. 不得使用char, int, float, double,
long等基本类型,应该用自己定义的类型显示表示类型的大小,如CHAR8, UCHAR8, INT16, INT32, FLOAT32,
LONG64, ULONG64等。
Rule 14. 不得使用类型char,必须显示声明为unsigned char或者signed char。
Rule 18. 所有数字常数应当加上合适的后缀表示类型,例如51L, 42U, 34.12F等。
Rule 19. 禁止使用八进制数。(因为086U这样的常数很容易引起误解)。
Rule 21. 不得定义与外部作用域中某个标识符同名的对象,以避免遮盖外部作用域中的标识符。
Rule 23. 具有文件作用域的对象尽量声名为static的。
Rule 24. 在同一个编译单元中,同一个标识符不应该同事具有内部链接和外部链接的声名。
Rule 25. 具有外部链接性质的标识符应该只声明一次。
Rule 27. 外部对象不得在多个文件中声名。
Rule 28. 禁止使用register关键字。
Rule 29. 自动对象(栈对象)使用前必须赋初值。
Rule 33. 操作符&&和||的右侧表达式不得具有副作用(side-effect)。
也就是说,象 if (x == 20 && ++y == 19)这样的表达式被禁止。
Rule 35. 在返回布尔值的表达式中不得出现赋值操作。
也就是说,常用的 if (!(fp = fopen("fname", "r"))) { /* error */ }
被禁止。
Rule 37. 不得对有符号数施加位操作,例如 1 << 4 将被禁止,必须写 1UL << 4;
Rule 39. 不得对有符号表达式施加一元 "-" 操作符。
Rule 40. 不得对有副作用的表达式施加sizeof操作符。
Rule 42. 除了循环控制语句,不得使用逗号表达式。
Rule 44. 禁止冗余的显式转型。比如: double pi = (double) 3.1416F;
Rule 45. 禁止从任意类型到指针的强制转型,禁止从指针到任意类型的强制转型。
例如:void* p = (void*)0xFFFF8888UL;
Rule 49. 显示测试值是否为零。
Rule 50. 不得显式判断浮点数的相等性和不等性。
Rule 52. 不得遗留“永远不会用到”的代码。
Rule 53. 所有非空语句必须具有副作用。
Rule 55. 除了switch语句,不得使用标号(label)。
Rule 56. 不得使用goto.
Rule 57. 不得使用continue。
Rule 58. 除了switch语句,不得使用break.
Rule 59. if, else if, else, while, do..while, for语句块必须使用{}括起。
Rule 60. 任何if..else if 语句,最后必须有一个收尾的else。例如:
if (ans == 'Y') {
...
}
else if (ans == 'N') {
...
}
else if (ans == 'C') {
...
}
else {
;
}
Rule 67. 循环计数器的值不得在循环体内修改。
Rule 70. 禁止任何直接和间接的递归函数调用。
Rule 82. 每个函数只能有一个推出点。
Rule 86. 如果一个函数可能返回错误信息,则调用后必须加以测试。
Rule 92. 不应该使用#undef
Rule 95. 不得将宏作为参数传给宏函数
Rule 98. 在一个宏定义中,#或##符号只能出现一次。
Rule 101. 禁止指针运算(代之以数组下标运算)。
Rule 102. 禁止超过两级的指针。
Rule 104. 禁止使用指向函数的非常量指针。
Rule 106. 不得将栈对象的地址传给外部作用域的对象。
(后面的规则针对实时嵌入式系统,对其他类型的开发未必适用,故略去13条)
Rule 118. 禁止使用动态堆分配(也就是不得使用malloc, calloc和realloc)。
Rule 119. 禁止使用errno。
Rule 120. 禁止使用offsetof.
Rule 121. 禁止使用
Rule 122. 禁止使用setjmp, longjmp.
Rule 123. 禁止使用
Rule 124. 禁止使用(不能用printf, scanf了!)
Rule 125. 禁止使用atoi, atof, atol。
Rule 126. 禁止使用abort, exit, getenv。
Rule 127. 禁止使用
The ANSI C89 Standard headers
C语言出现的历史背景
C语言的衍生过程
名称 |
设计者 |
设计时间 |
简述 |
ALGOL60 |
- |
1960 |
离硬件比较远,不适合编写系统程序 |
CPL |
剑桥大学 |
1963 |
相对于ALGOL60更接近硬件,但规模较大难以实现 |
BCPL |
剑桥大学Matin Richards |
1967 |
对CPL语言进行了一定的简化 |
B |
美国贝尔实验室Ken Thompson |
1970 |
语言过于简单(只有一种数据类型),功能有限 |
C |
美国贝尔实验室D.M.Ritchie |
1973 |
见下文 |
C语言历史
1978 K&R合著《The C Programming Language》
1983 美国国家标准化协会(ANSI)制定ANSI C
1987 ANSI公布 87 ANSI C
1988 K&R 合著第2版《The C Programming Language》
1990 国际标准化组织ISO接受87 ANSI C为ISO C
1.2 C语言的特点
C语言特点:
1 语言简洁、紧凑,灵活。
2 运算符丰富。
3 数据结构丰富。
4 以函数作为程序的模块单位,是理想的结构化语言。
5 语法限制不太严格,程序设计自由度大。
6 允许直接访问物理地址,能进行位(bit)操作,能实现汇编语言的大部分功能,可以直接对硬件进行操作。
7 生成目标代码质量高,程序执行效率高。
8 与汇编语言相比,可移植性好。
新的C语言: C99标准介绍
Randy Meyers
此篇文章摘取与即将登载于《 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语言的用户带来帮助。在翻译上,所有译者在翻译过程中有疑惑的术语或者其他一切都以括号形式把原文直接给出,诚心不想给读者半点误导,但是否如愿还需读者的评判, 关于本文的一切可以用[email protected] 与译者联系和讨论。
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 语言中去。
Randy Meyers is consultant providing training and mentoring in C, C++, and Java. He is the current chair of J11,
the ANSI C committee, and previously was a member of J16 (ANSI C++) and the ISO Java Study
Group. He worked on compilers for Digital Equipment Corporation for 16 years and was Project
Architect for DEC C and C++. He can be reached at [email protected] .