欢迎来到我的博客!💃💃
🏡🏡推荐博客:August_._
一起持续学习,不断总结,共同进步🎊🎊
编译和链接
把程序转化为机器可以执行的形式,C语言中常包含下列转化步骤:
- 预处理。首先程序会被交给预处理器( preprocessor )。预处理器执行以#开头的命令(通常称为指令)。预处理器有点类似于编辑器,它可以给程序添加内容,也可以修改程序。
- 编译。修改后的程序现在可以进入编译器( compiler )了。编译器会把程序翻译成机器指令(即目标代码)。然而,这样的程序还是不可以运行的。
- 链接。在最后一个步骤中,链接器( linker )把由编译器产生的目标代码和所需的其他附加代码整合在一起,这样才最终产生了完全可执行的程序。这些附加代码包括程序中用到的库函数(如 printf 函数)。
幸运的是,上述过程往往是自动实现的,因为预处理器通常会和编译器集成在一起,所以可能不会注意到它在工作。
集成开发环境
集成开发环境是一个软件包,我们可以在其中编辑、编译、链接、执行甚至调试程序。组成集成开发环境的各个部分可以协调工作。例如,当编译器发现程序中有错误时,它会让编辑器把包含出错代码的行突出显示。
C语言的三个特性
1、指令
在编译 C 程序之前,预处理器会首先对其进行编辑。我们把预处理器执行的命令称为指令。例如#include 指令。
#include <stdio.h>
这条指令说明,在编译前把 #include <stdio.h> 中的信息"包含"到程序中。#include <stdio.h>包含 C 标准输入/输出库的信息。 C 语言拥有大量类似于<stdio.h>的头,每个头都包含一些标准库的内容。这段程序中包含<stdio.h>的原因是 C 语言不同于其他的编程语言,它没有内置的"读"和"写"命令。输入/输出功能由标准库中的函数实现。
所有指令都是以字符#开始的。这个字符可以把 C 程序中的指令和其他代码区分开来。指令默认只占一行,每条指令的结尾没有分号或其他特殊标记。
2、函数
函数类似于其他编程语言中的"过程"或"子例程",它们是用来构建程序的构建块。事实上, C 程序就是函数的集合。函数分为两大类:一类是程序员编写的函数,另一类则是作为 C语言实现的一部分提供的函数。我们把后者称为库函数( library function ),因为它们属于一个由编译器提供的函数"库"。
在 C 语言中,计算数值的函数用 return 语句来指定所"返回"的值。例如,对参数进行加1操作的函数可以执行语句
return x+1;
而当函数要计算参数的平方差时,则可以执行语句:
return y*y-z*z ;
虽然一个 C 程序可以包含多个函数,但只有 main 函数是必须有的且不能更改名字。 main 函数是非常特殊的:在执行程序时系统会自动调用 main 函数。
#include <stdio.h>
int main(void)
{
printf("Hello World\n");
return 0;
}
main 前面的 int 表明该函数将返回一个整数值。圆括号中的 void 表明 main 函数没有参数。
语句 return 0;有两个作用:
1、使 main 函数终止(从而结束程序)
2、指出 main 函数的返回值是0。这个值表明程序正常终止。如果 main 函数的末尾没有 return 语句,程序仍然能终止。但是,许多编译器会产生一条警告信息(因为函数应该返回一个整数,却没有这么做)。
3、语句
语句是程序运行时执行的命令,有两种语句:
一是返回( return )语句;
二是函数调用( function call )语句。要求某个函数执行分派给它的任务称为调用这个函数。
例如,程序为了在屏幕上显示一条字符串就调用了 printf 函数:
printf("Hello World\n");
C 语言规定每条语句都要以分号结尾。
【就像任何好的规则一样,这条规则也有一个例外:后面会遇到的复合语句,就不以分号结尾。】
语句可以连续占用多行,有时很难确定结束位置,因此用分号来向编译器显示语句的结束位置。
但指令通常只占一行,因此不需要用分号结尾。
显示字符串
printf 是一个功能强大的函数,用 printf 函数显示用一对双引号包围的一系列字符,最外层的双引号不会出现。当显示结束时, printf 函数不会自动跳转到下一输出行。为了跳转到下一行,必须在要显示的字符串中包含 \n(换行符),换行符就意味着终止当前行,然后把后续的输出转到下一行。
换行符可以在一个字面串中出现多次。为了显示下列信息:
Nothing is impossible
Believe yourself
可以这样写:
printf ("Nothing is impossible\n Believe yourself\n ");
注释
我们的程序仍然缺乏某些重要内容:文档说明。符号/*标记注释的开始,*/标记注释的结束。例如:
/* This is a comment */
注释几乎可以出现在程序的任何位置上。它既可以独占一行,也可以和其他程序文本出现在同一行中。下面展示的程序就把注释加在了程序开始的地方:
/* Name: Lv Meng */
/* Hello! */
/* Nice to meet you! */
# include <stdio.h>
int main (void)
{
printf ("Nice to meet you!\n");
return 0;
}
注释还可以占用多行。如果遇到符号 /*,那么编译器读入(并且忽略)随后的内容直到遇到符号*/ 为止。如果愿意,还可以把一串短注释合并成为一条长注释:
/*
Name: Lv Meng
Hello!
Nice to meet you!
*/
更好的方法是用一个"盒形"格式把注释单独标记出来:
/*******************************************
* Name: Lv Meng *
* Hello! *
* Nice to meet you! *
********************************************/
简短的注释还可以与其他代码放在同一行:
int main (void) /* Beginning of main program */
C99提供了另一种类型的注释,以 //(两个相邻的斜杠)开始:
// This is a comment
这种风格的注释会在行末自动终止。如果要创建多于一行的注释,既可以使用以前的注释风格(/*...*/),也可以在每一行的前面加上 //:
// Name
// Purpose
// Author
新的注释风格有两个主要优点:
1、注释会在行末自动终止,不会出现未终止的注释意外吞噬部分程序的情况
2、因为每行注释前面都必须有 //,所以多行注释看上去更加醒目
变量和赋值
很少有程序会像示例那样简单。大多数程序在产生输出之前往往需要执行一系列的计算,因此需要在程序执行过程中有一种临时存储数据的方法。和大多数编程语言一样, C 语言中的这类存储单元被称为变量( variable )。
类型
每一个变量都必须有一个类型( type )。类型用来说明变量所存储的数据的种类。 C 语言拥有广泛多样的类型。选择合适的类型是非常关键的。数值型变量的类型决定了变量所能存储的最大值和最小值,同时也决定了是否允许在小数点后出现数字。
int型变量可以存储整数,如0、392或 -2553。但是,整数的取值范围是受限制的。最大的整数通常是2147483647,但在某些计算机上也可能是32767。
float型变量可以存储比 int 型变量大得多的数值。而且,可以存储带小数位的数,如379.125。但 float 型变量也有一些缺陷。进行算术运算时 float 型变量通常比 int 型变量慢;更重要的是, float 型变量所存储的数值往往只是实际数值的一个近似值。如果在一个 float 型变量中存储0.1,以后可能会发现变量的值为0.09999999999999987,这是舍入造成的误差。
声明
在使用变量之前必须对其进行声明(为编译器所做的描述)。为了声明变量,首先要指定变量的类型,然后说明变量的名字。例如,我们可能这样声明变量 height 和 profit :
int height;
float profit;
如果几个变量具有相同的类型,就可以把它们的声明合并:
int height,length,width,volume;
float profit,loss;
注意每一条完整的声明都要以分号结尾。且必须把声明放置在语句之前,建议在声明和语句之间留一个空行。
赋值
变量通过赋值( assignment )的方式获得值。例如,语句
height =8;
length =12;
把数值8、12分别赋给变量 height 、 length ,8、12称为常量( constant )。
变量在赋值或以其他方式使用之前必须先声明。也就是说,我们可以这样写:
int height ;
height=8;
但下面这样是不行的:
height = 8;
/*** WRONG ***/
int height ;
赋给 float 型变量的常量通常带小数点。例如,如果 profit 是一个 float 型的变量,可能会这样对其赋值:
profit = 2150.48;
当我们把一个包含小数点的常量赋值给 float 型变量时,最好在该常量后面加一个字母 f (代表 float ):
profit=2150.48f; /*不加 f 可能会触发编译器的警告*/
显示变量的值
用 printf 可以显示出变量的当前值,例如:
Height : h
这里的 h 表示变量 height 的当前值。我们可以通过如下的 printf 调用来实现输出上述信息的要求:
printf (" Height :%d\n ",height);
占位符% d 用来指明在显示过程中变量 height 的值的显示位置。
注意,因为在%d后面放置了 \n ,所以 printf 在显示完 height 的值后会跳到下一行。
% d 仅用于 int 型变量。如果要显示 float 型变量,则要用% f 来代替% d 。
默认情况下,% f 会显示出小数点后6位数字。如果要强制% f 显示小数点后 p 位数字,可以把 .p放置在%和 f 之间。例如,为了显示信息
Profit :2150.48
可以把 printf 写为如下形式:
printf (" Profit: %.2f\n ",profit);
初始化
当程序开始执行时,某些变量会被自动设置为零,而大多数变量则不会。没有默认值并且尚未在程序中被赋值的变量是未初始化的。
如果试图访问未初始化的变量(例如,用 printf 显示变量的值,或者在表达式中使用该变量),可能会得到不可预知的结果,如随机值或者其他同样没有意义的数值。在某些编译器中,可能会发生更坏的情况(甚至是程序崩溃)。
我们当然可以总是采用赋值的方法给变量赋初始值,但还有更简便的方法:在变量声明中加入初始值。例如,可以在一步操作中声明变量 height 并同时将其初始化:
int height=8;
按照 C 语言的术语,数值8是一个初始化器( initializer )。
在同一个声明中可以对任意数量的变量进行初始化:
int height = 8,length = 12,width = 10;
注意,上述每个变量都有属于自己的初始化器。在接下来的例子中,只有变量 width 拥有初始
化器10,而变量 height 和变量 length 都没有(也就是说这两个变量仍然未初始化):
int height,length,width = 10;
显示表达式的值
printf 的功能不局限于显示变量中存储的数,它可以显示任意数值表达式的值。利用这一特性既可以简化程序,又可以减少变量的数量。例如,语句
volume = height * length * width ;
printf ("%d \n", volume);
可以用以下形式代替:
printf("%d \n ",height*length*width);
printf 显示表达式的值的能力说明了 C 语言的一个通用原则:在任何需要数值的地方,都可以使用具有相同类型的表达式。
读入输入
为了获取输入,就要用到 scanf 函数。它是 C 函数库中与 printf 相对应的函数。 scanf 中的字母 f 和 printf 中的字母 f 含义相同,都表示"格式化"的意思。 scanf 函数需要知道将获得的输入数据的格式,而 printf 函数需要知道输出数据的显示格式。
scanf ("%d",&i);
其中,字符串"%d "说明 scanf 读入的是一个整数,而 i 是一个 int 型变量,用来存储 scanf 读入的输入。&运算符在使用 scanf 函数时通常是(但不总是)必需的。
定义常量的名字
当程序含有常量时,建议给这些常量命名。可以采用称为宏定义( macro definition )的特性给常量命名:
#define N 166
这里的 #define 是预处理指令,类似于前面所讲的# include ,因而在此行的结尾也没有分号。
当对程序进行编译时,预处理器会把每一个宏替换为其表示的值。例如,语句
weight = (volume+N-1)/N;
将变为
weight = (volume+166-1)/166;
此外,还可以利用宏来定义表达式:
# define PI (1.0f/3.14159f)
当宏包含运算符时,建议用括号把表达式括起来。
注意,宏的名字只用了大写字母。这是大多数 C 程序员遵循的规范,但并不是 C 语言本身的要求。(至今, C 程序员沿用此规范已经几十年了,希望读者不要背离此规范。)
标识符
在编写程序时,需要对变量、函数、宏和其他实体进行命名。这些名字称为标识符( identifier )。在 C 语言中,标识符可以含有字母、数字和下划线,但是必须以字母或者下划线开头。
【在C99中,标识符还可以使用某些"通用字符名"。】
下面是合法标识符的一些示例:
get _ next _ char _ done
接下来这些则是不合法的标识符:
10times get - next - char
不合法的原因:符号10times是以数字而不是以字母或下划线开头的;符号 get - next - char 包含了减号,而不是下划线。
C 语言是区分大小写的;例如,下列8个标识符全是不同的:
job joB job jOB Job JoB JOb JOB
上述8个标识符可以同时使用,且每一个都有完全不同的意义。
因为 C 语言是区分大小写的,所以许多程序员会遵循在标识符中只使用小写字母的规范(宏命名除外)。
其中所有关键字( keyword )对 C 编译器而言都有着特殊的意义,因此这些关键字不能作为标识符来使用。
关键字 | 说明 |
---|---|
auto | 声明自动变量 |
break | 跳出当前循环 |
case | 开关语句分支 |
char | 声明字符型变量或函数返回值类型 |
const | 定义常量,如果一个变量被 const 修饰,那么它的值就不能再被改变 |
continue | 结束当前循环,开始下一轮循环 |
default | 开关语句中的"其它"分支 |
do | 循环语句的循环体 |
double | 声明双精度浮点型变量或函数返回值类型 |
else | 条件语句否定分支(与 if 连用) |
enum | 声明枚举类型 |
extern | 声明变量或函数是在其它文件或本文件的其他位置定义 |
float | 声明浮点型变量或函数返回值类型 |
for | 一种循环语句 |
goto | 无条件跳转语句 |
if | 条件语句 |
int | 声明整型变量或函数 |
long | 声明长整型变量或函数返回值类型 |
register | 声明寄存器变量 |
return | 子程序返回语句(可以带参数,也可不带参数) |
short | 声明短整型变量或函数 |
signed | 声明有符号类型变量或函数 |
sizeof | 计算数据类型或变量长度(即所占字节数) |
static | 声明静态变量 |
struct | 声明结构体类型 |
switch | 用于开关语句 |
typedef | 用以给数据类型取别名 |
unsigned | 声明无符号类型变量或函数 |
union | 声明共用体类型 |
void | 声明函数无返回值或无参数,声明无类型指针 |
volatile | 说明变量在程序执行中可被隐含地改变 |
while | 循环语句的循环条件 |
C99 新增关键字
_Bool | _Complex | _Imaginary | inline | restrict |
C11 新增关键字
_Alignas | _Alignof | _Atomic | _Generic | _Noreturn |
_Static_assert | _Thread_local |
注意
有关标识符的其他限制。某些编译器把特定的标识符(如asm)视为附加关键字。属于标准库的标识符也是受限的。误用这些名字可能会导致编译或链接出错。以下划线开头的标识符也是受限的。