C语言

title: 历史.
title: 早期发展.
content: C语言最早由丹尼斯·里奇(Dennis Ritchie)为了在PDP-11电脑上运行的Unix系统所设计出来的程序语言,第一次发展在1969年到1973年之间。  C源于BCPL语言,后者由马丁·理察德(Martin Richards)于1967年左右设计实现。BCPL是一门"无类型"的编程语言:它仅能操作一种数据类型,即机器字(machine word)。1970年,肯·汤普逊为运行在PDP-7上的首个Unix系统设计了一个精简版的BCPL,这个语言被称为B语言,它也是无类型的。  Unix最早运行在PDP-7上,是以组合语言写成。在PDP-11出现后,丹尼斯·里奇与肯·汤普逊着手将Unix移植到PDP-11上,无类型的语言在PDP-11上愈发显得不合适。PDP-11提供了多种不同规格大小的基本对象:一字节长的字符,两字节长的整型数以及四字节长的浮点数。B语言无法处理这些不同规格大小的对象,也没有提供单独的操作符去操作它们。  C语言最初尝试通过向B语言中增加数据类型的想法来处理那些不同类型的数据。和大多数语言一样,在C中,每个对象都有一个类型以及一个值;类型决定了可用于值的操作的含义,以及对象占用的存储空间大小。  1973年,Unix操作系统的核心正式用C语言改写,这是C语言第一次应用在操作系统的核心编写上。  1975年C语言开始移植到其他机器上使用。史蒂芬·强生实现了一套「可移植编译器」,这套编译器修改起来相对容易,并且可以为不同的机器生成代码。从那时起,C在大多数计算机上被使用,从最小的微型计算机到与CRAY-2超级计算机。C语言很规范,即使没有一份正式的标准,你也可以写出C程序,这些程序无须修改就可以运行在任何支持C语言和最小运行时环境的计算机上。  C最初在小型机器上实现,并且继承了一系列小语种编程语言的特点;与功能相比,C的设计者更倾向于简单和优雅。此外,从一开始,C语言就是为系统级编程而设计,程序的运行效率至关重要,因此,C语言与真实机器能力的良好匹配也就不足为奇。例如,C语言为典型硬件所直接支持的对象:字符,整数(也许有多种大小),以及浮点数(同样可能有多种大小)提供了相应的基本数据类型。
title: K&R C.
content: 1978年,丹尼斯·里奇和布莱恩·柯林汉合作出版了《C程序设计语言》的第一版。书中介绍的C语言标准也被C语言程序员称作“K&R C”(柯里C),第二版的书中也包含了一些ANSI C的标准。  K&R C主要引入了以下语言特性:  即使在后来ANSI C标准被提出的许多年后,K&R C仍然是许多编译器的最低标准要求,许多老旧的编译仍然运行K&R C的标准。
title: ANSI C 和 ISO C.
content: 1989年,C语言被美国国家标准协会(ANSI)标准化,编号为ANSI X3.159-1989。这个版本又称为C89。标准化的一个目的是扩展K&R C,增加了一些新特性。  1990年,国际标准化组织(ISO)成立 ISO/IEC JTC1/SC22/WG14 工作组,来规定国际标准的C语言,通过对ANSI标准的少量修改,最终制定了 ISO 9899:1990,又称为C90。随后,ANSI亦接受国际标准C,并不再发展新的C标准。  K&R C语言到ANSI/ISO标准C语言的改进包括:  WG14工作小组之后又于1994年,对1985年颁布的标准做了两处技术修订(缺陷修复)和一个补充(扩展)。下面是 1994 年做出的所有修改:
title: C99.
content: 在ANSI的标准确立后,C语言的规范在一段时间内没有大的变动,然而C++在自己的标准化建立过程中继续发展壮大。《标准修正案一》在1994年为C语言建立了一个新标准,但是只修正了一些C89标准中的细节和增加更多更广的国际字符集支持。不过,这个标准引出了1999年的发表。它通常被称为C99。C99被ANSI于2000年3月采用。  在C99中包括的特性有:  用于x86架构的GCC,Clang和其它一些商业编译器(如IAR System,ICC)现支持C99的全部特性,而微软和Borland支持C99的部分特性。
title: C11.
content: 2011年12月8日,ISO正式发布了新的C语言的新标准C11,之前被称为C1X,官方名称为ISO/IEC 9899:2011。新的标准提高了对C++的兼容性,并增加了一些新的特性。这些新特性包括泛型、多执行绪、带边界检查的函式、匿名结构等。
title: C18.
content: C18没有引入新的语言特性,只对C11进行了补充和修正。
title: C2x.
content: C2x引入了一些新特性,但目前还没有到冻结特性阶段。目前可以确定的是,C2x标准将加入符合IEEE标准的十进制浮点数,这使得计算机能够精确地储存浮点数。
title: 语法.
title: Hello World 程序.
content: 下面是一个在标准输出设备(stdout)上打印出 "Hello, world!" 字串的简单程序。类似的程序,通常作为初学程序语言时的第一个程序:  int main(void)  其中只有int,main,return为C语言的关键字,而以"#"开头的语句为预处理器所负责的部分,即预处理部分。printf为stdio.h所提供的函数。  printf函数中的"\n"是一个转义字符,形式为"\"加上一个字符。所起的作用在ASCII码中规定。  关于printf的更多细节,参见printf;  关于格式化字符串的更多信息,参见格式化字符串
title: 进一步了解.
content: C语言由函数和变量组成,C的函数就像是Fortran中的子程序和函数。  在C语言中,程序从 codice_12 开始执行。codice_12 函数通过调用和控制其他函数进行工作。例如上面的codice_14。程序员可以自己写函数,或从库中调用函数。在上面的codice_15 使得 codice_12 返回一个值给调用程序的壳层,表明程序是否成功运行。  一个C语言的函数由返回值、函数名、参数列表和函数体组成。函数体的语法和其它的复合的语句部分是一样的。
title: 复合语句.
content: C语言中的复合语句(或称语句块)的格式为: 复合语句可以使得几个语句从文法上变成一个语句。  有时必须使用复合语句,否则会产生错误。例如,在运用循环语句的时候,如果循环体(即循环中执行部分)包含多个语句(以分号隔开),则必须使用花括号将他们合并成一个复合语句。如果不这么做,系统仅把第一个分号前的内容看做循环体。  需要注意的是,部分C编译器并不支持在任意位置使用复合语句。
title: 条件语句.
content: C语言有两种条件语句形式,分别是if和switch。  If 的格式如下: if(运算式) // 如果 // 有时还会有 else: else // 否则 运算式的值非零表示条件为真;如果条件为假,程序将跳过if处的语句,直接执行if后面的语句。但是如果if后面有else,则当条件为假时,程序跳到else处执行。if和else后面的语句可以是另个if语句,这种套叠式的结构,允许更复杂的逻辑控制流程得以实现。在一般情况下,else一定与最接近的if成对,因此常用括号{}越过此限制。比较下面两种情况: if(逻辑表达) if(逻辑表达式甲) else  要注意这里的缩进和换行只用于方便阅读。编译器并不会根据缩进层级猜测 if 和 else 的对应关系。  switch通常用于对几种有明确值的条件进行控制。它要求的条件值通常是整数或字符。与switch搭配的条件转移是case。使用case后面的标值,控制程序将跳到满足条件的case处一直往下执行,直到语句结束或遇到break。通常可以使用default把其他例外的情况包含进去。如果switch语句中的条件不成立,控制程序将跳到default处执行;如果省略default子句,则直接执行下一语句。switch是可以嵌套的。 switch(值)  简单的条件判断也可用?: 运算式?值1:值2; 如:
title: 循环语句.
content: C语言有三种形式的循环语句:  do while(判断式);  和:  while(判断式)  和:  for(起始化;判断式;运算式) 在while和for中,语句将执行到表达式的值为零时结束。在do...while语句中,循环将至少被执行一次。这三种循环结构可以互相转化:  for(起始化;判断式;运算式) 如果语句中不使用continue语句的话,相当于  起始化; while (判断式) {  当循环条件一直为真时,将产生无穷循环。
title: 跳转语句.
content: 跳转语句包括四种:goto,continue,break和return。 goto 标记; goto语句是无条件转移语句,且标记必须在当前函数中定义,使用“标记:”的格式定义。程序将跳到标记处继续执行。由于goto(特别是向回 goto 和长距离的 goto)容易产生阅读上的困难,所以对新手应该尽量少用。GCC 编译器拓展支持对指针 goto和宏内 goto,一定程度上增强了 goto 的可读性。 continue语句用在循环语句中,作用是结束当前一轮的循环,马上开始下一轮循环。  break语句用在循环语句或switch中,作用是结束当前循环,跳到循环体外继续执行。但是使用break只能跳出一层循环。在要跳出多重循环时,可以使用goto使得程序更为简洁。  当一个函数执行结束后要返回一个值时,使用return。return可以跟一个运算式或变量。如果return后面没有值,将执行不返回值。
title: 在C语言中的运算符号.
content: 比较特别的是,位元右移(»)运算子可以是"算术"(左端补最高有效位)或是"逻辑"(左端补 0)位移。例如,将 11100011 右移 3 位元,算术右移后成为 11111100,逻辑右移则为 00011100。因算术位元右移较适于处理带负号整数,所以几乎所有的编译器都是算术位元右移。  运算符的优先级从高到低大致是:单目运算符、算术运算符、关系运算符、逻辑运算符、条件运算符、赋值运算符(=)和逗号运算符。
title: 数据类型.
content: 见数据类型 (C语言)。
title: 数组.
content: 如果一个变量名后面跟着一个有数字的中括号,这个声明就是阵列声明。字串也是一种阵列,它们以ASCII的NUL作为阵列的结束。要特别注意的是,方括内的索引值是从0算起的。  例如:  int myvector [100];/* 从myvector[0]至[99]共100个元素 */ char mystring [80]; // 声明时初始化 float mymatrix [3] [2] = {2.0, 10.0, 20.0, 123.0, 1.0, 1.0}; int notfull [3][3] = ; // 数组套数组 char lexicon [10000] [300];/* 共一万个最大长度为300的字符阵列。*/ int a[3][4];  上面最后一个例子创建了一个阵列,但也可以把它看成是一个多维阵列。注意阵列的下标从0开始。这个阵列的结构如下:  例子中notfull创建了一个3*3的二维阵列,初始化时有些元素并未赋值。如下: 根据C标准的规定,在存在初始化列表时,如果初始化列表中未提供对所有元素的初始化,则剩余元素会被默认初始化,并使用与静态变量相同的初始化规则。
title: 指针.
content: 如果一个变量声明时在前面使用 * 号,表明这是个指针型变量。换句话说,该变量存储一个地址,而 *(此处特指单目运算符 *,下同。C语言中另有双目运算符 * 表示乘) 则是取内容操作符,意思是取这个内存地址里存储的内容。把这两点结合在一起,可将 codice_17看作是 “*a 解得的内容类型为 int”,对更复杂的声明也如此。指针是 C 语言区别于其他同时代高级语言的主要特征之一。  指针不仅可以是变量的地址,还可以是数组、数组元素、函数的地址。通过指针作为形式参数可以在函数的调用过程得到一个以上的返回值(不同于codice_18这样的仅能得到一个返回值。  指针是一把双刃剑,许多操作可以通过指针自然的表达,但是不正确的或者过分的使用指针又会给程序带来大量潜在的错误。  例如:  int *pi; // 指向整型数据的指针 pi int * api[3];// 由指向整型数据的指针构成的数组,长度为 3 char ** argv;// 指向一个字符指针的指针 struct { int member; } stinst, // pst是一个指向一个匿名结构体的指针  储存在指针中的地址所指向的数值在程序中可以由 * 读取。例如,在第一个例子中, *pi 是一个整型数据。这叫做引用一个指针。  另一个运算符 &,叫做取地址运算符,它将返回一个变量、数组或函数的存储地址。因此,下面的例子:  int i, *pi; /* int and pointer to int */ pi = &i;  i 和 *pi 在程序中可以相互替换使用,直到 pi 被改变成指向另一个变量的地址。  当指针指向结构体时,可以使用运算符 -> 代替 *和. 的作用,如 (*p).m 与 p->m 等效。
title: 字符串.
content: C语言的字符串其实就是char型数组,所以使用字符串并不需要引用库。然而C标准库确实包含了用于对字符串进行操作的函数,使得它们看起来就像字符串而不是阵列。使用这些函数需要引用标头档string.h。
title: 文件输入/输出.
content: 在C语言中,输入和输出是经由标准函式库中的一组函数来实现的。在ANSI/ISO C中,这些函数被定义在标头档stdio.h中。
title: 标准输入/输出.
content: 有三个标准输入/输出是标准I/O库预先定义的:  下面的这个例子显示了一个过滤程序(filter program)是怎样构成的。  int main(int argc, const char * argv[])
title: 函数.
content: C语言的基本结构单位是函数。系统首先调用 main函数(主函数),通过函数的嵌套调用,再调用其他函数。函数可以是系统自带的函数,也可以是用户定义的函数。C语言中,不允许函数嵌套定义。
title: 内存管理.
content: C语言的特色之一是:程序员必须亲自处理内存的分配细节。  大多数C语言实现使用栈(Stack)来保存函数返回地址/栈帧基址、完成函数的参数传递和函数局部变量的存储。然而,在部分极特殊的平台上,使用栈并不能获得最大效率。此时的实现由编译器决定。 如果程序需要在运行的过程中动态分配内存,可以利用堆(Heap)来实现。  基本上C程序的元素存储在内存的时候有3种分配策略:  如果一个变量声明为全局变量或者是函数的静态变量,这个变量的存储将使用静态分配方式。静态分配的内存一般会被编译器放在数据段或代码段来存储,具体取决于实现。这样做的前提是,在编译时就必须确定变量的大小。 以IA32的x86平台及gcc编译器为例,全局及静态变量放在数据段的低端;全局及静态常量放在代码段的高端。  函数的自动局部变量应该随着函数的返回会自动释放(失效),这个要求在一般的体系中都是利用栈(Stack)来满足的。相比于静态分配,这时候,就不必绝对要求这个变量在编译时就必须确定变量的大小,运行时才决定也不迟,但是C89仍然要求在编译时就要确定,而C99放松了这个限制。但无论是C89还是C99,都不允许一个已经分配的自动变量运行时改变大小。  所以说C函数永远不应该返回一个局部变量的地址。  要指出的是,自动分配也属于动态分配,甚至可以用alloca函数来像分配堆(Heap)一样进行分配,而且释放是自动的。  还有一种更加特殊的情况,变量的大小在运行时有可能改变,或者虽然单个变量大小不变,变量的数目却有很大弹性,不能静态分配或者自动分配,这时候可以使用堆(Heap)来满足要求。ANSI C定义的堆操作函数是malloc、calloc、realloc和free。  使用堆(Heap)内存将带来额外的开销和风险。
title: 安全问题.
content: C语言的特色之一是:语言不负责内存边界检查,这是因为在运行时进行内存边界检查会造成性能问题,与UNIX哲学不符。此特性容易导致缓冲区溢出问题。然而,部分编译器会出于安全性的考量,提供方法以进行运行时内存边界检查。
title: 库.
content: C语言的标准文档要求了一个平台移植C语言的时候至少要实现的一些功能和封装的集合,称为“标准库”,标准库的声明头部通过预处理器命令#include进行引用。  在C89标准中:  在94年的修正版中 在C99中增加了六个函式库 以上是C语言的标准。各个系统各自又对C库函数进行的各种扩充,就浩如烟海了。如POSIX C、GNU C等。
title: 工具软件.
content: 工具软件可以帮助程序设计者避免一些程序中潜藏或容易出现的问题,例如常会造成程序未预期动作或是执行期错误的程序码。  许多语言都有自动源代码检查及审计工具,C语言也有类似工具,像是Lint。可以在程序刚写好时用Lint找出可能有问题的程序,通过Lint后再用C编译器进行编译,许多编译器也可以设定是否要针对一些可能有问题的程序码提出警告。MISRA C是一套针对嵌入式系统的法则,可主要也是避免一些可能有问题的程序码。  也有一些编译器、程序库或操作系统可以处理一些非标准C语言的功能,例如边界值检查、缓存溢出侦测、序列化及自动垃圾回收功能。  使用像Valgrind或IBM Rational Purify等软件工具,或者连结有特别malloc函式的程序库,有助于找出一些运行期内存使用的问题。
title: 保留关键字.
content: 以下是C语言的保留关键字:
title: 经典错误.
content: “void main()”的用法并不是任何标准制定的。 C语言标准语法是“int main()”,任何实现都必须支持codice_19和codice_20。 在 C++ 标准中,main的标准型态应是int,否则类型是由实现定义的。任何实现都必须支持codice_21和codice_20。

Summary: C是一种通用的程序语言,广泛用于系统软件与应用软件的开发。于1969年至1973年间,为了移植与开发UNIX操作系统,由丹尼斯·里奇与肯·汤普逊,以B语言为基础,在贝尔实验室设计、开发出来。