Skip to content

解我之惑

解释器

当打开cmd、powershell窗口或使用类Unix终端时,背后实际运行了一个cmd.exe/pwsh.exe/shell解释程序。

它负责接受给定的输入命令及参数,根据内置或下载的二进制程序,查询命令的存在性(合法性),执行相应的二进制程序并响应输出。

这个过程看似是连续的、不间断的,但机器并不是在接收到你的命令后连续地执行的,机器可能还并发(在极短时间内、ms级切换)的执行图形界面渲染等程序。

因此命令行程序能理解并执行输入的gcc main.c -o main

编译器

编译程序并不能理解人类较为自然的编程语言,它只是通过预先设置的规则,根据C语言的关键字、标识符、运算符等,对所写程序源码进行处理转为一种标记序列。对标记序列进行词法分析:

语法分析:

语义分析:

再将语法分析结果转化成一种中间代码,方便在编译阶段接受优化参数对源码进行编译优化。

最终将优化后的中间代码转换为汇编代码(或特定平台机器码)。

而汇编代码 与 可执行的机器码是一一对应的,即一条汇编代码直接对应一串二进制序列

最后,通过链接程序,将生成的汇编代码与引入的库文件合并,生成最终可执行程序。


逐行解析

  1. #include <stdio.h>

在C语言中,通过井号#作为特定的标记,与代码实现部分(void mainprintf(...))明显不同。

在这里,#include所做的操作,就是将后面跟的库,原封不动的**“复制粘贴”**到此程序。

可以看到,我们导入了一个stdio.h文件即标准输入输出(STanDard I/O),由于使用了其标准输入输出库文件中的一个函数printf()作为展示结果到命令窗口。

这里的.h表明这是一个头文件(header),C语言允许你通过头文件的方式,将一些声明与具体实现分离,可在头文件中仅做声明,在.c文件中再具体实现。

  1. int main(){}

在C/C++中main(){}的形式是整个程序的入口,也是整个程序执行真正开始的地方。其前方的int是一个关键字,含义为整型数字,它指明了,最终执行完{}中所有的内容后,将要返回给操作系统什么样的值,操作系统默认期望值为0以标志正确执行成功。

从此可以看出{}是一个函数或其他结构的边界线,它编译时在语法分析中被单独切开作为标记,在后续的编译中两两配对。

  1. printf("Hello World!\n");

在这一行,我们使用了库中的一个与屏幕交互的预定义函数,其含义为将printf()括号内的内容按照要求输出到屏幕上。在这里可以看到,字符\n似乎没有在屏幕中显示,实际上,\n是对格式的一种要求的特定转义字符,它的作用是,当输出到此处是换行。类似的还有如下表,这些转义序列在C/C++程序中被编译器特殊处理,不会按字面值输出,而是执行相应的控制功能或显示特殊字符

转义序列名称描述
\n换行符将光标移到下一行开头
\t水平制表符将光标移到下一个制表位置
\r回车符将光标移到当前行开头
\\反斜杠表示一个反斜杠字符
\"双引号表示一个双引号字符
\'单引号表示一个单引号字符
\0空字符(null)字符串结束标记
\a警报(alert)发出系统提示音
\b退格符将光标向左移动一个位置
\f换页符将光标移到下一页开头
\v垂直制表符在文本中垂直移动光标
\xhh十六进制值表示ASCII码为十六进制值hh的字符
\ooo八进制值表示ASCII码为八进制值ooo的字符
\?问号表示一个问号(在某些情况下避免三字符组)

可以轻易看出,\负责将某一种字符转化成另一种含义,即\为转义字符。这种方式在许多编程语言是通用的。

例如,在一些shell解释器中,想要输出某些特定字符,必须进行转义,因为这些字符在解释器中被定义为一些操作或对象:

bash
# 这二者的输出是不一样的
echo $0
echo \$0
  1. return 0;

return作为一个关键字,将其后面的内容作为结果返回到函数外,它可以返回多种类型的数据,包括int, float , double

数据类型

基本数据类型

类型关键字描述典型大小值范围
字符char用于表示字符,也可作为小整数1字节1281270255
整型int基本整数类型4字节(通常)2312311
浮点型float单精度浮点数4字节约$±3.4E±38 $(6-7位精度)
双精度浮点double双精度浮点数8字节±1.7E±308(15-16位精度)
无值void表示没有值--

整数类型修饰符

修饰符应用于描述
signed整型表示可以存储负值(默认)
unsigned整型只存储零和正值,扩大正值范围
shortint较小范围整数类型 (通常2字节)
longint较大范围整数类型 (通常4字节或8字节)
long longint更大范围整数类型 (通常8字节)

常见的组合类型

组合类型描述典型大小
unsigned char字节数据,ASCII值1字节 (0255)
unsigned int非负整数4字节 (02321)
short int短整数2字节 (2162161)
unsigned short int非负短整数2字节 (02321)
long int长整数4或8字节
unsigned long int非负长整数4或8字节
long long int更长整数8字节
long double扩展精度浮点数8或16字节

派生数据类型

类型描述声明例子
数组同类型元素的集合int numbers[10];
指针存储内存地址int *ptr;
结构体不同类型元素的组合struct {...} name;
联合共享内存的不同数据类型union {...} name;
枚举整型常量的集合enum {...} name;

类型限定符

限定符描述
const声明不可修改的变量
volatile告诉编译器变量可能被外部改变
restrict指示指针是访问对象的唯一方式(C99)

注:具体类型的大小和范围可能因编译器和系统而异