C_概述

C_概述

编写一个简单的C程序

1
2
3
4
5
6
7
//HelloWorld .c
#include <stdio .h> // 预处理指令

int main(void) {
printf("Hello world .\n");
return 0;
}
  1. #include<stdio .h> 是一条预处理指令,表示在程序中 “包含” stdio .h 头文件, 其中有C语言标准输入/输出库的信息。

  2. main函数是程序的入口, main函数中的第一行代码是用来打印信息的。printf函数来自于标准输入/输出库,可以产生格式化的输出。\n是一个转义序列,表示换行。

  3. 第二行代码 return 0; 有两个作用:一是终止 main函数。二是程序终止时会向操作系统返回状态码 0 ( 0 表示正常终止,非 0 表示异常终止)。

编译和链接

对 C 程序来说,通常包含下面 4 个步骤:

  1. 预处理

    首先程序会由预处理器 (preprocessor) 进行处理。预处理器执行以 # 开头的指令。主要是把头文件中内容 copy 到源代码文件中,或者是对宏进行文本替换。

    • #include:头文件包含 (把头文件中的内容复制到指令所在的位置)
    • #define N 5:宏定义 (简单的文本替换)
    • #define FOO(x) (1 + (x) * (x)):带参数的宏 (宏函数) (文本替换:用“实参”替换“形参”)(记得带括号)(宏函数:左括号应该紧挨宏函数的名称,参数应该由小括号括起来,整个表达式也应该用小括号括起来)
  2. 编译

    经过预处理器处理的文件会交给编译器进行编译。编译器会把程序翻译成对应平台的汇编代码。

  3. 汇编

    汇编器会把生成的汇编代码翻译成对应平台的机器代码 (目标代码)。

  4. 链接

    在链接阶段,链接器会把由汇编器生成的目标代码和程序需要的其它附加代码整合在一起,生成最终可执行的程序。附加代码包括程序中用到的库函数(如 printf 函数)或库文件(.a为静态库文件,.so为动态库文件)。

    编译与链接原理

    注意:在C/C++中,编译单元为源文件,会对每一个源文件进行编译,生成对应的目标文件。然后将多个目标文件链接在一起,生成可执行程序。

进程虚拟内存空间

程序经过预处理、编译和链接,最终生成可执行文件。可执行文件被操作系统加载到内存,程序才得以运行。运行的程序为进程 (process)。每个进程都有自己的虚拟内存空间,如下所示:

进程虚拟内存

变量和赋值

类型

每一个变量都必须有一个类型 。类型用来说明变量所存储数据的种类

声明

在使用变量之前必须对其进行声明,声明的格式如下:

1
类型 变量名;

例如,声明变量 height 和 profit:

1
2
int height;
float profit;

有几个变量具有相同的类型,可以声明合并:

1
2
int height, length, width, volume;
float profit, loss;

赋值

变量通过赋值操作获取值。如

1
2
3
height = 8;
length = 12;
width = 10;

通常会把一个浮点数赋值给 float 类型的变量,而且往往会在浮点数后面添加字母f。如:

1
profit = 2150.48f;

一旦变量被赋值,就可以计算其它变量的值:

1
2
3
4
height = 8;
length = 12;
width = 10;
volumn = height * length * width;

显示变量的值

可以用 printf 显示变量的值。如:

1
printf("Height: %d\n", height);

其中%d是占位符——转换说明 (conversion specification),用来指明变量 height 在显示中的位置。

%d仅适用于 int 类型变量,要显示 float 类型变量,需要用 %f 来代替 %d。默认情况下,%f 会显示出小数点后 6 位数字。要强制 %f 显示小数点后 p 位数字,可以把 .p 放置在 %f 之间,如:

1
printf("Profit: $%.2f\n", profit);

C 语言没有限制 printf 可以显示变量的数量,可以同时显示多个变量的值:

1
printf("Height: %d Length: %d Width: %d\n", height, length, width);

初始化

当程序开始执行时,某些变量会被自动设置为零,而大多数变量不会。没有默认值并且尚未在程序中被赋值的变量是未初始化的

注意:试图访问未初始化的变量,其行为是未定义的。在有些编译器中,可能会得到一个无意义的值;在另一些编译器中,则可能发生更坏的情况 (如程序崩溃)。

在声明变量的同时赋初始值。如:

1
int height = 8;

其中数值 8 是一个初始化式 (initializer)。

在一个声明语句中,可以对任意数量的变量进行初始化:

1
int height = 8, length = 12, width = 10;

注意:上面每一个变量都有自己的初始化式。如果写成:

1
int height, length, width = 10;

则只有变量 width 被初始化了。

标识符

在编写程序时,需要对变量、函数、宏等内容进行命名。名字为标识符(identifier)。

在C语言中,标识符只能包含数字,字母和下划线,但是必须以字母或者下划线开头,不能以数字开头。

下面是一些合法的标识符:

1
times10, get_next_char, _done

接下来是一些不合法的标识符 :

1
10times, get-next-char

C语言的标识符是区分大小写的。比如,下面的标识符全都不同:

1
job, Job, jOb, joB, JOb, JoB, jOB, JOB

下划线标识符:

1
symbol_table, current_page, name_and_address

单词首字母大写标识符 (驼峰命令法):

1
symbolTable, currentPage, nameAndAddress

关键字

对 C 编译器有特殊意义的名称,称为关键字 (keyword)。关键字是不能作为标识符来使用的。如:

auto enum restrict (C99) unsigned break extern
return void case float short volatile
char for signed while const goto
sizeof _Bool (C99) continue if static _Complex (C99)
default inline (C99) struct _Imaginary (C99) do int
switch double long typedef else register
union

为常量定义名字

当程序含有特殊意义的常量时 (比如 32.0f),建议给这些常量定义名字,以免别人在阅读程序不知道这个常量的含义。在 C 语言中可以采取宏定义的方式给常量命名:

1
#define FREEZING_PT 32.0f

在预处理阶段,预处理器会把每一个宏替换为其表示的值。

可以利用宏来定义表达式,如:

1
#define SCALE_FACTOR (5.0f / 9.0f)

当宏表示一个表达式时,最好用括号把表达式括起来。