某人的常量可能是其他人的变量
1.编写一个简单的 C 程序
与其他语言编写的程序相比,C程序较少要求形式化的东西
,一个完整的
C程序可以只有寥寥数行。
![[C语言代码块#helloworld.c]]
上面的程序会在每次运行的时候,在屏幕上打印出语句 “Hellow world !”
#include <stdio.h>
是必不 可少的,符号 # 开头的语句是预处理器的指令,stdio 是 输入/输出 标准库的名称缩写,.h 是C语言库的后缀。整句语句的作用是在程序正式开始执行前,将代码中需要用到的库预先处理。
int main()
中,main 是一个函数,函数一般包含一个流程,用于对数据进行处理,而一般 main 函数会作为程序的入口,所以在大多数情况下,main 函数必不可少;int 是函数期望返回的数据类型;() 中表明该函数将要使用的参数,此处为空。
printf()
是 stdio.h 库中的一个输出函数,用于在屏幕上打印语句。
return 0
程序终止,并向操作系统返回数值 0,数值类型与 main 函数前的 int 相对应。
1.1 编译和链接
将程序代码保存在命名为 helloworld.c
的文件中,接下来将该文件中的程序转换为机器可执行的形式,一般包含三个步骤:预处理
-> 编译
-> 链接
预处理
:首先程序会被送交给预处理器(preprocessor)
。预处理器执行以#
开头的命令(通常称为指令),这些被预处理的一般是库文件,包含接下来程序可能需要使用的函数
。预处理器
有点类似于编辑器,它可以给程序添加内容,也可以对程序进行修改。编译
:修改后的程序现在可以进入编译器(compiler)
了。编译器会把程序翻译成机器指令(即目标代码)
。然而,这样的程序还是不可以运行的。链接
:在最后一个步骤中,链接器(linker)
把由编译器产生的目标代码
和所需的其他附加代码
整合在一起,这样才最终产生了完全可执行的程序。这些附加代码包括程序中用到的库函数(如printf函数)
。
在编译和链接好程序后,编译器会把可执行程序放到默认名为 a.out 的文件中。
上述的过程往往是自动实现的。在日常编写代码的过程中,由于预处理器
通常和编译器
集成在一起,所以我们一般不会感觉到这个过程在发生,但此时我们需要知道在程序编译运行时,这个过程是存在的。
源文件
在开发软件的过程中,我们需要将编写好的代码(Code)
保存到一个文件中,这样代码才不会丢失,才能够被编译器找到,才能最终变成可执行文件。这种用来保存代码的文件就叫做源文件(Source File)
。
每种编程语言的源文件都有特定的后缀,以方便被编译器识别,被程序员理解。源文件后缀大都根据编程语言本身的名字来命名,如C 语言
使用.c
后缀来标明这是一个C 程序文件
源文件
的后缀仅仅是为了表明该文件中保存的是某种语言的代码(例如.c
文件中保存的是C语言代码),这样程序员更加容易区分,编译器也更加容易识别,它并不会导致该文件的内部格式
发生改变。
可执行程序
我们平时所说的程序,是指双击后就可以直接运行的程序,这样的程序被称为可执行程序(Executable Program)
。在 Windows 下,可执行程序的后缀有.exe
和.com
(其中.exe
比较常见);在类UNIX 系统
(Linux、Mac OS 等)下,可执行程序没有特定的后缀,系统根据文件的头部信息
来判断是否是可执行程序。
可执行程序
的内部是一系列计算机指令和数据的集合
,它们都是二进制形式的,CPU 可以直接识别,毫无障碍;但是对于程序员,它们非常晦涩,难以记忆和使用。
编译器
C语言代码
由固定的词汇按照固定的格式组织起来,简单直观,程序员容易识别和理解,但是对于CPU,C语言代码就是天书,根本不认识,CPU只认识几百个二进制形式的指令。这就需要一个工具,将C语言代码转换成CPU能够识别的二进制指令,也就是将代码加工成.exe
程序的格式;这个工具是一个特殊的软件,叫做编译器(Compiler)
编译器
能够识别代码中的词汇
、句子
以及各种特定的格式
,并将他们转换
成计算机能够识别的二进制形式
根据编译器
和操作系统
的不同,编译和链接所需要的命令也是不同的。
Windows
下常用的是微软开发的Visual C++
,它被集成在 Visual Studio 中,一般不单独使用。
Linux
下常用的是GUN
组织开发的GCC
,很多 Linux 发行版都自带 GCC。
Mac
下常用的是LLVM/Clang
,它被集成在Xcode
中(Xcode 以前集成的是 GCC,后来由于 GCC 的不配合才改为 LLVM/Clang,LLVM/Clang 的性能比 GCC 更加强大)
1.2 集成开发环境
初始阶段使用操作系统提供的特殊窗口键入命令的方式来调用命令行编译器
,这对理解底层编译逻辑有一定的帮助。
而在实际开发中,我们一般使用集成开发环境(integrated development environment, IDE)
来进行编译。集成开发环境是一个软件包,我们可以在其中编辑
、编译
、链接
、执行
甚至调试程序
。
2.简单程序的一般形式
简单的C程序一般具有如下形式:
![[C语言代码块#C通用程序格式]]
该格式中,英文字符
表示实际的C语言程序代码,所有中文部分
则表示需要由程序员提供的内容。
其中的大括号用于标出main
函数的起始
和结束
。
即使最简单的C程序也依赖三个关键的语言特性:指令
、函数
和语句
。
2.1 指令
在编译C程序之前,预处理器
会首先对其进行编辑
。我们把预处理器执行的命令称为指令
。
在C语言中,指令有很多,但这里只关注#include指令。程序helloworld.c由下列这行指令开始:
#include <stdio.h>
这条指令说明,在编译前把<stdio.h>
中的信息包含
到程序中。<stdio.h>
包含了关于C标准输入/输出库
的信息。
C语言拥有大量类似于<stdio.h>
的头(header),每个头都包含一些标准库的内容。
这段程序中包含<stdio.h>
的原因是:C语言不同于其他的编程语言,它没有内置的读和写命令。输入/输出功能由标准库中的函数实现。
所有指令都是以字符#
开始的。这个字符可以把C程序中的指令和其他代码区分开来。指令默认只占一行,每条指令的结尾没有分号或其他特殊标记。
2.2 函数
函数类似于其他编程语言中的过程
或子例程
,它们是用来构建程序的构建块
。事实上,C程序就是函数的集合
。
函数分为两大类:一类是程序员编写的函数
,另一类则是作为C语言实现的一部分提供的函数。我们把后者称为库函数(library function)
,因为它们属于一个由编译器提供的函数“库”。
术语“函数”来源于数学。在数学中,函数是指根据一个或多个给定参数进行数值计算的规则,而后返回运算后的结果。
![[C语言代码块#helloworld.c]]
以上述main
函数为例,mian
后面的()
中可以存放函数运行所需要的值
,如数学中的f(x, y)
;main
前面的int
表示该函数会返回的值的类型
;main
中的return
用于返回函数的运算结果
。
2.3 语句
语句
是程序运行时执行的命令。在helloworld.c
这个程序中只用到两种语句。
一种是返回语句(return)
。
另一个则是函数调用语句(function call)
。当要求某个函数执行分派给它的任务时,可以称为调用
这个函数
。如打印“hello world !"这个语句时就调用了printf()函数
。
C语言规定每条语句都要用分号结尾
。
3.注释
注释(comment)
应存在于每一个C程序的代码中,其充当文档说明
,包含识别信息
,如程序名
、编写日期
、作者
、程序的用途
以及其他相关信息。
单行注释以//
开始,无结束字符,但仅限于单行注释。
多行注释以/*
开始,以*/
结束,中间部分可以存放任意内容。
![[C语言代码块#注释]]
4.变量和赋值
很少由程序会如helloworld.c
这样简单,大多数程序在产生输出之前往往需要执行一系列的运算,因此需要在程序执行过程中由一种临时存储
数据的方法。在C语言中,这类存储党员被称为变量(variable)
。
4.1 类型
每一个变量
都必须有一个类型(type)
。类型用来说明变量所存储的数据的种类
。
C语言拥有广泛多样的类型。但是现在,我们将只限定在两种类型范围内:int
类型和float
类型。
int(即integer的简写)
型变量可以存储整数,如0、1、392或者-2553。但是,整数的取值范围是受操作系统
的带宽
限制的,不同带宽的操作系统中int类型数值的最大/小值可能不同。float(即floating-point的简写)
型变量可以存储比int型
变量大得多的数值。而且,float型变量可以存储带小数位的数,如379.125。但float型变量也有一些缺陷,进行算术运算时float型变量通常比int型变量慢;更重要的是,float型变量所存储的数值往往只是实际数值的一个近似值。如果在一个float型变量中存储0.1,以后可能会发现变量的值为0.09999999999999987,这是舍入造成的误差。
由于类型
会影响变量的存储方式
以及允许对变量的操作
,所以选择合适的类型是非常关键的。数值型变量的类型决定了变量所能存储的最大值和最小值,同时也决定了是否允许在小数点后出现数字。
4.2 声明
在使用变量时必须先对其进行声明
,类似于向各方宣告我们将使用这个变量,即使这个声明只是面向编译器
的。
int height;
float profit;
第一条声明的是一个int类型的变量,变量height可以存储一个整数值。
第二条声明的是一个float类型的变量,变量profit可以存储一个浮点数。
可以同时声明多个变量,也叫做合并声明
int x,y,z;
float k,h;
4.3 赋值
变量通过赋值(assignment)
的方式来获得值。
height = 8;
length = 10;
声明
和赋值
可以分开进行,但也可以同时进行
int height;
height = 10;float weight = 121.1;
当把一个包含小数点的常量
赋值给float
型变量
时,最好在常量后面加一个字母f
(向编译器表示这是一个float型常量),不加的时候可能会引发编译器警告
float profit = 215.47f;
正常情况下,要将int类型
的值赋给int类型
的变量,将float类型
的值赋给float类型
的变量.
混合类型赋值是可以的,但不一定安全,如将一个float类型的值
赋给一个int类型的变量
,此时会发生取整,取整方式由编译器
决定,致使在不同编译器中结果不唯一
。这会很奇怪,为什么输入一样,输出不一样呢?
4.4 显示变量的值
可以通过printf()
函数打印信息,其中需要使用转义字符
%d
仅用于显示int型
变量
%f
仅用于显示float型
变量
int height = 10;
int weight = 12.1f;printf("%d .. %f",height,weight);
printf("\n"); // 跳转到下一行打印
printf("%.3f",height,weight);
printf("\n"); // 跳转到下一行打印
printf("%d",height*height);
printf("\n"); // 跳转到下一行打印
printf("You weight is : %.3f", weight);
以下为一个打印相关的例子
![[C语言代码块#计算箱子的空间重量]]
4.5 初始化
当程序开始执行时,某些变量会被自动设置为零,而大多数变量则不会。没有默认值并且尚未在程序中被赋值的变量是未初始化的(uninitialized)
。
当声明一个变量时,系统会将一个内存区间
划分给这个变量,但不会清除这个内存区域的数据,此时若是打印该变量,可能会得到一个不可预知的值
。
如果可以,在声明
变量后,尽可能快地初始化
变量。
5.读入输入
为了获取输入,需要用到stdio.h
库的scanf()
函数,它是 C 函数库中与printf()
相对应的函数。可以注意到scan
与print
分辨表示读
与写
的意思,而两个函数后的字母f
则是format string
的缩写,表示格式化字符串的意思,所以这两个函数可以理解为格式化的输入输出
函数。
在[[C02-C语言基本概念#4.5 初始化]]中测算空间程序只能计算一个固定的输入值是不合理的,在这里可以用scanf( ) 函数来优化上面的程序。
![[C语言代码块#计算箱子的空间重量(改进输入版)]]
6.定义常量
常量在C代码中的存储是个问题,因为不可能用变量的形式存储常量,所以需要使用宏
来定义常量,被宏定义
的常量是不可变
的,满足了常量的特性
。
// 常量定义方式
#define RECIROCAL_OF_PI (1.0f / 3.14159f)
7.标识符
在编写程序时,标识符是用来标识变量
、函数
、宏
或任何其他用户自定义实体
的名称。这些名字称为标识符(identifier)
。
一个标识符以字母 A-Z
或 a-z
或下划线 _
开始,后跟零个或多个字母、下划线和数字(0-9)。
C 标识符内不允许出现标点字符
,比如 @、$ 和 %。C 是区分大小写的编程语言。因此,在 C 中,Manpower 和 manpower 是两个不同的标识符
// 合法标识符
times time10 get_next_char _done// 非法标识符
9times get-next-char
关键字
关键字(keyword)
对C语言编译器有特殊意义,因为这些关键字不能作为标识符来使用。
C99中的关键字
![[C语言代码块#关键字]]