预编译指令:
条件编译指令:属于编译环境和编译器之间的交流指令,预编译指令控制的是代码。
#include <stdio.h>
#define DEMO 5
#ifndef DEMO//如果没有定义DEMO做的事情
int num = 0;
#endif // !DEMO
#ifdef DEMO//如果定义了DEMO要做的事情
int num = 100;
#endif // !DEMO
//
int main()
{
printf("%d", num);
#if (DEMO==0)//这里条件必须是使用预编译时候就有的条件,不可以使用num等变量。num要与处理完之后才有的。
printf("DEMO == 0\r\n");
#elif(DEMO == 1)
printf("DEMO == 1");
#else
printf("DEMO!=0 && DEMO!=1");
#endif
return 0;
}
如果两个.h文件互相包含,main.c里面包含任何一个.h,编译的时候就会导致递归,包含了无穷个.h。这种情况就是用宏来做
#ifndef
#define
#endif
用这个来防止一个库文件被多次包含。
宏定义还可以在属性里面的预处理器和命令行里面进行添加,命令行里面可以给宏定义赋值。
错误提示预编译指令:
用在编译的时候提示错误。
#include <stdio.h>
#define ERROR
int main()
{
#ifdef ERROR
#error You Are Error
#endif
printf("222");
return 0;
}
上面这段代码再便宜的时候就会提示出错误,并打印#error后面的文字。多用于多人协作的时候使用。
#warning已被遗弃,用法差不多的。
设置文件名及行号:
将对应地点的行号进行设置,文件名也对应设置
#include <stdio.h>
#line 100 "main.c"
int main()
{
printf("%s中的第%d行", __FILE__, __LINE__);
return 0;
}
#line后面的第一个数字代表当前行的下一行行号被设置成这个数,后面的字符串代表文件名被改成这个名字。
版本控制利器#Pragma message:
1:特殊的预编译指令:实际叫做预编译指示字。
2:#pragma是一个指示字,不同的C语言编译器对他的理解是不同的,这个是没有标准的。
3:它不具有移植性,但是如果一个编译器发现了一个他没有定义的预编译指示字的时候,他会忽略。不影响移植性。
4:他并非作用于代码,它主要的作用是指示编译器来完成特殊的指令。
#include <stdio.h>
#define VER 1
#if(VER == 1)
#pragma message("版本1.0")//message指示字是VS特有的。这些并不作用于代码,只是在编译的时候输出信息
#endif
int main()
{
return 0;
}
BAT名企敲门砖-高端面试之内存对齐:
1:#pragma pack(2)//内存对齐控制指示字,里面的数字必须为1 2 4 8 16 32。
2:内存对齐是为了方便cpu对数据的读取,cpu是按照块来读取的。
注意:有的cpu可能只会从偶数位的地址开始读取,如果为奇数,可能报硬件错误。
3:默认的对其方式是4。程序编译器对结构的存储的特殊处理确实提高CPU存储变量的速度,但是有时候也带来了一些麻烦,我们也屏蔽掉变量默认的对齐方式,自己可以设定变量的对齐方式。
4:对其的时候,会首选较小的数据类型扩大。例如默认的对齐方式是4byte,如果一个结构体为struct s1 {short s;int n;};,那么他占用空间为8byte,short为前2byte,空2byte,后四个为int。这种更方便cpu读取。具体准确规则比较复杂,通过下面的例子来看
#include <stdio.h>
//内存的对齐方式控制的指示字
#pragma pack(4)//里面跟的数字必须为1 2 4 8 16 数据对齐方式
struct s1
{
short s; //2byte
int n; //4byte
char c;
};
struct s2
{
char c; //1byte
struct s1 s; //它使用的对齐方式为里面成员使用的对齐参数的最大值 4
double d; //8byte
};
struct s3 //偏移是内存地址的偏移(必须可整除对齐参数),pack是设置的,size是数据类型大小,对齐参数是pack和size取小值,结果为占用内存位置空间
{ // 偏移 pack size 对齐参数 结果
char c;// 0 4 1 1 0
int n;// 4 4 4 4 4-7
short s;// 8 4 2 2 8-9
char c1;// 10 4 1 1 10
long l;// 12 4 4 4 12-15
double d;// 16 4 8 4 16-23
};//总长度必须可以整除对齐参数
#pragma pack()
int main()
{
printf("%d\r\n", sizeof(struct s1));
printf("%d\r\n", sizeof(struct s2));
printf("%d\r\n", sizeof(struct s3));
struct s3 ss3;
ss3.c = 'a'; ss3.c1 = 'b'; ss3.d = 100; ss3.l = 50; ss3.n = 25; ss3.s = 12;
return 0;
}
奇淫意巧-生态生成变量名:
1:宏定义的时候使用的#号,可以将其他的转化为字符串:
#include <stdio.h>
#define ERROR(n) #n
int main()
{
int error_code = 10;
printf("%s", ERROR(100));//将打印100
return 0;
}
2:##的使用:
#include <stdio.h>
#define STR(n) #n//#号的使用,转化成字符串
#define NO(n) No##n//##的使用,代表连接符
/*
#define STRUCT(type) typedef struct tag_##type; struct tag_##type; //上课讲了,但运行出错
STRUCT(S1)
{
int i;
};*/
int main()
{
int error_code = 10;
printf("%s", STR(100));//将打印100
int NO(1);
int NO(2);
int NO(3);
for (int i = 0; i < 20; ++i)
{
printf("%s\r\n", STR(NO(i)));//这里会打印20个NOi。他只会认里面的直接字。
}
/*S1 s;
s.i = 100;*/
return 0;
}