在ANSI C的任何一种实现中,存在两个不同的环境。 第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。 第2种是执行环境,它用于实际执行代码。



这些现象我们可以在vs code中很清楚的查看 在vs code 中我们创建两个代码,来进行加法的计算
test.c
#include<stdio.h>
//声明一个外部函数
extern int Add(int x,int y);
int main()
{
int a = 10;
int b = 20;
int sum = Add(a,b);
printf("%d\n", sum);
return 0;
}add.c
int Add(int x,int y)
{
return x+y;
}预处理

此时我们发现test.i文件已经生成好了,然后打开文件

编译

此时就生成了一个 test.s 的文件,我们再来观察一下 test.s

在观察 test.s 时我们发现很难读取 而其实在这个编译阶段是将C语言代码翻译成了汇编代码
汇编

汇编就是把汇编代码转化成二进制的指令 -> 再形成符号表,所以我们无法直接读取
链接的过程

程序执行的过程:
_FILE_ ------- //进行编译的源文件 _LINE_ ------- //文件当前的行号 _DATE_ -------- //文件被编译的日期 _TIME_ --------- //文件被编译的时间 _STDC_ --------- //如果编译器遵循ANSI C,其值为1,否则未定义

因为vs不是严格遵守C标准 ,所以我们使用vscode来实现_STDC_

语法: #define name stuff
比如:
#define MAX 1000 #define reg register //为 register这个关键字,创建一个简短的名字 #define do_forever for(; ; ) //用更形象的符号来替换一种实现 #define CASE break;case //在写case语句的时候自动把 break写上。 // 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。 #define DEBUG_PRINT printf(“file:%s\tline:%d\t \ date:%s\ttime:%s\n” ,\ FILE,LINE , \ DATE,TIME )
在define定义标识符的时候,不要在最后加上 ; 因为在执行程序时分号也会被一起替换,容易出现语法问题
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro) 或定义宏(define macro)。
宏的申明方式:
#define name( parament-list ) stuff 其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中。 注意: 参数列表的左括号必须与name紧邻。 如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分
比如:
#define SQUARE( x ) x * x
SQUARE( 5 )
这个宏接收一个参数 x .如果在上述声明之后,你把SQUARE( 5 )置于程序中,预处理器就会用下面这个表达式替换上面的表达式:
5 * 5
注意:在宏的使用中,千万记得不要吝啬打括号,少了括号结果可能差距很大 下面我们来看个例子
int a = 5; printf(“%d\n” ,SQUARE( a + 1) );
可能大家在读这个代码后觉得是输出 36 但结果确是 11
5+1*5+1
这才是正确的代码读取,若想输出 36 则需要重新定义宏
#define SQUARE( x ) (x) * (x)
所以在宏的定义时:
用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中 的操作符或邻近操作符之间不可预料的相互作用。
在程序中扩展#define定义符号和宏时,需要涉及几个步骤:
#

这样看字符串是有自动连接的特点
#define print_format(format, num) \
printf("the value of num is "format, num)
int main()
{
int i = 10;
print_format("%d", i+3 );
return 0;
}
这样如果我们想要将 num 换成 对应的字符,这时我们就要用到 #

这样就能让两个字符串连接起来
the value of num is 13 —> the value of i+3 is 13
##
##可以把位于它两边的符号合成一个符号。 它允许宏定义从分离的文本片段创建标识符。

注意:但是这样的连接必须可以产生一个合法的标识符,否则它的结果就是未定义的
x+1;//不带副作用 x++;//带有副作用
#define MAX(a, b) ( (a) > (b) ? (a) : (b) ) … x = 5; y = 8; z = MAX(x++, y++); printf(“x=%d y=%d z=%d\n”, x, y, z);
代码结果应该为:x=6,y=10,z=9;
宏的优点:
宏的缺点:

把宏名全部大写 函数名不要全部大写




在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。


这就是条件编译,只有满足条件才会编译
常见的条件编译指令:
1.直接条件判断

2. 多个分支的条件编译

3. 判断是否被定义

4.嵌套指令
#if defined(OS_UNIX)
#ifdef OPTION1
unix_version_option1();
#endif
#ifdef OPTION2
unix_version_option2();
#endif
#elif defined(OS_MSDOS)
#ifdef OPTION2
msdos_version_option2();
#endif
#endif
#include 指令可以使另外一个文件被编译。就像它实际出现于 #include 指令的地方 一样。

查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件。 如果找不到就提示编译错误。

查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。
对于库函数也可以使用 “ ” 的包含方式只不过效率不高
如果出现这样的场景,可能就造成头文件的重复

comm.h和comm.c是公共模块。 test1.h和test1.c使用了公共模块。 test2.h和test2.c使用了公共模块。 test.h和test.c使用了test1模块和test2模块。 这样最终程序中就会出现两份comm.h的内容。这样就造成了文件内容的重复
解决方法:
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif #pragma once 好了,C语言的程序环境和预处理就讲到这里,感谢大家观看