# C语言概述 C 语言是一种通用的高级语言,最初是由丹尼斯·里奇在贝尔实验室为开发 UNIX 操作系统而设计的。C 语言最开始是于 1972 年在 DEC PDP-11 计算机上被首次实现。 在 1978 年,布莱恩·柯林汉(Brian Kernighan)和丹尼斯·里奇(Dennis Ritchie)制作了 C 的第一个公开可用的描述,现在被称为 K&R 标准。 UNIX 操作系统,C编译器,和几乎所有的 UNIX 应用程序都是用 C 语言编写的。由于各种原因,C 语言现在已经成为一种广泛使用的专业语言。 - 易于学习。 - 结构化语言。 - 它产生高效率的程序。 - 它可以处理底层的活动。 - 它可以在多种计算机平台上编译。 ## 关于 C - C 语言是为了编写 UNIX 操作系统而被发明的。 - C 语言是以 B 语言为基础的,B 语言大概是在 1970 年被引进的。 - C 语言标准是于 1988 年由美国国家标准协会(ANSI,全称 American National Standard Institute)制定的。 - 截至 1973 年,UNIX 操作系统完全使用 C 语言编写。 - 目前,C 语言是最广泛使用的系统程序设计语言。 - 大多数先进的软件都是使用 C 语言实现的。 - 当今最流行的 Linux 操作系统和 RDBMS MySQL 都是使用 C 语言编写的。 ## 为什么要使用 C? C 语言最初是用于系统开发工作,特别是组成操作系统的程序。由于 C 语言所产生的代码运行速度与汇编语言编写的代码运行速度几乎一样,所以采用 C 语言作为系统开发语言。下面列举几个使用 C 的实例: - 操作系统 - 语言编译器 - 汇编器 - 文本编辑器 - 打印假脱机 - 网络驱动器 - 现代程序 - 数据库 - 语言解释器 - 实体工具 ### C语言的简洁 **C语言仅有32个关键字:** ![2016-05-31_173134](assets/clip_image006.jpg) **9种控制语句:** ![2016-05-31_173306](assets/clip_image008.jpg) **34种运算符:** ![2016-05-31_173424](assets/clip_image010.jpg) ## C 程序 一个 C 语言程序,可以是 3 行,也可以是数百万行,它可以写在一个或多个扩展名为 **"`.c`"** 的文本文件中,例如,*`hello.c`*。您可以使用 **"vi"**、**"vim"** 或任何其他文本编辑器来编写您的 C 语言程序。 本教程假定您已经知道如何编辑一个文本文件,以及如何在程序文件中编写源代码。 ### 第一个C语言程序 ``` #include int *main*() { //这是第一个C语言代码 *printf*("hello world\n"); return 0; } ``` > C语言的源代码文件是一个普通的文本文件,但扩展名必须是.c ### C 编译器 写在源文件中的源代码是人类可读的源。它需要"编译",转为机器语言,这样 CPU 可以按给定指令执行程序。 C 语言编译器用于把源代码编译成最终的可执行程序。这里假设您已经对编程语言编译器有基本的了解了。 最常用的免费可用的编译器是 GNU 的 C/C++ 编译器,如果您使用的是 HP 或 Solaris,则可以使用各自操作系统上的编译器。 以下部分将指导您如何在不同的操作系统上安装 GNU 的 C/C++ 编译器。这里同时提到 C/C++,主要是因为 GNU 的 `gcc` 编译器适合于 C 和 C++ 编程语言。 **编译命令格式如下:** ``` gcc [-option1] ... g++ [-option1] ... ``` - 命令、选项和源文件之间使用空格分隔 - 一行命令中可以有零个、一个或多个选项 - 文件名可以包含文件的绝对路径,也可以使用相对路径 - 如果命令中不包含输出可执行文件的文件名,可执行文件的文件名会自动生成一个默认名,Linux平台为`a.out`,Windows平台为`a.exe` **gcc**、**g++**编译常用选项说明: | **选项** | **含义** | | -------- | -------------------------- | | -o file | 指定生成的输出文件名为file | | -E | 只进行预处理 | | -S(大写) | 只进行预处理和编译 | | -c(小写) | 只进行预处理、编译和汇编 | #### UNIX/Linux 上的安装 如果您使用的是 **Linux 或 UNIX**,请在命令行使用下面的命令来检查您的系统上是否安装了 GCC: ``` $ gcc -v ``` 如果您的计算机上已经安装了 GNU 编译器,则会显示如下消息: ``` Using built-in specs. Target: i386-redhat-linux Configured with: ../configure --prefix=/usr ....... Thread model: posix gcc version 4.1.2 20080704 (Red Hat 4.1.2-46) ``` 如果未安装 GCC,那么请按照 [http://gcc.gnu.org/install/](https://gcc.gnu.org/install/) 上的详细说明安装 GCC。 #### Mac OS 上的安装 如果您使用的是 Mac OS X,最快捷的获取 GCC 的方法是从苹果的网站上下载 Xcode 开发环境,并按照安装说明进行安装。一旦安装上 Xcode,您就能使用 GNU 编译器。 Xcode 目前可从 [developer.apple.com/technologies/tools/](https://developer.apple.com/technologies/tools/) 上下载。 #### Windows 上的安装 为了在 Windows 上安装 GCC,您需要安装 MinGW。为了安装 MinGW,请访问 MinGW 的主页 [www.mingw.org](https://www.mingw.org/),进入 MinGW 下载页面,下载最新版本的 MinGW 安装程序,命名格式为 MinGW-.exe。 当安装 MinWG 时,您至少要安装 gcc-core、gcc-g++、binutils 和 MinGW runtime,但是一般情况下都会安装更多其他的项。 添加您安装的 MinGW 的 bin 子目录到您的 **PATH** 环境变量中,这样您就可以在命令行中通过简单的名称来指定这些工具。 ```{tip} 当完成安装时,您可以从 Windows 命令行上运行 gcc、g++、ar、ranlib、dlltool 和其他一些 GNU 工具。 ``` ### 代码分析 针对前面写的[hello.c](#第一个C语言程序); 我们先来简单了解下每一个语句的作用 **include头文件包含** - `#include`的意思是头文件包含,`#include `代表包含`stdio.h`这个头文件 - 使用C语言库函数需要提前包含库函数对应的头文件,如这里使用了`printf()`函数,需要包含`stdio.h`头文件 ``` {admonition} #include< > 与 #include ""的区别: - < > 表示系统直接按系统指定的目录检索 - "" 表示系统先在 "" 指定的路径(没写路径代表当前路径)查找头文件,如果找不到,再按系统指定的目录检索 ``` `stdio.h`是在操作系统的系统目录下: ![2016-05-31_231146](assets/clip_image036.jpg) **main函数** - 一个完整的C语言程序,是由一个、且只能有一个`main()`函数(又称主函数,必须有)和若干个其他函数结合而成(可选)。 - main函数是C语言程序的入口,程序是从main函数开始执行。 **{} 括号,程序体和代码块** - {}叫代码块,一个代码块内部可以有一条或者多条语句 - C语言每句可执行代码都是"**;**"分号结尾 - 所有的#开头的行,都代表预编译指令,预编译指令行结尾是没有分号的 - 所有的可执行语句必须是在代码块里面 **注释** - `//`叫行注释,注释的内容编译器是忽略的,注释主要的作用是在代码中加一些说明和解释,这样有利于代码的阅读 - `/**/`叫块注释 - 块注释是C语言标准的注释方法 - 行注释是从C++语言借鉴过来的 **printf函数** - `printf`是C语言库函数,功能是向标准输出设备输出一个字符串 - `printf(“hello world\n”);` ` //\n`的意思是回车换行 **return语句** - return代表函数执行完毕,返回return代表函数的终止 - 如果main定义的时候前面是int,那么return后面就需要写一个整数;如果main定义的时候前面是void,那么return后面什么也不需要写 - 在main函数中return 0代表程序执行成功,return -1代表程序执行失败 - int main()和void main()在C语言中是一样的,但C++只接受int main这种定义方式 ## C语言编译过程 ### C程序编译步骤 C代码编译成可执行程序经过4步: 1. **预处理**:宏定义展开、头文件展开、条件编译等,同时将代码中的注释删除,这里并不会检查语法 2. **编译**:检查语法,将预处理后文件编译生成汇编文件 3. **汇编**:将汇编文件生成目标文件(二进制文件) 4. **链接**:C语言写的程序是需要依赖各种库的,所以编译之后还需要把库链接到最终的可执行程序中去 ![图片5](assets/clip_image038.png) ### gcc编译过程 #### 分步编译 预处理: ```shell gcc -E hello.c -o hello.i ``` 编 译: ```shell gcc -S hello.i -o hello.s ``` 汇 编: ```shell gcc -c hello.s -o hello.o ``` 链 接: ```shell gcc hello.o -o hello ``` 选项列表 | **选项** | **含义** | | -------- | --------------------------- | | -E | 只进行预处理 | | -S(大写) | 只进行预处理和编译 | | -c(小写) | 只进行预处理、编译和汇编 | | -o file | 指定生成的输出文件名为 file | 文件后缀含义 | **文件后缀** | **含义** | | ------------ | --------------------- | | .c | C 语言文件 | | .i | 预处理后的 C 语言文件 | | .s | 编译后的汇编文件 | | .o | 编译后的目标文件 | 完整过程如下 ```shell [root@node1 c]<20210713 14:38:13># gcc -E hello.c -o hello.i [root@node1 c]<20210713 14:38:29># ls hello.c hello.i [root@node1 c]<20210713 14:38:35># gcc -S hello.i -o hello.s [root@node1 c]<20210713 14:38:47># ls hello.c hello.i hello.s [root@node1 c]<20210713 14:38:49># gcc -c hello.s hello.o gcc: error: hello.o: No such file or directory [root@node1 c]<20210713 14:39:03># gcc -c hello.s -o hello.o [root@node1 c]<20210713 14:39:12># gcc hello.o -o hello_elf [root@node1 c]<20210713 14:39:30># ./hello_elf Hello World[root@node1 c]<20210713 14:39:32># ``` #### 一步编译 gcc hello.c -o demo(还是经过:预处理、编译、汇编、链接的过程): ```shell gcc hello.c -o hello ``` ## CPU内部结构与寄存器 - 寄存器是CPU内部最基本的存储单元 - CPU对外是通过总线(地址、控制、数据)来和外部设备交互的,总线的宽度是8位,同时CPU的寄存器也是8位,那么这个CPU就叫8位CPU - 如果总线是32位,寄存器也是32位的,那么这个CPU就是32位CPU - 有一种CPU内部的寄存器是32位的,但总线是16位,准32为CPU - 所有的64位CPU兼容32位的指令,32位要兼容16位的指令,所以在64位的CPU上是可以识别32位的指令 - 在64位的CPU构架上运行了64位的软件操作系统,那么这个系统是64位 - 在64位的CPU构架上,运行了32位的软件操作系统,那么这个系统就是32位 - 64位的软件不能运行在32位的CPU之上 ### 寄存器名字 | 8位 | 16位 | 32位 | 64位 | | ---- | ---- | ---- | ---- | | A | AX | EAX | RAX | | B | BX | EBX | RBX | | C | CX | ECX | RCX | | D | DX | EDX | RDX | ### 寄存器、缓存、内存三者关系 **按与CPU远近来分,离得最近的是寄存器,然后缓存(CPU缓存),最后内存。** CPU计算时,先预先把要用的数据从硬盘读到内存,然后再把即将要用的数据读到寄存器。于是 CPU<--->寄存器<--->内存,这就是它们之间的信息交换。 那为什么有缓存呢?因为如果经常操作内存中的同一址地的数据,就会影响速度。于是就在寄存器与内存之间设置一个缓存。 因为从缓存提取的速度远高于内存。当然缓存的价格肯定远远高于内存,不然的话,机器里就没有内存的存在。 ```{tip} 由此可以看出,从远近来看:CPU〈---〉寄存器〈---> 缓存 <---> 内存。 ```