C语言 编辑

C语言C语言

C语言是一种较早的程序设计语言,诞生于1972年的贝尔实验室。1972 年,Dennis Ritchie 设计了C语言,它继承了B语言的许多思想,并加入了数据类型的概念及其他特性。 尽管C 语言是与 UNIX 操作系统一起被开发出来的,但它不只支持UNIX。C是一种通用(广泛可用)的编程语言。程序设计语言实与编译器是一体的,C编译器有著名的GCC(GNU Compiler Collection)等。广泛应用于底层开发。C语言能以简易的方式编译、处理低级存储器。C语言是仅产生少量的机器语言以及不需要任何运行环境支持便能运行的高效率程序设计语言。尽管C语言提供了许多低级处理的功能,但仍然保持着跨平台的特性,以一个标准规格写出的C语言程序可在包括类似嵌入式处理器以及超级计算机等作业平台的许多计算机平台上进行编译。最新的C语言标准是C23。

简介

编辑
C语言是一门面向过程的计算机编程语言,与C++、C#、Java等面向对象编程语言有所不同。C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、仅产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。C语言描述问题比汇编语言迅速、工作量小、可读性好、易于调试、修改和移植,而代码质量与汇编语言相当。C语言一般只比汇编语言代码生成的目标程序效率低10%-20%。因此,C语言可以编写系统软件。

当前阶段,在编程领域中,C语言的运用非常之多,它兼顾了高级语言和汇编语言的优点,相较于其它编程语言具有较大优势。计算机系统设计以及应用程序编写是C语言应用的两大领域。同时,C语言的普适较强,在许多计算机操作系统中都能够得到适用,且效率显著。

C语言拥有经过了漫长发展历史的完整的理论体系,在编程语言中具有举足轻重的地位。

发展历史

编辑
C语言诞生于美国的贝尔实验室,由丹尼斯·里奇(Dennis MacAlistair Ritchie)以肯尼斯·蓝·汤普森(Kenneth Lane Thompson)设计的B语言为基础发展而来,在它的主体设计完成后,汤普森和里奇用它完全重写了UNIX,且随着UNIX的发展,c语言也得到了不断的完善。为了利于C语言的全面推广,许多专家学者和硬件厂商联合组成了C语言标准委员会,并在之后的1989年,诞生了第一个完备的C标准,简称“C89”,也就是“ANSI C”,截至2020年,最新的C语言标准为2018年6月发布的“C18”。

C语言之所以命名为C,是因为C语言自Ken Thompson发明的B语言,而B语言则源自BCPL语言。

1967年,剑桥大学的Martin Richards对CPL语言进行了简化,于是产生了BCPL(Basic Combined Programming Language)语言。

20世纪60年代,美国AT&T公司贝尔实验室(AT&T Bell Laboratories)的研究员肯·汤普森(Kenneth Lane Thompson)闲来无事,手痒难耐,想玩一个他自己编的,模拟在太阳系航行的电子游戏——Space Travel。他背着老板,找到了台空闲的小型计算机——PDP-7。但这台电脑没有操作系统,而游戏必须使用操作系统的一些功能,于是他着手为PDP-7开发操作系统。后来,这个操作系统被命名为——UNICS(Uniplexed Information and Computing Service)。

1969年,美国贝尔实验室的Ken Thompson,以BCPL语言为基础,设计出很简单且很接近硬件的B语言(取BCPL的首字母),并且用B语言写了初版UNIX操作系统(叫UNICS)。

1971年,同样酷爱Space Travel的丹尼斯·里奇为了能早点儿玩上游戏,加入了汤普森的开发项目,合作开发UNIX。他的主要工作是改造B语言,使其更成熟。

1972年,美国贝尔实验室的丹尼斯·里奇在B语言的基础上最终设计出了一种新的语言,他取了BCPL的第二个字母作为这种语言的名字,这就是C语言。

1973年初,C语言的主体完成。汤普森和里奇迫不及待地开始用它完全重写了UNIX。此时,编程的乐趣使他们已经完全忘记了那个“Space Travel”,一门心思地投入到了UNIX和C语言的开发中。随着UNIX的发展,C语言自身也在不断地完善。直到2020年,各种版本的UNIX内核和周边工具仍然使用C语言作为最主要的开发语言,其中还有不少继承汤普逊和里奇之手的代码。

在开发中,他们还考虑把UNIX移植到其他类型的计算机上使用。C语言强大的移植性(Portability)在此显现。机器语言和汇编语言都不具有移植性,为x86开发的程序,不可能在Alpha、SPARC和ARM等机器上运行。而C语言程序则可以使用在任意架构的处理器上,只要那种架构的处理器具有对应的C语言编译器和库,然后将C源代码编译、连接成目标二进制文件之后即可在哪种架构的处理器运行。

1977年,丹尼斯·里奇发表了不依赖于具体机器系统的C语言编译文本《可移植的C语言编译程序》。

C语言继续发展,在1982年,很多有识之士和美国国家标准协会(ANSI)为了使C语言健康地发展下去,决定成立C标准委员会,建立C语言的标准。委员会由硬件厂商、编译器及其他软件工具生产商、软件设计师、顾问、学术界人士、C语言作者和应用程序员组成。1989年,ANSI发布了第一个完整的C语言标准——ANSI X3.159-1989,简称“C89”,不过人们也习惯称其为“ANSI C”。C89在1990年被国际标准化组织(International Standard Organization,ISO)一字不改地采纳,ISO官方给予的名称为:ISO/IEC 9899,所以ISO/IEC9899:1990也通常被简称为“C90”。1999年,在做了一些必要的修正和完善后,ISO发布了新的C语言标准,命名为ISO/IEC 9899:1999,简称“C99”。 在2011年12月8日,ISO又正式发布了新的标准,称为ISO/IEC9899:2011,简称为“C11”。

2018年6月,ISO发布了ISO/IEC9899:2018,简称“C18”(或“C17”) 。C18标准没有引入新的语言特性,只对C11进行了补充与修正。

2022年9月3日,ISO于Open Standards(计算机标准开放组织)网站上发布了新的C语言标准定稿,称为 ISO/IEC 9899:2023,简称“C23”。

语言特点

编辑

主要特点

C语言是一种结构化语言,它有着清晰的层次,可按照模块的方式对程序进行编写,十分有利于程序的调试,且c语言的处理和表现能力都非常的强大,依靠非常全面的运算符和多样的数据类型,可以轻易完成各种数据结构的构建,通过指针类型更可对内存直接寻址以及对硬件进行直接操作,因此既能够用于开发系统程序,也可用于开发应用软件。通过对C语言进行研究分析,总结出其主要特点如下:

(1)简洁的语言

C语言包含的各种控制语句仅有9种,关键字也只有32个,程序的编写要求不严格且以小写字母为主,对许多不必要的部分进行了精简。实际上,语句构成与硬件有关联的较少,且C语言本身不提供与硬件相关的输入输出、文件管理等功能,如需此类功能,需要通过配合编译系统所支持的各类库进行编程,故c语言拥有非常简洁的编译系统。

(2)具有结构化的控制语句

C语言是一种结构化的语言,提供的控制语句具有结构化特征,如for语句、if...else语句和switch语句等。可以用于实现函数的逻辑控制,方便面向过程的程序设计。

(3)丰富的数据类型

C语言包含的数据类型广泛,不仅包含有传统的字符型、整型、浮点型、数组类型等数据类型,还具有其他编程语言所不具备的数据类型,其中以指针类型数据使用最为灵活,可以通过编程对各种数据结构进行计算。

(4)丰富的运算符

C语言包含34个运算符,它将赋值、括号等均视作运算符来操作,使C程序的表达式类型和运算符类型均非常丰富。

(5)可对物理地址进行直接操作

C语言允许对硬件内存地址进行直接读写,以此可以实现汇编语言的主要功能,并可直接操作硬件。C语言不但具备高级语言所具有的良好特性,又包含了许多低级语言的优势,故在系统软件编程领域有着广泛的应用。

(6)代码具有较好的可移植性

C语言是面向过程的编程语言,用户只需要关注所被解决问题的本身,而不需要花费过多的精力去了解相关硬件,且针对不同的硬件环境,在用C语言实现相同功能时的代码基本一致,不需或仅需进行少量改动便可完成移植,这就意味着,对于一台计算机编写的C程序可以在另一台计算机上轻松地运行,从而极大的减少了程序移植的工作强度。

(7)可生成高质量、目标代码执行效率高的程序

与其他高级语言相比,C语言可以生成高质量和高效率的目标代码,故通常应用于对代码质量和执行效率要求较高的嵌入式系统程序的编写。

特有特点

C语言是普适性最强的一种计算机程序编辑语言,它不仅可以发挥出高级编程语言的功用,还具有汇编语言的优点,因此相对于其它编程语言,它具有自己独特的特点。具体体现为以下三个方面:

其一,广泛性。C语言的运算范围的大小直接决定了其优劣性。C语言中包含了34种运算符,因此运算范围要超出许多其它语言,此外其运算结果的表达形式也十分丰富。此外,C语言包含了字符型、指针型等多种数据结构形式,因此,更为庞大的数据结构运算它也可以应付。

其二,简洁性。9类控制语句和32个关键字是C语言所具有的基础特性,使得其在计算机应用程序编写中具有广泛的适用性,不仅可以适用广大编程人员的操作,提高其工作效率,同时还能够支持高级编程,避免了语言切换的繁琐。

其三,结构完善。C语言是一种结构化语言,它可以通过组建模块单位的形式实现模块化的应用程序,在系统描述方面具有显著优势,同时这一特性也使得它能够适应多种不同的编程要求,且执行效率高。

缺点

1.C语言的缺点主要表现为数据的封装性弱,这一点使得C在数据的安全性上有很大缺陷,这也是C和C++的一大区别。

2.C语言的语法限制不太严格,对变量的类型约束不严格,影响程序的安全性,对数组下标越界不作检查等。从应用的角度,C语言比其他高级语言较难掌握。也就是说,对用C语言的人,要求对程序设计更熟练一些。

语言组成

编辑

基本构成

数据类型

C的数据类型包括:整型(short、int、long、long long)、字符型(char)、实型或浮点型(单精度float和双精度double)、枚举类型(enum)、数组类型、结构体类型(struct)、共用体类型(union)、指针类型和空类型(void)。

常量与变量

常量其值不可改变,符号常量名通常用大写。

变量是以某标识符为名字,其值可以改变的量。标识符是以字母或下划线开头的一串由字母、数字或下划线构成的序列,请注意第一个字符必须为字母或下划线,否则为不合法的变量名。变量在编译时为其分配相应存储单元。

数组

如果一个变量名后面跟着一个有数字的中括号,这个声明就是数组声明。字符串也是一种数组。它们以ASCII的NULL作为数组的结束。要特别注意的是,中括号内的索引值是从0算起的。

指针

如果一个变量声明时在前面使用*号,表明这是个指针型变量。换句话说,该变量存储一个地址,而*(此处特指单目运算符*,下同。C语言中另有双目运算符*)则是取内容操作符,意思是取这个内存地址里存储的内容。指针是C语言区别于其他同时代高级语言的主要特征之一。

指针不仅可以是变量的地址,还可以是数组、数组元素、函数的地址。通过指针作为形式参数可以在函数的调用过程得到一个以上的返回值,不同于return(z)这样的仅能得到一个返回值。

指针是一把双刃剑,许多操作可以通过指针自然的表达,但是不正确的或者过分的使用指针又会给程序带来大量潜在的错误。

字符串

C语言的字符串其实就是以'\0'字符结尾的char型数组,使用字符型并不需要引用库,但是使用字符串就需要C标准库里面的一些用于对字符串进行操作的函数。它们不同于字符数组。使用这些函数需要引用头文件。

文件输入/输出

在C语言中,输入和输出是经由标准库中的一组函数来实现的。在ANSI C中,这些函数被定义在头文件;中。

标准输入/输出

有三个标准输入/输出是标准I/O库预先定义的:

stdin标准输入

stdout标准输出

stderr输入输出错误

运算

C语言的运算非常灵活,功能十分丰富,运算种类远多于其它程序设计语言。在表达式方面较其它程序语言更为简洁,如自加、自减、逗号运算和三目运算使表达式更为简单,但初学者往往会觉的这种表达式难读,关键原因就是对运算符和运算顺序理解不透不全。当多种不同运算组成一个运算表达式,即一个运算式中出现多种运算符时,运算的优先顺序和结合规则就会显得十分重要。

关键字

关键字又称为保留字,就是已被C语言本身使用,不能作其它用途使用的字。例如关键字不能用作变量名、函数名等标识符。

数据类型关键字

short:修饰int,短整型数据,可省略被修饰的int。(K&R时期引入)

long:修饰int,长整型数据,可省略被修饰的int。(K&R时期引入)

long long:修饰int,超长整型数据,可省略被修饰的int。(C99标准新增)

signed:修饰整型数据,有符号数据类型。(C89标准新增)

unsigned:修饰整型数据,无符号数据类型。(K&R时期引入)

restrict:用于限定和约束指针,并表明指针是访问一个数据对象的初始且唯一的方式。(C99标准新增)

复杂类型关键字

struct:结构体声明。(K&R时期引入)

union:联合体声明。(K&R时期引入)

enum:枚举声明。(C89标准新增)

typedef:声明类型别名。(K&R时期引入)

sizeof:得到特定类型或特定类型变量的大小。(K&R时期引入)

inline:内联函数用于取代宏定义,会在任何调用它的地方展开。(C99标准新增)

存储级别关键字

auto:指定为自动变量,由编译器自动分配及释放。通常在栈上分配。与static相反。当变量未指定时默认为auto。(K&R时期引入)

static:指定为静态变量,分配在静态变量区,修饰函数时,指定函数作用域为文件内部。(K&R时期引入)

register:指定为寄存器变量,建议编译器将变量存储到寄存器中使用,也可以修饰函数形参,建议编译器通过寄存器而不是堆栈传递参数。(K&R时期引入)

extern:指定对应变量为外部变量,即标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。(K&R时期引入)

const:指定变量不可被当前线程改变(但有可能被系统或其他线程改变)。(C89标准新增)

volatile:指定变量的值有可能会被系统或其他线程改变,强制编译器每次从内存中取得该变量的值,阻止编译器把该变量优化成寄存器变量。(C89标准新增)

流程控制关键字

跳转结构

return:用在函数体中,返回特定值(如果是void类型,则不返回函数值)。(K&R时期引入)

continue:结束当前循环,开始下一轮循环。(K&R时期引入)

break:跳出当前循环或switch结构。(K&R时期引入)

goto:无条件跳转语句。(K&R时期引入)

分支结构

if:条件语句,后面不需要放分号。(K&R时期引入)

else:条件语句否定分支(与if连用)。(K&R时期引入)

switch:开关语句(多重分支语句)。(K&R时期引入)

case:开关语句中的分支标记,与switch连用。(K&R时期引入)

default:开关语句中的“其他”分支,可选。(K&R时期引入)

语法结构

顺序结构

顺序结构的程序设计是最简单的,只要按照解决问题的顺序写出相应的语句就行,它的执行顺序是自上而下,依次执行。

例如:a=3,b=5,现交换a、b的值,这个问题就好像交换两个杯子里面的水,这当然要用到第三个杯子,假如第三个杯子是c,那么正确的程序为:c=a;a=b;b=c,执行结果是a=5,b=c=3,如果改变其顺序,写成:a=b;c=a;b=c;则执行结果就变成a=b=c=5,不能达到预期的目的,初学者最容易犯这种错误。顺序结构可以独立使用构成一个简单的完整程序,常见的输入、计算、输出三步曲的程序就是顺序结构,例如计算圆的面积,其程序的语句顺序就是输入圆的半径r,计算s=3.14159*r*r,输出圆的面积s。不过大多数情况下顺序结构都是作为程序的一部分,与其它结构一起构成一个复杂的程序,例如分支结构中的复合语句、循环结构中的循环体等。

选择结构

顺序结构的程序虽然能解决计算、输出等问题,但不能做判断再选择。对于要先做判断再选择的问题就要使用选择结构。选择结构的执行是依据一定的条件选择执行路径,而不是严格按照语句出现的物理顺序。选择结构的程序设计方法的关键在于构造合适的分支条件和分析程序流程,根据不同的程序流程选择适当的选择语句。选择结构适合于带有逻辑或关系比较等条件判断的计算,设计这类程序时往往都要先绘制其程序流程图,然后根据程序流程写出源程序,这样做把程序设计分析与语言分开,使得问题简单化,易于理解。程序流程图是根据解题分析所绘制的程序执行流程图。

循环结构

循环结构可以减少源程序重复书写的工作量,用来描述重复执行某段算法的问题,这是程序设计中最能发挥计算机特长的程序结构,C语言中提供四种循环,即goto循环、while循环、do while循环和for循环。四种循环可以用来处理同一问题,一般情况下它们可以互相代替换,但一般不提倡用goto循环,因为强制改变程序的顺序经常会给程序的运行带来不可预料的错误。

特别要注意在循环体内应包含趋于结束的语句(即循环变量值的改变),否则就可能成了一个死循环,这是初学者的一个常见错误。

三个循环的异同点:用while和do while循环时,循环变量的初始化的操作应在循环体之前,而for循环一般在语句1中进行的;while循环和for循环都是先判断表达式,后执行循环体,而do while循环是先执行循环体后判断表达式,也就是说do while的循环体最少被执行一次,而while循环和for就可能一次都不执行。另外还要注意的是这三种循环都可以用break语句跳出循环,用continue语句结束本次循环,而goto语句与if构成的循环,是不能用break和continue语句进行控制的。

顺序结构、分支结构和循环结构并不彼此孤立的,在循环中可以有分支、顺序结构,分支中也可以有循环、顺序结构,其实不管哪种结构,均可广义的把它们看成一个语句。在实际编程过程中常将这三种结构相互结合以实现各种算法,设计出相应程序,但是要编程的问题较大,编写出的程序就往往很长、结构重复多,造成可读性差,难以理解,解决这个问题的方法是将C程序设计成模块化结构。

具体内容:

for循环

for循环结构是c语言中最具有特色的循环语句,使用最为灵活方便,它的一般形式为:

for(表达式1;表达式2;表达式3)循环体语句。(其中;不能省略)

表达式1为初值表达式,用于在循环开始前为循环变量赋初值。

表达式2是循环控制逻辑表达式,它控制循环执行的条件,决定循环的次数。

表达式3为循环控制变量修改表达式,它使for循环趋向结束。

循环体语句是在循环控制条件成立的情况下被反复执行的语句。

但是在整个for循环过程中,表达式1只计算一次,表达式2和表达式3则可能计算多次,也可能一次也不计算。循环体可能多次执行,也可能一次都不执行。

先执行表达式2,然后执行循环结构,最后表达式3,一直这样循环下去。

for循环语句是c语言种功能最为强大的语句,甚至在一定程度上可以代替其他的循环语句。

do

do循环结构,do 1 while(2);的执行顺序是1->2->1...循环,2为循环条件。

while

while循环结构,while(1) 2;的执行顺序是1->2->1...循环,1为循环条件

以上循环语句,当循环条件表达式为真则继续循环,为假则跳出循环。

函数

C程序是由一组变量或是函数的外部对象组成的。函数是一个自我包含的完成一定相关功能的执行代码段。我们可以把函数看成一个“黑盒子”,你只要将数据送进去就能得到结果,而函数内部究竟是如何工作的,外部程序是不知道的。外部程序所知道的仅限于输入给函数什么以及函数输出什么。函数提供了编制程序的手段,使之容易读、写、理解、排除错误、修改和维护。

C程序中函数的数目实际上是不限的,如果说有什么限制的话,那就是,一个C程序中必须至少有一个函数,而且其中必须有一个并且仅有一个以main为名的函数,这个函数称为主函数,整个程序从这个主函数开始执行。

C语言程序鼓励和提倡人们把一个大问题划分成一个个子问题,对应于解决一个子问题编制一个函数,因此,C语言程序一般是由大量的小函数而不是由少量大函数构成的,即所谓“小函数构成大程序”。这样的好处是让各部分相互充分独立,并且任务单一。因而这些充分独立的小模块也可以作为一种固定规格的小“构件”,用来构成新的大程序。

C语言发展的那么多年来,用C语言开发的系统和程序浩如烟海。在发展的同时也积累了很多能直接使用的库函数。

ANSI C提供了标准C语言库函数。

C语言初学者比较喜欢的Turbo C 2.0提供了400多个运行时函数,每个函数都完成特定的功能,用户可随意调用。这些函数总体分成输入输出函数、数学函数、字符串和内存函数、与BIOS和DOS有关的函数、字符屏幕和图形功能函数、过程控制函数、目录函数等。

Windows系统所提供的Windows SDK中包含了数千个跟Windows应用程序开发相关的函数。其它操作系统,如Linux,也同样提供了大量的函数让应用程序开发人员调用。

作为程序员应尽量熟悉目标平台库函数其功能。这样才能游刃有余地开发特定平台的应用程序。比如作为Windows应用程序的开发者,应尽量熟悉Windows SDK;作为Linux应用程序开发者,应尽量熟悉Linux系统调用和POSIX函数规范。

运算符号

比较特别的是,比特右移(>>)运算符可以是算术(左端补最高有效位)或是逻辑(左端补0)位移。例如,将11100011右移3比特,算术右移后成为11111100,逻辑右移则为00011100。因算术比特右移较适于处理带负号整数,所以几乎所有的编译器都是算术比特右移。

运算符的优先级从高到低大致是:单目运算符、算术运算符、关系运算符、逻辑运算符、条件运算符、赋值运算符(=)和逗号运算符。

编程开发

编辑

编译器

GCC,GNU组织开发的开源免费的编译器

MinGW,Windows操作系统下的GCC

Clang,开源的BSD协议的基于LLVM的编译器

Visual C++ :: cl.exe,Microsoft VC++自带的编译器

集成开发环境

Code::Blocks,开源免费的C/C++ IDE

CodeLite,开源、跨平台的C/C++集成开发环境

Dev-C++,可移植的C/C++IDE

C-Free

Light Table

Visual Studio系列

示例程序

编辑
下面是一个在标准输出设备(stdout)上,印出"Hello, world!"字符串的简单程序。类似的程序,通常作为初学编程语言时的第一个程序:

#include <stdio.h> int main(){     printf("Hello, World! \n");     return 0; }

  • 1.

    程序的第一行#include是预处理器指令,告诉C编译器在实际编译之前要包含stdio.h文件。

  • 2.

    下一行int main()是主函数,程序从这里开始执行。

  • 3.

    下一行printf(...)是C中另一个可用的函数,会在屏幕上显示消息"Hello, World!"。

  • 4.

    下一行return 0;终止main()函数,并返回值0。

    #include <stdio.h> int main(){ void move(int *xa,int *xb); int a,b; printf("请输入两个数a,b:"); scanf_s("%d,%d",&a,&b); move(&a,&b); printf("a=%d,b=%d\n",a,b); return 0; } void move(int *xa,int *xb){ int a; a=*xa; *xa=*xb; *xb=a; }

    1.程序的第三行是对函数的声明,声明了一个有两个指针参数的无返回值函数。由于函数的定义在调用函数处下,所以必须进行声明。

    2.第四行是变量的定义,定义了两个整型变量。

    3.第六行输入两个数,必须以1,0这样中间有逗号的方式输入。把两个数放入变量a,b内。"&"是取地址运算符。

    4.第七行调用move函数,将两个变量的值转换。

    5.main函数后就是move函数的定义。

    从这个例子可以看出,指针变量可以获得更多的返回值,这样是非常方便的。

  • 下一篇 翻译

    上一篇 计算机语言