C语言进阶---程序环境和预处理
- 前言
- 一、程序的翻译环境、执行环境
- 二、详解:C语言程序的编译+链接
- 三、预定义符号介绍
- 四、预处理指令 #define
- 五、宏和函数的对比(思维导图)
- 六、命令定义、预处理指令 #include #undef
- 1.命名约定
- 2.命令行定义
- 七、条件编译
前言
那么好了好了,宝子们,从今天开始开始总结暑假博客,今天的主角:程序环境和预处理,来吧开始整活!⛳️
一、程序的翻译环境、执行环境
在ANSI C的任何一种实现中,存在两个不同的环境。
第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。 第2种是执行环境,它用于实际执行代码。
二、详解:C语言程序的编译+链接
- 组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)。
- 每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。
- 链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。
编译本身也分为几个阶段:
看代码:
#include <stdio.h>
//sum.c
int g_val = 2016;
void print(const char* str)
{printf("%s\n", str);
}
//test.c
int main()
{extern void print(char* str);extern int g_val;printf("%d\n", g_val);print("hello bit.\n");return 0;
}
这是预处理期间发生的:
#include <stdio.h>
int main()
{int i = 0;for(i=0; i<10; i++){printf("%d ", i);}return 0;
}
- 预处理 选项 gcc -E test.c -o test.i 预处理完成之后就停下来,预处理之后产生的结果都放在test.i文件中。
- 编译 选项 gcc -S test.c 编译完成之后就停下来,结果保存在test.s中。
- 汇编 gcc -c test.c 汇编完成之后就停下来,结果保存在test.o中。
VIM学习资料
简明VIM练级攻略:
简明VIM练级攻略链接:https://coolshell.cn/articles/5426.html
给程序员的VIM速查卡
给程序员的VIM速查卡:https://coolshell.cn/articles/5479.html
- 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
- 程序的执行便开始。接着便调用main函数。
- 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
- 终止程序。正常终止main函数;也有可能是意外终止。
三、预定义符号介绍
预定义符号:
四、预处理指令 #define
#define
#define 定义标识符:
#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定义标识符的时候,要不要在最后加上 ; ?
nononono
比如:
#define MAX 1000;//错误,不建议这样写
#define MAX 1000
提示:(只要对于宏定义的来说,多加括号就完事了,肯定不会出错。)
所有用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用!
#define 替换规则:
在程序中扩展#define定义符号和宏时,需要涉及几个步骤。
- 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
- 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值替换。
- 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
注意:
- 宏参数和#define 定义中可以出现其他#define定义的变量。但是对于宏,不能出现递归。
- 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
五、宏和函数的对比(思维导图)
六、命令定义、预处理指令 #include #undef
1.命名约定
一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。 那我们平时的一个习惯是:
把宏名全部大写 函数名不要全部大写
#undef
这条指令用于移除一个宏定义。
#undef NAME
//如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除
2.命令行定义
许多C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。 例如:当我们根据同一个源文件要编译出不同的一个程序的不同版本的时候,这个特性有点用处。(假定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数组,但是另外一个机器内存大写,我们需要一个数组能够大写。)
#include <stdio.h>
int main()
{ int array [ARRAY_SIZE]; int i = 0; for(i = 0; i< ARRAY_SIZE; i ++) { array[i] = i; } for(i = 0; i< ARRAY_SIZE; i ++) { printf("%d " ,array[i]); } printf("\n" ); return 0;
}
七、条件编译
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。
调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译。
注: 推荐《高质量C/C++编程指南》中附录的考试试卷(很重要)。
笔试题:
- 头文件中的 ifndef/define/endif是干什么用的?
- #include <filename.h> 和 #include "filename.h"有什么区别?
其他预处理指令
#error
#pragma
#line
…
不做介绍,自己去了解。
#pragma pack()在结构体部分介绍。
参考《C语言深度解剖》学习
好了,今天的分享就到这里了
如果对你有帮助,记得点赞👍+关注哦!
我的主页还有其他文章,欢迎学习指点。关注我,让我们一起学习,一起成长吧!