目录:
- 编译和链接的过程
- 预定义符号的分类
- #define的简单使用
-define定义的符号
- 预定义符号的分类
- *define定义的宏
编译和链接的过程
同时有以下两个环境:编译环境和执行环境:
翻译环境:
组成一个程序的每个源文件通过编译过程分别转换成目标代码然后每个目标文件由链接器捆绑在一起,形成一个单一而完整的可执行程序。
执行环境:
明确:
- 程序要载入内存中,在有操作系统的情况下,程序的载入由手工安排
- 其次程序一开始执行就调用main函数
- 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
预定义符号的分类
一下这些符号是不需要#define来进行定义的,是本身就定义好我们可以直接使用的符号。
__FILE__;进行编译的源文件
__LINE__;文件当前的行号
__DATE__;文件被编译的日期
__TIME__;文件被编译的时间
__STDC__;如果编译器遵循ANSI C,则其值为1,否则未定义
__FUNCTION__;当前运行的函数
以下程序实验一下:
int main()
{
printf("%s\n", __FILE__);
printf("%d\n", __LINE__);
printf("%s\n", __DATE__);
printf("%s\n", __TIME__);
printf("%s\n", __FUNCTION__);
return 0;
}
我们尝试写一个日志的文件如下:
int main()
{
int i = 0;
FILE* pf = fopen("log.txt", "a+");//追加的形式输入
if (pf == NULL)//判断文件是否打开成功
{
perror("fopen\n");
return 1;
}
for (i = 0; i < 10; i++)
{
fprintf(pf, "%s %d %s %s %d\n", __FILE__, __LINE__, __DATE__, __TIME__, i);
//将我们的写代码时的信息存入log.txt这个文件里面
}
return 0;
}
define的简单使用
需要注明的是宏是整体替换,否则有如下的bug:
#define SQUARE(x) x*x
#define DOUBLE(x) ((x)+(x))
int main()
{
//int a = 9;
//int r = SQUARE(a);
//printf("%d\n", r);//81
//int r = SQUARE(a+1);//答案不是100而是19 x=a+1
//不经过计算直接传进来
//实际上是 a+1*a+1-->9+1*9+1=19
//例子二:
int b = 20;
int ret = 3 * DOUBLE(b);//本来是3*40(20的两倍)=120
//实际上 int ret=3*(b)+(b)=4b #define DOUBLE(x) (x)+(x)
//所以为了避免上述情况的发生我们要这样 #define DOUBLE(x) ((x)+(x)) 加括号 ret=3*(20+20)
printf("%d\n", ret);
return 0;
}
另外为了解决函数的一些弊端,引入宏如下:
下列代码可以满足the value of a is 20
the value of pai is 3.14
从而解决了函数传参的问题
//#define PRINT(N,format) printf("the value of"#N" is " format "\n",N)
//
//int main()
//{
// int a = 20;
// double pai = 3.14;
// PRINT(a, "%d");
// PRINT(pai,"%lf");
// return 0;
//}
函数传参的弊端:
void print(int n)
{
printf("the value of n is %d\n", n);
}
int mian()
{
int a = 10;
printf("the value of a is %d\n", a);
print(a);//这样调用函数始终是the value of n is ...
int b = 20;
printf("the value of a is %d\n", b);
return 0;
}
define定义的符号
//#undef MALLOC //取消MALLOC这个宏
//int main()
//{
// int arr[10] = { 0 };
// int i = 0;
// for (i = 0; i < 10; i++)
// {
// arr[i] = i+1;
// printf("%d ", arr[i]);//测试是否放的是1~10
// }
//
// return 0;
//}
//int main()
//{
// int arr[10] = { 0 };
// int i = 0;
// for (i = 0; i < 10; i++)
// {
// arr[i] = i + 1;
//#if 0 //若改为1则该句会参与编译
// printf("%d ", arr[i]);//让这句话不参与编译
//#endif
// }
//
// return 0;
//}
//多分支语句
//#define NUM 1
//int main()
//{
//#if NUM==1
// printf("hehe\n");
//#elif NUM==2
// printf("haha1\n");
//#else
// printf("hehe3\n");
//#endif
// return 0;
//}
//判断某个符号有无定义
//#define MAX 1
//
//int main()
//{
// //正面1的例子:
#if defined(MAX)
printf("hehe\n");
#endif
//
反面1的未定义#define MAX打印
//#if !defined(MAX)
// printf("hehe\n");
//#endif
//
正面2的例子:
#ifdef MAX
printf("hehe\n");
#endif
//
反面2未定义#define MAX打印
//#ifndef MAX
// printf("hehe\n");
//#endif
// return 0;
//}
宏和函数的对比:
条件编译
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的
因为我们有条件编译指令
调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译
基本格式#(条件编译类型)...代码...#endif
每一种类型的条件编译后面的要跟上一个#endif,表示条件编译的范围
#ifdef和#undef
#ifdef:判断是否被定义
#ifndef:判断是否不被定义
如下代码:
#define PRINT
int main()
{
#ifdef PRINT//如果PRINT被定义了,才会编译,否则不会
printf("hehe\n");//所以这里是会编译的
#endif
//如果ifdef为假,在预处理阶段就删掉了
#ifndef HEHE
printf("hehe\n");//因为ifndef没有被定义,所以此语句会被编译
#endif
return 0;
}
这给代码同上一样的意思:
#if defined(TEST)//这种跟第一种是一样的
printf("test2\n");
#endif
#if !defined(HEHE)
printf("hehe2\n");
#endif
return 0;
}
`
`常见的条件编译指令如下:`
//常见的条件编译指令
int main()
{
#if 0//如果#if后面的表达式为真,就编译
printf(“hehe\n”);
#endif
return 0;
}
//多个分支的条件编译
int main()
{
#if 11
printf(“hehe\n”);
#elif 12
printf(“haha\n”);
#else
printf(“heihei\n”);
#endif
return 0;
}
#的使用场景:
我们先来看以下这个场景
int main()
{
int a=10;
int b=20;
int c=30;
//现在想要打印三句话
printf("the value of a is %d\n",a);
printf("the value of b is %d\n",b);
printf("the value of c is %d\n",c);
//这样写的话我们的the value of...is这些语句就重复写了三次
return 0;
}
这样写的话我们的the value
of…is这些语句就重复写了三次,那么有没有什么更简单的方法?这时候,很多小伙伴都会想,我们可以使用函数,传参不就好了?但事实上操作起来,函数实现是非常麻烦的,因为the
value of后面这个字符是会变的, 因此这个时候我们就可以利用#这个操作符来帮助我们完成
#define PRINT(X) printf("the value of "#X" is %d\n",X);
// ^此#非前面的#
// ^此处如果没有#,就会被替换,代码就会出错了
//但是如果加上#,就不是替换了,#X就会变成X的内容所对应的字符串
int main()
{
int a = 10;
PRINT(a);//这一句跟下面这一句是完全等价的
printf("the value of ""a"" is %d\n", a);
int b = 20;
PRINT(b);
printf("the value of ""b"" is %d\n", b);
int c = 30;
PRINT(c);
printf("the value of ""c"" is %d\n", c);
return 0;
}
通过这种方式,我们就可以只是用三个PRINT()就可以完成我们的任务了
注意:定义宏的时候,两个字符串中间的X如果不加上#,就会被直接替换,这样的话代码是会出问题的
但是,如果我们加上#,就不是直接替换了,#X就会变成X的内容所对应的字符串
这样,我们就能很好的实现我们的功能
如果有不同类型的数据,我们还可以稍微优化一下我们的代码
//我们还可以把上面的代码稍微优化一下
//#X这个#只能在宏上面用
#define PRINT(X,FORMAT) printf("the value of "#X" is "FORMAT"\n",X)
int main()
{
int a = 10;
PRINT(a, "%d");
int b = 20;
PRINT(b, "%d");
int c = 30;
PRINT(c, "%d");
float f = 5.5f;
PRINT(f, "%f");
return 0;
}
注意,#X只能在宏上使用
##符号的使用
作用:连接两个符号变成一个符号:
#define CAT(X,Y) X##Y
//可以把两个符号连成一个符号
int main()
{
int grade9 = 100;
printf("%d\n", CAT(grad, e9));
//这里的CAT是直接把grad和e9连起来,所以打印的当然是100
return 0;
}