任何一个ANSI C,都有两个环境,一个翻译环境一个运行环境。
运行环境相当于windows,翻译环境相当于编译器。
一个程序从代码到执行程序,要进行一系列复杂的工作,在vs2022的编译器中,这些工作都我们完成了。都这里只简单讨论其执行步骤(如果想深入了解推荐《程序员的自我修养》)。
vs2022是集成开发环境(IDE),其内部集成和编译器(cl.exe)和连接器(link.exe),来帮助使用者快速和方便的编译代码,最后生成可执行程序.exe。这里到生成可执行程序的步骤,可以统称为编译。
但是在某些编译器上则需要自己手动进行编译和处理。
这里把一个代码文件test.c生成了一个可执行test.exe
步骤:
1. 预处理(预编译) 生成中间件 test.i
这里会发现,中间件因为加载了头文件,代码行数到了六万多行。还删除了注释。并且把#define定义的符号进行了替换。
2.编译结果保存在test.s中。中间件test.s中保存的就是源代码的汇编代码。
3.汇编完成,结果保存在test.obj中。是一堆二进制指令和形成符号表。
4.最后进行链接,进行到这里的时候编译器就能判断外部函数是否正确的被调用。比对符号表,发现符号表上并没有找的对应的函数就会报错。
1.一个程序要执行,必须载入内存。在windows下,是系统帮我们载入的,在其他平台上需要自己手动烧录。
2.程序的开始main()函数。
3.开始执行程序代码。
4.终止程序。
#include 和 #define都是预处理指令,一个是用来加载头文件,一个是用来宏定义。
在vs编译器内部有很多的宏定义
- __FILE__ 当前文件
- __LINE__ 当前代码行
- __TIME__ 文件被编译的时间
- __DATE__ 文件被编译的日期
- __func__ 当前函数名
- __STDC__ 编译器遵循ANSI C,其值为1
当然我们也可以自己定义一个宏,这时就需要使用预处理命令#define。
这里定义了一个MAX(x,y),会发现相较于写一个比较大小的函数,这种写法简洁方便。但是要注意,#define并不是如同函数那样接收参数,而是单纯的符号替换。
第二个文件为程序编译时,产生的中间件。这里会发现这里#define定义的宏,完全是替换的功能。 并不像函数那样在其内部进行计算,所以没有在栈区产生消耗,所以速度相较于函数来说比较快一些。
但是因为其是单纯的符号替换,所以这里必需要注意符号的优先级
比如这里只是想计算10乘以比较大小后的数,但是这里进行替换后却发现10*10是先计算的,会造成错误。所以这里要注意必需要加上括号。
但是如果只对单个的参数括了起来,还是会出现错误,所以这里需要把整体一起括起来
而且,#define定义的宏,输入的参数不能带副作用(a+1没有副作用,但是a++有副作用,其改变了a本身的值)
#define MAX(x,y) ((x)>(y)?(x):(y))
int a=5;
int b=8;
int c=MAX(a++,b++);
这里其实就会发现,前面变量计算后,影响到了后面的值。所求的值跟原来进行比较的值已经不一样了。
对应一个函数来说其参数据有固定的类型,要进行不同类型的比较,要定义多个函数,但是宏没有类型检查,可以接受任意类型的数据。
宏没办法像函数一样进行调试,宏也不能递归。
所以要进行宏定义的时候,要根据自己需要来选择要不要使用。
#undef:移除宏定义
#define NUM 10;
#undef NUM
#if 常量表达式
//...
#endif
#if 常量表达式
//...
#elif 常量表达式
//...
#else 常量表达式
//...
#endif
#define NUM 1
#if NUM==1
printf("1\n");
#elif NUM==2
printf("2\n");
#else
printf("3\n");
#endif
这段代码的含义是,如果NUM等于1,则打印1,如果NUM等于2,打印2,否则打印3。会发现跟 if() 语句非常相似。
#if defined(MAX)
//...
#endif //等价于下面
#ifdef MAX
//...
#endif
这段代码的含义是,如果MAX被宏定义,则执行后面的代码。
#if !defined(MAX)
//...
#endif //等价于下面
#ifndef MAX
//...
#endif
这段代码的含义是,如果MAX没有被宏定义,则执行后面的代码。
#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
条件编译指令可以嵌套使用,但是会发现代码可读性非常差,很难以理解。
PS:进入vs自带的库函数会发现,条件编译在vs底层非常常见。
1.#pragma once
#pragma也是一个预处理指令,这里加一个once,一般写在头文件最上面,其意思是只加载一次头文件。但是这种写法在其他编译器上有可能不支持。所以还有一种普遍的写法。
#ifndef _TEST_H
#define _TEST_H
#includ
//...
#endif
这种写法也可以防止多次加载头文件,并且兼容性好。