一、程序设计语言简述
程序设计语言是人与计算机交流的语言,计算机无法识别人类的语言,人如果想和计算机交流或让计算机执行人的意图,需要一套由特定的符号系统和语法系统构成的语言与之交流。
计算机语言通常分为机器语言、汇编语言和高级语言三类。
机器语言是计算机机器指令的集合。机器指令就是计算机可以正确执行的命令。电子计算机的机器指令是一列二进制数字,计算机将其转换为一列高低电平,使计算机的电子器件受到驱动,进行运算。
举个例子,我们将寄存器BX的内容送到AX中(寄存器,简单理解是CPU中可以存放数据的器件,一个CPU中有多个寄存器,AX、BX是CPU中不同寄存器的代号),机器指令如下:
1000100111011000
书写和阅读机器码程序不是一件容易的事,一旦出错更是不易查找,要记住这些抽象的二进制码更是难上加难。
早期的程序员很快发现了使用机器语言带来的麻烦,它是如此的难于辨认和记忆,于是汇编语言产生了。
汇编语言的主体是汇编指令,汇编指令和机器指令的差别在于指令的表示方法上,汇编指令是机器指令便于记忆的书写形式。同样的例子,将寄存器BX的内容送到AX中,汇编指令如下:
mov ax, bx
计算机能读懂的只有机器指令,这就需要一个能将汇编指令转换为机器指令的翻译程序,这样的程序称为编译器。
汇编语言可以高效地被计算机执行,但汇编语言高度依赖机器硬件,不同的机器系统有不同的汇编指令,换言之,用一种机器上的汇编指令编写的程序在其它种类的机器上可能不能执行,即程序移植性不好,且汇编语言的可读性仍不高,于是人们开始寻求一种既能高效被计算机执行同时又具有良好移植性的高级语言(可移植性是高级语言的基本特点),于是,作为高级语言的C语言诞生了。
二、 C语言的历史
1、 起源
C语言(C language)是贝尔实验室的Dennis Ritchie在1972年设计的,当时他正和Ken Thompson一起设计UNIX操作系统,C语言不是由Ritchie独立构想出来的,它来源于Thompson的B语言。由于开发UNIX操纵系统的需要,Ritchie的设计目标是使C语言成为一种真正实用的语言。
2、标准化
C语言在20世纪70年代(特别是1977到1979年间)持续发展,这时出现了第一本关于C语言的书,Brian Kernighan和Dennis Ritchie合著的The C Programming Language,该书于1978年出版,并迅速成为C程序员必读的“圣经”。由于当时没有C语言的正式标准,该书成为事实上的标准,编程爱好者将其称为“K&R”。该书中定义的C称为经典C。
1983年,在美国国家标准协会(ANSI)的推动下,美国开始制定本国的C语言标准,并于1989年12月正式通过,1990年,国际标准化组织(ISO)通过了此项标准,将其作为ISO/IEC 9899:1990国际标准,这一C语言版本称为C89或C90。
1999年通过的ISO/IEC 9899:1999新标准中包含了一些重要的改变,这一标准描述的C语言称为C99。
ISO/IEC 9899:2011 标准于 2011 年发布。这一版本的C语言称为C11。C11标准在 C99 基础上进行了进一步的扩展和改进,包括了一些新特性,例如泛型宏、多线程支持、_Static_assert等。C11标准对于一些早期 C标准中存在的问题进行了修正,使得 C语言更加健壮和安全。C11 标准在工业界的普及程度相对较低,但已经被一些编译器支持。
C17 是 ISO/IEC 9899:2018 标准,于 2018 年发布。C17 标准在 C11 基础上进行了一些小的修订和更新,主要是对标准库进行了一些改进和扩展。C17 标准对于C语言本身的特性并没有做出太大的改变,主要是对于一些库函数进行了更新和扩展。C17 标准目前还比较新,尚未得到广泛的应用。
本系列文章以C89版本为基础进行讲解,兼及介绍某些C99的重要特性。
3、基于C的语言
C语言对现代编程语言影响巨大,许多现代编程语言都借鉴了大量C语言的特性,如:
C++:包含了所有C语言特性,增加了类和其它面向对象编程的特性。
Java:是基于C++的,也继承了许多C的特性。
C#:由C++和Java发展起来的一种语言
Python:一种由C语言实现的解释性语言,借鉴了C语言的某些特性。
三、C语言的优缺点
C语言具是融合了控制特性的一种语言,是编程者容易采用自顶向下的设计,采用结构化编程。它执行高效,可移植性好,具有强大的功能和灵活性。其特点见表1所示。
特点 | 描述 |
C语言是一种底层语言 | C语言提供了对机器级概念(如字节,地址)的访问;提供了与计算机内置指令紧密协调的操作 |
C语言是一种小型语言 | 与其它语言相比,C语言提供了一套更有限的特性集合,C语言在很大程度上依赖一个标准函数“库” |
C语言是一种包容性语言 | C语言不像其它语言那样强制进行错误检查 |
由于以上的特点决定了C语言的优点。其优点见表2所示。
优点 | 描述 |
高效 | 这是C语言天然的优点,与最初的设计理念有关 |
可移植 | C语言自身的特性决定了它支持可移植性 |
功能强大 | C语言拥有一个庞大的数据类型和运算符集合,这使得C语言具有强大的表达能力 |
灵活 | C语言在其特性使用上的限制非常少 |
标准库 | C语言的一个突出优点是它具有标准库,其中包含了数百个可以用于输入/输出、字符串处理、存储分配及其它使用操作的函数 |
与UNIX集成 | C语言与UNIX(包括Linux)结核性方面及其强大 |
C语言与机器紧密结合的特性也同样决定了它的缺点,其缺点表3所示。
缺点 | 描述 |
C语言更容易隐藏错误 | C语言的灵活性使它编程出错的概率较高,这与汇编语言极为相似 |
C程序可能会难以理解 | 由于其自身是小型语言,其缺少本该拥有的许多特性,这些特性可以多种方式结合使用,造成其难以理解;另外,C语言过于简明扼要、过于灵活,这些都会使其代码难以理解 |
C程序可能难以修改 | 在程序设计中,如果没有充分考虑维护问题,C语言编写的大型程序可能难以修改 |
但是,它的优点远大于它的缺点,时至今日,C语言仍是系统编程和嵌入式编程不可替代的语言。
掌握C语言是计算机专业,甚至于诸多非计算机专业学习人员的必备技能。也将为进一步学习更高级的语言,如C++,Java,Python打下坚实基础。
四、如何学好C语言
想要学好C语言,除了学习任何一门学问都需要的持之以恒,勤奋努力之外,最重要的是动手操作,多做编程实验。这里建议读者要将本系列文章中出现的例题和编程习题亲自动手,反复上机实验。
五、C程序的运行
一个C语言程序的编程过程可以概括为如下几个过程。
第1步:编辑源程序
这一步的工作是将C语言的代码输入到一个纯文本文件中,并以扩展名“.c”保存,此过程可以使用任何文本编辑软件完成,如windows操作系统自带的记事本。
第2步:预处理
预处理由预处理器完成,预处理器执行以“#”开头的命令(通常称为指令),预处理器类似一个编辑器,它可以给程序添加内容,也可以对程序进行修改。
第3步:编译
编译由编译器完成,经过预处理后的程序可以进入编译器,编译器将其翻译为汇编指令。
第4步:汇编
编译出来的文件送入汇编器汇编为二进制的目标代码。
第5步:链接
目标代码中缺少两个元素,一种叫做启动代码,此代码相当于你的程序和操作系统的接口,二是缺少库例程的代码。链接器将目标代码,系统的标准启动代码和库代码链接起来,得到可执行文件。
六、 第一个C语言程序——Hello World
在“K&R”中,第一个程序及其简短,它仅仅输出一条hello, world消息。
1、第一个程序
程序2-1 helloworld.c
/* helloworld.c -- 第一个C程序 */
#include <stdio.h>
int main(void)
{
printf("hello,world\n");
return 0;
}
下面对于“程序1-1”给以简要的说明。
第1行是一个注释。
/* helloworld.c -- 第一个C程序 */ ←注释
经典C给出的注释方式是在以“/*”开头,并以“*/”结尾,其中间的部分为注释内容,注释是给人阅读的,计算机将忽略从“/*”至“*/”的所有内容,注意三个问题:
(1)注释可以换行,如下面1到4行的注释是合法的。
/*
helloworld.c -- 第一个C程序
该程序输出一条hello,world消息
*/
(2)该种注释不可以嵌套,换言之,在“/*”至“*/”之间不要包含任何的“/*”和“*/”字符串形式,否则可能会造成错误。
(3)注释在编程过程中必不可少,简明扼要的注释,对于他人或日后的自己理解代码都很重要。
另外,随着C++的流行,C中还引入了一种注释形式,其以“//”开头直至本行的结尾。如下面的第1行的注释“// helloworld.c – 第一个C程序 ”和第二行的注释“// 这是第2行,引入头文件”都是合法的。
// helloworld.c -- 第一个C程序
#include <stdio.h>
int main(void)
{
printf("hello,world\n");
return 0;
}
但是注意,该种形式的注释不能跨行。
第2行引入头文件。
#include <stdio.h> ← 包含一个头文件
该行告诉编译器包含stdio.h中的全部内容,文件stdio.h是所有C语言编译包的标准部分,这个文件对关键字输入和显示输出提供支持。这里
#include
称为指令,所有的指令都是以#开始,每条指令默认占一行,结尾没有分号或其它标记。
第3行为主函数名。
int main(void) ← 函数名
C程序包含一个或多个函数,它们是C程序的基本模块,程序1-1包含一个名为main的函数,圆括号表明main()是一个函数的名字,int表明mian()函数返回一个整数,void表示函数main()不带任何参数。C程序必须有且只有一个名为main()的函数,称为主函数,C程序总是从main()函数开始执行,main()函数可以写在源程序的任何位置,C编译器有能力自动找到它。
第4行为函数体开始。
{ ← 函数体开始
第7行为函数体结束。
} ← 函数体结束
任何函数都需要有一个函数体,表示该函数中做什么事、执行哪些操作,函数体以“{”开始,以“}”结束。
第5行是一个函数调用语句
printf("hello,world\n"); ← 函数调用
这是C语言的一个语句,必须使用分号“;”结尾。printf()为一个函数,是C标准库中的一部分,作用是在屏幕上显示“hello world!”,并且换行。在程序里使用这个函数称为调用一个函数。
符号“\n”的作用是告诉计算机换一行,也就是把光标移动到下一行的开始处。
第6行为返回语句
return 0; ← 返回
C函数可以给它的使用者返回一个数值。
2、 一个简单C语言程序的结构
通过前面简单的例子,可以总结,一个简单的C语言程序应包含函数头(就是指令),一个主函数和函数体,其中函数体中包含若干语句。格式如下:
#include <stdio.h>
int main(void)
{
statements;
reuturn 0;
}
七、 windows基本的控制台命令
当你进入控制台之初,进入的是“C:\Users\Administrator”目录,这是系统管理员工作目录,不是我们的工作目录。你可以在非C盘建立一个用于本书实验的目录,编者建立在J盘下,将其命名为“AboutC”,“J:\AboutC”便是我们的工作目录。下面给出一些基本的操作命令。
(1)进入某盘命令:“盘符:”
想要在“J:\AboutC”下工作,我们先要进入这个目录。初入控制台的时候在C盘下,只要在控制台键入:
J:
然后回车,便可以进入J盘。
(2)进入下级目录:“cd 目录名”
进入J盘后键入:
cd AboutC
然后回车,便可以进入“J:\AboutC”目录。
(3)返回上级目录:“cd..”
在“J:\AboutC”目录下,键入:
cd..
然后回车,便可以进入上级目录。再次操作
cd AboutC
进入我们的工作目录。
(4)在当前目录下创建目录:“md 新建目录名”
在“J:\AboutC”目录下创建“ch2”目录,键入
md ch2
然后回车,便可以在“J:\AboutC”目录下创建“ch2”目录。
(5)查看当前目录下文件:“dir”
在“J:\AboutC”目录下,我们创建了一个“ch2”的子目录,我们查看是否创建成功,只要键入:
dir
然后回车,便可以查看当前目录下的文件和子目录信息。如下图1所示。
图1 dir命令查看当前目录
为了给出完整信息,我们手动在“J:\AboutC\ch2”目录中建立一个“example.txt”文件,和一个名为“mydir”的文件夹,再次键入
dir
命令,然后回车,信息如下图2所示。
图2 目录信息
标称<DIR>的行给出的信息,表示该文件为一个目录,“.”代表本级目录,“..”代表上级目录,未有标称者为一个文件,“example.txt”为文件名,前面的0表示文件内容大小,因为没有输入任何内容,其大小为0。
(6)删除文件:“del 文件名”
在“J:\AboutC\ch2”目录下,删除example.txt文件,键入
del example.txt
然后回车,便可以删除该文件。
(7)删除文件夹:“rmdir /Q /S 目录名”
在“J:\AboutC\ch2”目录下,键入
rmdir /Q /S mydir
然后回车,便可以将mydir文件夹删除。
注意,该命令要慎用,它会删除拟删除目录下的所有文件和文件夹,并且没有任何提示。
八、完整的实验过程
下面,我们基于程序2-1给出完整的实验过程。
第一步:编辑程序
打开“Visual Studio Code”软件,新建文件,保存在“J:\AboutC\ch2”目录下,名为“helloworld.c”,键入程序1-1的内容,然后保存。
第二步:调出控制台
调出控制台,进入“J:\AboutC\ch2”目录。
第三步:预处理
预处理命令如下:
gcc -E helloworld.c -o helloworld.i
此时,便在“J:\AboutC\ch2”目录下生产了“helloworld.i”文件,你可以用记事本查看一下该文件,它将“#include <stdio.h>”扩展成了四百余行的内容,实际这个过程就是将系统的“stdio.h”文件复制到“helloworld.c”文件中,生成的“helloworld.i”文件。
第四步:编译
编译命令如下:
gcc -S helloworld.i -o helloworld.s
此时,便在“J:\AboutC\ch2”目录下生产了“helloworld.s”文件,你可以用记事本查看一下该文件,你可能看不懂这个文件内容,这些是汇编语言代码。
第五步:汇编
汇编命令如下:
gcc -c helloworld.s -o helloworld.o
此时,便在“J:\AboutC\ch2”目录下生产了“helloworld.o”文件,这个文件称为目标文件,其代码就是目标代码,你可以用记事本查看一下该文件,它将是一堆你看不懂的乱码,这是一个二进制文件。
第六步:链接
链接命令如下:
gcc helloworld.o -o helloworld.exe
此时,便在“J:\AboutC\ch2”目录下生产了“helloworld.exe”文件,它便是一个可执行文件。
第七步:执行
在控制台直接键入
helloworld.exe
便可以执行该程序,我们将看到最后的结果,如图3所示。
图3 程序2-1的执行结果
在以上诸步骤中,你可以将步骤三至步骤六合并为一个步骤,命令为:
gcc helloworld.c -o helloworld.exe
在日后的实验中,你可以直接使用上面的命令,它将不再生成中间的文件。
在编译的过程中,如果你的程序存在语法问题,即你的程序不完全符合C标准的语法规则,会出现两类提示信息,一类是警告(warning)信息,这不影响生成可执行文件,但是执行后可能无法得到预期的效果;第二类是严重的错误(error)信息,这将导致无法生成可执行文件,这时你需要根据报错信息,进行修改。
警告和错误信息的种类比较繁杂,需要日常积累,其实,试错的过程何尝不是绝佳的学习过程呢!举个例子,如下的程序:
程序2-2 helloworld_error.c
/* helloworld_error.c -- 一个错误程序 */
#include <stdio.h>
int main(void)
{
printf("hello,world\n")
return 0;
}
进行编译的时候,就会报告错误信息,如下图4 所示。
图4 报错信息
编译器提示,“In function ‘main’”(即在函数main()中),“5:28:”(即第5行第28列)位置,“before return”(即“return”前)缺少一个分号“;”。你看出问题了吗?是的,在第五行
printf("hello,world\n")
语句后缺少了一个分号。
注意,C语言编译器的报错信息指出的行位置,一般为实际出现错误的下一行。
最后总结:
通过上面的示例,我们可以看到,gcc的命令格式为:
gcc [选项] 输入文件名 -o 输出文件名