『C语言』1 - 概述

来源:互联网 发布:魔方数据是什么 编辑:程序博客网 时间:2024/04/28 01:30

1.1 C语言起源

  贝尔实验室的Dennis Ritchie和Ken Thompson在设计UNIX系统时,从Thompson的B语言获得了灵感,在1972年开发了C语言,C语言是一种面向过程的语言。

1.2 C的特性和用途

  设计特性——自顶向下规划、结构化、模块化编程。
  高效性——程序紧凑,运行速度快。
  可移植性——在不同的机器和系统上不作修改或很少的修改就可使用。
  功能及灵活性——功能强大,简约灵活。
  更接近底层——允许访问硬件,操纵内存特定位。
  
  其缺点也很明显,因其代码灵活,编写自由,所以可能会导致一些在其它语言上不会犯的错误。

  C几乎可以开发出任何程序,但它更适合接近底层的开发,如嵌入式系统、操作系统内核、硬件驱动等等。

1.3 计算机工作基本原理

  现代计算机主要由几大部件组成:中央处理单元——CPU,随机访问存储器(内存)——RAM,永久存储器——硬盘,输入输出设备——键盘、鼠标、显示器等等。

  CPU从内存中获取一个指令并执行该指令,然后从内存中获取下一个指令并执行。CPU有自己的工作区,工作区由若干个寄存器组成,每个寄存器可以保存一个数。一个寄存器保存下一条指令的内存地址,CPU使用该地址获取下一条指令,获取一条指令后,CPU在另一个寄存器中保存该指令并将第一个寄存器的值更新为下一条指令的地址。CPU只能理解有限的指令,这些指令由CPU所支持的指令集所提供。
  
  存储在计算机中的一切都是二进制数字,数字、字符、每一条执行的指令等等都由其对应的二进制代码存储。

1.4 高级语言和编译器

  高级语言的语法和表达方式更接近人类的自然语言,比机器语言和汇编语言更好理解和运用,但是计算机只能识别机器语言,不能识别这种高级语言编写的代码,而编译器就是一种将高级语言编写的程序解释成计算机能够理解的机器语言的程序。

1.5 编译机制

  用C语言编写程序时,编写的内容保存在一个被称为源代码的文本文件中,名称都以“.c”结尾,如“hello.c”,小数点前半部分为基本名,后半部分为扩展名,组合起来是文件名。hello.c以字节序列的方式存储在文件中,每个字节都有一个整数值,而该整数对应于某个字符,通常为ASCII字符,只由ASCII字符构成的文件称为文本文件,所有其它的文件都称为二进制文件
  
  C编程的基本策略是使用编译器将源代码文件转换为可执行文件,此文件包含可以运行的机器语言代码。C分两步完成这一工作——编译链接
  
  编译器将源代码文件转换为中间代码,链接器将此中间代码与其它代码(如启动代码、库代码)相结合来生成可执行文件。这样可以使程序便于模块化,当有多个模块需要编译时,可以分别编译各个模块,然后用链接器将编译后的模块结合起来,这样,如果需要更改某个模块,只需重新编译此模块即可,然后重新链接,不必把所有的模块重新编译。
  
  中间文件通常是将源代码转换为机器语言代码,并把结果放在一个被称为目标代码的文件中(目标文件),此文件还不能运行,原因是缺少启动代码和库代码。

  启动代码相当于程序和操作系统之间的接口,因为不同的系统对程序的处理方式不同,所以不同系统的启动代码不同,
库代码是标准C库中所包含的代码,如printf函数使用了头文件stdio.h中所声明的代码,而定义这个函数的代码在另一个库文件中,目标文件不包含此函数的代码。

  链接器的作用是将这三个元素(目标代码、启动代码和库代码)结合在一起,生成可执行文件。链接器只从库代码中提取你所使用函数所需要的代码,并不会全部提取。
  
  目标文件和最后生成的可执行文件都是由机器语言指令组成的,但目标文件只包含你所编写的代码所转换成的机器代码,而可执行文件则包含启动代码和你所使用的库代码所转换成的机器代码。

1.6 语言标准

  最初C并没有官方标准,最早是Brian Kernighan和Dennis Ritchie编写的《The C Programming Language》成为大家接受的标准,被称为K&R C。
  
  1999年修订后的标准被称为C99,该标准减小了C与C++的不兼容性和增加了一些新特性,目前大多数编译器都已支持C99标准。

1.7 C程序示例

/*文件test.c*/#include <stdio.h>int main(void)  //主函数开始{    int num;    //声明一个名为num的int型变量    num = 1;    //给变量赋值为1    printf("The number is %d.\n", num); //调用printf函数    return 0;}

运行结果:
The number is 1.

1.8 示例说明

#include <stdio.h>
  这是程序第2行,表示你在文件中的该行引入了文件“stdio.h”的完整内容,实际上是一种复制粘贴的操作,可以方便的在多个程序间共享公用的信息。
  
  #include为C预处理指令,C编译器在编译前要对源代码做一些准备工作,称为预处理
  
  “stdio.h”文件为C标准库所提供,代表标准输入输出头文件,它提供了有关输入输出函数的信息,如printf函数、scanf函数。我们把出现在文件顶部的信息集合称为。头文件还包含了建立最终可执行程序时编译器所用到的信息,如定义常量,声明函数等,但是里面的函数代码通常在被包含在一个预编译代码的库文件中,而不是在头文件中,编译器的链接部分负责找到所需要的库代码。

int main(void)
  这是程序第3行,它声明了一个main函数,C总是且只从main函数开始执行,一个C程序有且只有一个main函数存在。圆括号表示它是一个函数,int指明了该函数的返回值类型,在此返回一个int型的数给操作系统。圆括号里面表示要传递给该函数的信息,在此不传递任何信息,即void。
  
  程序中有像/*……*/和//……这样的行,这些被称为注释,以//开头的是C99新增的注释方式,注释只是方便自己和别人理解代码,在编译时会被编译器忽略。前一种注释的范围为//之间,可以跨越多行,而后一种注释只能单行。

  程序的第4行到第9行为用花括号括起来的代码,花括号划定了main函数的界限,中间的代码被称为代码块

int num;
  这是程序第五行,它是一个声明语句,声明了一个int型变量,分号代表这是一个语句,且为这个语句的一部分。int是一个关键字,代表C中一个基本数据类型,在这里为整型。num为这个变量的名字,也称为标识符,标识符不能为C中的关键字,所有的变量在使用前都必须先声明
  
  C99允许一个标识符最多有63个字符,外部标识符最多31个字符,C90分别为31个和6个。可以使字符数量超过限制,但编译器不会识别超出之后的字符。标识符只能以英文字母或下划线开头,除开头之外的字符可以是英文字母、数字或下划线,且标识符区分大小写。

num = 1;
  程序第6行是一个赋值语句,意思是把整数1赋给变量num。前面的声明语句在计算机内存中为变量num分配了空间,该赋值语句在那个空间为变量存储了一个值,该值可以被改变。

printf("The number is %d.\n", num);
  程序第7行调用了C中的一个标准函数,圆括号中的内容为main函数传递给printf函数的信息,这样的信息被称为实际参数,简称参数。双引号表示这是一个字符串,整个字符串为一个参数,而后面的num为第二个参数,参数与参数之间用逗号隔开。
  
  在C中调用一个函数只需键入函数名字,把所需的一个或多个参数放进圆括号中,当程序运行到这一行时,控制权将交给该函数(在这里是printf函数),当函数完成了它所做的工作后,控制权交给原来的函数(在这里是main函数),像这样,main函数称为调用函数,printf函数称为被调函数
  
  传递给该函数第一个参数中的%d是一个占位符,作用是指出第二个参数num的位置,%号表示格式化输出,d表示把num的值以十进制整数形式输出,占位符可以看作是引用num的值,并把它放在这个位置上。\n是一个转义字符,表示输出一个换行符,即把光标移到下一行的开始处,并不能直接使用回车键换行。

return 0;
  这是程序中最后一个语句,在函数最开始,int main(void)表示它将返回一个整数,C标准要求它这样做,在这里,返回整数0给操作系统。

  就这样,一个简单地程序就完成了,一个程序由一个或多个函数组成,其中必须要有且只有一个main函数。函数由函数头和函数体组成,函数头包括预处理语句和函数名(在这里,main和#include是一个函数头),函数体位于花括号中并由一系列语句组成,每个语句以一个分号结束。

1.9 编译过程

一个文件被编写好了以后需要编译执行,以hello.c文件为例,如下:

//文件hello.c#include <stdio.h>int main(void){    printf("hello world!\n");    return 0;}

在Linux下,以GCC编译器为例,编译过程如下:

单步编译:
$ gcc hello.c -o hello
  直接把文本文件hello.c编译为可执行文件hello。

分步编译:实际上编译过程分为四步——预处理、编译、汇编、链接。
预处理:
$ gcc -E hello.c -o hello.i
  预处理器根据#开头的命令修改源程序,此时hello.i还是文本文件。

编译:
$ gcc -S hello.i -o hello.s
  编译器将文本文件hello.i翻译成由汇编语言组成的文本文件hello.s。

汇编:
$ gcc -c hello.s -o hello.o
  汇编器将hello.s翻译并打包为一个由机器语言指令组成的可重定位目标程序。

链接:
$ gcc hello.o -o hello
  因为此文件调用了printf函数,而printf函数存在于一个名为printf.o的单独预编译好了的目标文件中,链接器需要把这个文件合并到hello.o的程序中,结果得到hello文件,它是一个可执行文件,能被加载到内存中,由系统执行。

在终端中执行该文件:
$ ./hello
hello world!

多文件编译:
  设一个由test_1.c和 test_2.c两个源文件组成的程序,为了对它们进行编译,并最终生成可执行程序test,可以使用如下命令:
$ gcc test_1.c test_2.c -o test
  这个过程仍然会按照预处理、编译、汇编和链接的过程依次进行,上面这条命令相当于依次执行以下三条命令:
$ gcc -c test_1.c -o test_1.o
$ gcc -c test_2.c -o test_2.o
$ gcc test_1.o test_2.o -o test

0 0