# C 语言 —— 预处理

# #error

// 编译程序时,只要遇到 #error 就会生成一个编译错误提醒,并且停止编译,
语法格式:
#error error-message
实例:
#ifdef xxx
#error “xxx has been defined”
#else
#endif

# #ifndef #define #endif

// 当项目中有多个 c 文件使用到同一个头文件是,在编译的时候会出现大量的变量,函数声明冲突,解决就是使用
#ifndef _HEAR_H_
#define _HEAR_H_
#endif

# #define 和 const

Define 和 const 都可以用于定义常量但以下区别 (生效时间,内存占用情况,类型检查):

  1. define 只是单纯的文本替换,define 常量的生命生命周期止于编译器,不存在分配内存,存在与程序的代码段
  2. const 生效于编译的阶段;define 生效于预处理阶段
  3. Const 修饰的常量处于程序的数据段,在堆栈中分配空间
  4. Const 有数据类型检查,define 没有
  5. #define 不可调试,const 能调试
  6. const 定义的变量在 C 中不是真正的常量
  7. Const 定义的常量不能作为数组的大小

# typedef 和 #define

原理不同:

  1. 首先 #define 是预处理命令,在预处理阶段只是机械的替换带入字符串,并不会左类型检查,
  2. typedef 是关键字,作用是给自己的作用域内给一个已经存在的类型起个别名
  3. #define 没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用,而 typedef 有自己的作用域
  4. 对指针的操作不同

# #define

表明一年

#define YEAR (60 * 60  * 24 * 365)ul

标准宏 MIN

#define MIN(a, b) ((a) < (b) ? (a) : (b))

缺点

  1. 无法进行类型检查
  2. 运算优先级问题
  3. 无法调试
  4. 代码膨胀
  5. 无法操作类的私有数据成员
#define m(a,b) a*b
// #define m (a,b) (a)*(b)// 避免出问题最好加上 ()
int main()
{
    printf("%d\n",m(5,6));
    printf("%d\n",m(5+1,6));// 实际是这样的:5+1*6
    return 0;
}
#define sqort(a)  ((a)*(a))
// 实例 1: 
int a = 5;
int b = sqort(a++);//((a++) * (a++) )  先给括号赋值 5 之后 a 再 + 1=6
printf("%d\n",b);//30
printf("%d\n",a);//7
// 实例 2:	
int a = 5;
int b = sqort(++a);//((++a)* (++a)) 
printf("%d\n",b);//49
printf("%d\n",a);//7
这里主要是考察++a和后加加的问题
// 记住一点:
++a返回的a的引用,a++返回的是a加之前的数值
a的引用是要等最终的那个a才能确定的

# #include

对于.#include <头文件>,表示是系统文件,编译会先从标准库路径下搜索,编译器设置的头文件路径 --> 系统变量

对于 #include” 头文件”,当前头文件目录 --> 编译器设置的头文件路径 --> 系统变量

# C 代码编译过程

  1. 预处理(Preprocessing):
  • 预处理阶段是在实际编译之前的一个可选步骤,用于处理源代码中的预处理指令,比如 #include#define
  • 预处理器将处理这些指令,并且可能会包含其他文件、进行宏替换等。
  • 预处理的输出是一个经过处理的源文件,通常以 .i.ii 为扩展名。
  1. 编译(Compiling):
  • 编译阶段将预处理后的源代码转换为汇编代码(Assembly Code)。
  • 编译器(如 GNU Compiler Collection 中的 gcc )将 C 源代码翻译成汇编语言。
  • 输出是一个以 .s 为扩展名的汇编代码文件。
  1. 汇编(Assembling):
  • 汇编阶段将汇编代码转换为机器语言指令。
  • 汇编器(如 GNU Assembler 中的 as )将汇编代码翻译成机器码。
  • 输出是一个以 .o.obj 为扩展名的目标文件,包含了二进制指令。
  1. 链接(Linking):
  • 链接阶段将多个目标文件(例如,多个 C 文件分别编译得到的目标文件)合并为一个可执行文件。
  • 链接器(如 GNU 的 ldgcc 中的链接器部分)将各个目标文件中的函数和变量引用解析,并创建一个可执行文件。
  • 链接的输出是一个可执行的二进制文件,可以在操作系统上运行。

# 在头文件中是否可以定义静态变量

不可以,因为静态变量是有记忆的,不会随函数结束而结束,所以,如果定义在头文件中,那么就会被多个文件开辟空间,浪费资源或者重新出错

# #和 ## 的作用

// #利用宏参数字符串化
#define ARGV(x) printf(""#x" is %s\n",#x) // 表示把参数 x 解释为字符串
int a = 5;
ARGV(a); //a is a
// ## 运算符粘合剂 组合成一个变量,强制分隔
#define targ( n )   X##n   // 表示 X1...... 或 Xn
int main()
{
   int a = 5;
   // ARGV(a);
   int targ(1) = 10;// 表示 X1 =10
   printf("%d\",X1);//10
   return 0
}