C语言
C,是一种通用的程序设计语言,它主要用来进行系统程序设计。具有高效、灵活、功能丰富、表达力强和移植性好等的特点,在程序员中备受青睐。
C语言是由UNIX的研制者丹尼斯•里奇(Dennis Ritchie)和肯•汤普逊(Ken Thompson)于1970年研制出的B语言的基础上发展和完善起来的。C语言可以广泛应用于不同的操作系统,例如UNIX、MS-DOS、Microsoft Windows及Linux等。C语言是一种面向过程的语言,同时具有高级语言和汇编语言的优点。在C语言的基础上发展起来的有支持多种程序设计风格的C++语言,网络上广泛使用的Java、JavaScript,微软的C#等。
1983年,美国国家标准委员会(ANSI)对C语言进行了标准化,于1983年颁布了第一个C语言标准草案(83 ANSI C),后来于1987年又颁布了另一个C语言标准草案(87 ANSI C)。最新的C语言标准是在1999年颁布并在2000年3月被ANSI采用的 C99 ,但由于未得到主流编译器厂家的支持,直到2004年C99 并未被广泛使用,增加了若干新特性后 C99 已经逐渐让C语言和C++分道扬镳。
C语言的特色
• C语言是系统程序语言。流行的操作系统核心部分几乎无一例外的选择了C语言。
• C语言保留了低级语言的特性,例如C语言允许使用指针来对任意内存做直接修改。
• C语言使用了预处理机制,使得程序员可以在编译源程序之前对程序结构做出控制。常用的预处理机制包括宏与编译选择等。 C语言的主要不足是缺乏对真实事物的描述手段。用C语言表达复杂的逻辑系统将会十分困难,而且代码量也明显高于C++语言。同时C语言过度的信赖程序员的做法也一直存在很大争议,是否允许程序员随意修改内存中的任何东西一直都是争论的焦点所在。C语言的不足可以由C语言发展而来的新的编程语言所改进。Cyclone语言拥有提防内存错误的特性。C++和Objective C提供了用于面向对象的编程结构。Java和C#在提供面向对象编程结构的同时,也提供了垃圾收集机制,这使得对内存的管理更加透明与自动化。
C語言的主要特性 • C語言保留了低階語言的特性,例如涉及記憶體的指針。 • C語言通過參數在函數裏傳遞數值。 • 使用了預處理機制,使得程式裏可以通過包含例如巨集處理的方式來處理根源程式。 • C語言提供了一套標準庫,這些庫裏提供了十分有用的功能。 但是並不是所有的這些特性都是有效的。例如,預處理通常作為一個獨立的程式被處理,這使得預處理的程式並不一定被完全編譯。
雖然C是高階語言,但是它同時擁有一些組合語言的特性,對其他的語言來說這是接近低階語言的特點。例如,在C語言裏,程式師可以對電腦記憶體進行管理。在默認的情況下,C語言不會對陣列的範圍進行檢查,也就是說即使陣列越界,C語言也不會作出錯誤提示。對電腦記憶體的管理使得程式員可以編出更快捷、更有效的程式,這對於設備驅動程式來說尤為重要。但是這也使得程式容易產生令人討厭的「臭蟲」,例如緩衝器溢出錯誤。然而,這些錯誤可以由一些工具來避免。
C語言的不足可以由從C語言發展而來的更新的編程語言改進。Cyclone語言的擁有提防對於記憶體錯誤的特性。C++和Objective C提供了用於面向物件的編程結構。Java和C#增加了面向物件的結構使得對記憶體的管理自動化。
C語言的歷史 C語言的第一次發展在1969年到1973年之間。C之所以被稱為C是因為C語言的很多特性是由一種更早的被稱為B語言的編程語言中發展而來的。 到了1973年,C語言已經可以用來編寫Unix操作系統的內核。這是第一次用C語言來編寫操作系統的內核。丹尼斯•裡奇和Brian Kernighan在1978年出版了《C程序設計語言》(The C Programming Language,經常簡稱為「白皮書」或「K&R」)。 1980年以後,貝爾實驗室使得C變得更為廣泛的流行,使得C一度成為了操作系統和應用程序編程的首選。甚至到今天,它仍被廣泛用於編寫操作系統以及作為廣泛的計算機教育的語言。2005年4月,C++之父稱C++用戶超過300萬。 1980年代晚期,布賈尼•斯特勞斯特盧普和貝爾實驗室為C語言添加了面向對象的特性.這種語言成為了C++。C++現在廣泛應用的在Microsoft Windows下運行的商業應用程序的編制,然而C仍然是UNIX世界的熱門編程語言。
C语言的版本 K&R C C不断的从它的第一版本进行改进。在1978年,Kernighan和里奇的《C程序设计语言》第一版出版。它介绍了下面的有关C语言版本的特性: • struct数据类型 • long int数据类型 • unsigned int数据类型 • 把运算符=+改为+=,依次类推。因为=+使得编译器混淆。 在以后的几年里,《C程序设计语言》一直被广泛作为C语言事实上的规范。在这本书中,C语言通常被表述成“K&R C”。(第二版的包括了ANSI C标准) K&R C通常被作为C编译器所支持的最基本的C语言部分。虽然现在的编译器并不一定都完全遵循ANSI标准,但K&R C作为C语言的最低要求仍然要编程人员掌握。但是无论怎样,现在使用广泛的C语言版本都已经与K&R C相距甚远了,因为这些编译器都使用ANSI C标准。 //.... ANSI C和ISO C(1985年) 1989年,C语言被ANSI标准化。(ANSI X3.159-1989)。标准化的一个目的是扩展K&R C。这个标准包括了一些新的特性。在K&R出版后,一些新的特征被“非官方”的加到C语言中。 • void函数 • 函数返回struct或union类型 • void *数据类型 在ANSI标准化自己的过程中,一些新的特征被加了进去。ANSI也规定一套了标准函数库。ANSI ISO(国际标准化组织)成立 ISO/IEC JTC1/SC22/WG14工作组来规定国际标准的C语言。通过对ANSI标准的少量修改,最终通过了ISO 9899:1990。随后ISO标准被ANSI采纳。 传统C语言到ANSI/ISO标准C语言的改进包括: • 增加了真正的标准库 • 新的预处理命令与特性 • 函数原型允许在函数申明中指定参数类型 • 一些新的关键字,包括const、volatile与signed • 宽字符、宽字符串与字节多字符 • 对约定规则、声明和类型检查的许多小改动与澄清
ANSI C和ISO C(1995年) 作为对标准的维护与更新,WG14工作小组在1995年对1985年颁布的标准做了两处技术修订(缺陷修复)和一个补充(扩展)。下面是1995年做出的所有修改: • 3个新的标准库头文件 iso646.h、wctype.h和wchar.h • 几个新的记号与预定义宏,用于对国际化提供更好的支持 • printf/sprintf函数一系列新的格式代码 • 大量函数和一些类型与常量,用于多字节字符和宽字节字符
C99 在ANSI标准化后,WG14小组继续致力于改进C语言。新的标准很快推出,就是ISO9899:1999(1999年出版)。这个版本就是通常提及的C99。它被ANSI于2000年三月采用。 在C99中包括的特性有: • 对编译器限制增加了,比如源程序每行要求至少支持到 4095 字节,变量名函数名的要求支持到 63 字节 (extern 要求支持到 31) • 预处理增强了。例如: o 宏支持取参数 #define Macro(...) __VA_ARGS__ o 使用宏的时候,参数如果不写,宏里用 #,## 这样的东西会扩展成空串。(以前会出错的) o 支持 // 行注释(这个特性实际上在C89的很多编译器上已经被支持了) • 增加了新关键字 restrict, inline, _Complex, _Imaginary, _Bool o 支持 long long, long double _Complex, float _Complex 这样的类型 • 支持 <: :> <% %> %: %:%: ,等等奇怪的符号替代,D&E 里提过这个 • 支持了不定长的数组。数组的长度就可以用变量了。声明类型的时候呢,就用 int a[*] 这样的写法。不过考虑到效率和实现,这玩意并不是一个新类型。所以就不能用在全局里,或者 struct union 里面,如果你用了这样的东西,goto 语句就受限制了。 • 变量声明不必放在语句块的开头,for 语句提倡这么写 for(int i=0;i<100;++i) 就是说,int i 的声明放在里面,i 只在 for 里面有效。 • 当一个类似结构的东西需要临时构造的时候,可以用 (type_name){xx,xx,xx} 这有点像 C++ 的构造函数 • 初始化结构的时候现在可以这样写: struct {int a[3], b;} hehe[] = { [0].a = {1}, [1].a = 2 }; struct {int a, b, c, d;} hehe = { .a = 1, .c = 3, 4, .b = 5} // 3,4 是对 .c,.d 赋值的 • 字符串里面,/u 支持 unicode 的字符 • 支持 16 进制的浮点数的描述 • 所以 printf scanf 的格式化串多支持了 ll / LL (VC6 里用的 I64) 对应新的 long long 类型。 • 浮点数的内部数据描述支持了新标准,这个可以用 #pragma 编译器指定 • 除了已经有的 __line__ __file__ 以外,又支持了一个 __func__ 可以得到当前的函数名 • 对于非常数的表达式,也允许编译器做化简 • 修改了对于 / % 处理负数上的定义,比如老的标准里 -22 / 7 = -3, -22 % 7 = -1 而现在 -22 / 7 = -4, -22 % 7 = 6 • 取消了不写函数返回类型默认就是 int 的规定 • 允许 struct 定义的最后一个数组写做 [] 不指定其长度描述 • const const int i; 将被当作 const int i; 处理 • 增加和修改了一些标准头文件, 比如定义 bool 的
进一步了解C C语言由函数和变量组成。C的函数就像是Fortran中的子程序和函数。 在C语言中,程序从main开始执行。main函数通过调用和控制其他函数进行工作。例如上面的printf。程序员可以自己写函数,或从库中调用函数。在上面的return 0;使得main返回一个值给调用程序的外壳,表明程序已经成功运行。 一个C语言的函数由返回值、函数名、参数列表(或void表示没有返回值)和函数体组成。函数体的语法和其它的复合的语句部分是一样的。 复合语句 C语言中的复合语句(或称语句块)的格式为: {语句;语句;……} 复合语句可以使得几个语句变成一个语句。 但一般情况下,我们不推荐这样多个语句顺序书写, 因为这样会使其可读性减弱,加大代码维护难度。 条件语句 C语言有三种条件语句形式。两种是if,另一种是switch。 两种if包括: if (条件表达式) 语句;以及 if (条件表达式) 语句; else 语句;在条件表达式中,任何非零的值表示条件为真;如果条件不满足,程序将跳过if后面的语句,直接执行if后面的语句。但是如果if后面有else,则当条件不成立时,程序跳到else处执行。 switch通常用于对几种有明确值的条件进行控制。它要求的条件值通常是整数或字符。与switch搭配的条件转移是case。使用case后面的标值,控制程序将跳到满足条件的case处一直往下执行,直到语句结束或遇到break。通常可以使用default把其它例外的情况包含进去。如果switch语句中的条件不成立,控制程序将跳到default处执行。switch是可以嵌套的。 switch (<表达式>) { case <值1> : <语句> case <值2> : <语句> default : <语句> } 循环语句 C语言有三种形式的循环语句: do <语句> while (<表达式>); while (<表达式>) <语句>; for (<表达式1> ; <表达式2> ; <表达式3>) <语句>; 在while和do中,语句将执行到表达式的值为零时结束。在do...while语句中,循环体将至少被执行一次。这三种循环结构可以互相转化: for (e1; e2; e3) s; 相当于 e1; while (e2) { s; e3; } 当循环条件一直为真时,将产生死循环。 跳转语句 跳转语句包括四种:goto,continue,break和return。 goto语句是无条件转移语句: goto 标记 标记必须在当前函数中定义,使用“标记:”的格式定义。程序将跳到标记处继续执行。由于goto容易产生阅读上的困难,所以应该尽量少用。 continue语句用在循环语句中,作用是结束当前一轮的循环,马上开始下一轮循环。 break语句用在循环语句或switch中,作用是结束当前循环,跳到循环体外继续执行。但是使用break只能跳出一层循环。在要跳出多重循环时,可以使用goto使得程序更为简洁。 当一个函数执行结束后要返回一个值时,使用return。return可以跟一个表达式或变量。如果return后面没有值,将执行不返回值。 在C99中运算符号 () [] -> . ! ++ -- (cast) 括号、成员、逻辑非、自加、自减、强制转换 ++ -- * & ~ ! + - sizeof 单目运算符 * / % 算术运算符 + - 算术运算符 << >> 位运算符 < <= > >= 关系运算符 == != 关系运算符号 & 位与 ^ 位异或 | 位或 && 逻辑与 || 逻辑或 ?: 条件运算符 = += -= *= /= %= <<= >>= &= |= ^= 赋值运算符 , 顺序运算符 数据类型 基础数据类型 注意:以下是典型的数据位长和范围。但是编译器可能使用不同的数据位长和范围。这取决于使用的编译器。请参考具体的参考手册。 在头文件
例子(*)创建了一个3*3的二维数组,初始化时有些元素并未赋值.如下: 1 0 0 1 2 3 4 5 0 为0的位置的数值是随机的. 指针 如果一个变量声明时在前面使用*号,表明这个变量是一个指针。换句话说,该变量是一个地址,而*则是取内容操作符,意思是取这个内存地址里存储的内容。指针是C语言区别于其他同时代高级语言的主要特征之一。指针是一把双刃剑,许多操作可以通过指针自然的表达,但是不正确的或者过分的使用指针又会给程序带来大量潜在的错误。 例如: int *pi; /* 指向整型数据的指针 */ int *api[3]; /* 由指向整型数据的指针构成的数组,长度为3 */ char **argv; /* 指向一个字符指针的指针 */ 储存在指针中的地址所指向的数值在程序中可以由*读取。例如,在第一个例子中,*pi是一个整型数据。这叫做引用一个指针。 另一个运算符&,叫做取地址运算符,它将返回一个变量、数组或函数的存储地址。因此,下面的例子: int i, *pi; /* int and pointer to int */ pi = &i; i和*pi在程序中可以相互交替使用,直到pi被改变成指向另一个变量的指针。 字符串 要使用字符串并不需要引用库,但是C标准库确实包含了一些用于对字符串进行操作的函数,使得它们看起来就像字符串而不是数组。使用这些函数需要引用头文件
• strcat(dest, source) - 连接两个字符串,把source加到dest末尾。 • strchr(s, c) -在字符串c中找出字符s第一次出现的位置。当没有找到时,返回Null。 • strcmp(a, b) - 比较字符串a和b的大小。如果两个字符串相同,返回0;如果a>b,返回正数;如果ab,返回正数;如果a中。 标准输入/输出 有三个标准输入/输出是预定义的: • stdin 标准输入 • stdout 标准输出 • stderr 输入输出错误 这些定义在运行过程中是自动的打开和关闭的,所以它们并不需要进行显式定义。 下面的这个例子显示了一个过滤程序(filter program)是怎样构成的。 #include