嵌入式基础学习|C语言篇——预处理(含进阶)

C语言预处理及进阶技巧

#include <stdio.h>

hello.c

gcc hello.c  //编译 

预处理 (预编译)
汇编 (汇编语言) ---助记符 
编程:
人类语言 --->编程语言(C语言)---汇编语言--->机器语言(01010)

八位的单片机 0101 0101  //加法 
             //0101 0101
16位 
32位      0101 0101 0101 0101  0101 0101  0101 0101  //add 
64位 
main()
{
   int a = 1;
   int b = 2;
   
    printf("hello world! %d \n",a+b);
}
a.out //
-----------------------------------
编译过程:
预处理   
编译
汇编
链接 

预处理:
1.宏定义
2.文件包含
3.条件编译 

1.宏定义  --- 定义了符号常量 

 #define 标识符 字符串
 #define 宏名  宏值
 #define N  100
 表示,定义了一个宏 (符号),而这个符号 代表的值 就是100 
 #define 宏名(参数)  宏值
说明:
  1. 预处理阶段,只做文本原样替换
  2. 宏 就是用 宏值 将 宏名 原样替换 
  3. 用途 
     当作常量来用
     好处,可以做到 一改全改,方便操作 
  4. 宏名 
     符合标识符命名规则
     宏名,一般都是大写 --- 主要是为了和普通变量名区分 
  5. 程序中 处于 ""中的 宏名 不会被替换 
  6. 宏名 不要和关键字重名
  7. 宏替换过程中,存在宏的副作用 ---- 文本的原样替换 
     处理:
         能加括号的尽量都加括号
  8. 宏定义 只能放在一行 
练习:
   从键盘输入 圆的半径,求出圆的周长和面积 
   L = 2 * π * r 
   S = π * r * r
   
   π 常量 
   
   #define PI 
   
练习:
     用带参宏,实现找两个数中的最大值 

文件包含 
#include "文件名"   //首先在当前路径下寻找要包含的文件,找不到再到系统默认路径下寻找
#include <文件名>   //默认到系统指定路径下寻找头文件 

作用:
   将文件名代表的文件中的内容 ,替换到当前文件 

条件编译: //也是文本替换 --- 符合条件的文本 
//形式1
#ifdef 标识符
    程序段1
#else
    程序段2
#endif
//形式2
#ifdef 标识符
    程序段1
#endif 


#ifndef 标识符
    程序段1
#else
    程序段2
#endif


#if    表达式
    程序段1
#else
    程序段2
#endif


多文件编程 
 main.c
  |--add.c  //自己的点c通常会包含自己的点h 
  |--add.h  //一部分是 对外的函数声明 一部分时自己的点c需要用到的头文件等内容 
  |--sub.c
  |--sub.h 
 
作业:
   定义一个字符串 "12345"
   要求:
     将"12345" 转化数值 12345 输出 
     写成函数 
     
     重要知识点总结

1. 预处理指令的基本概念
-预处理阶段:在编译前执行,处理所有以 `#` 开头的指令。
- 常见指令:`#include`, `#define`, `#ifdef`, `#ifndef`, `#endif`, `#if`, `#else`, `#elif`, `#error`, `#pragma` 等。

2. 宏定义(`#define`)
(1) 无参宏
- 简单替换:`#define PI 3.14`,预处理时直接替换为值。
- 注意事项:
  - 避免在宏末尾加分号,可能导致语法错误。
  - 若替换内容是表达式,需加括号确保优先级正确:
    ```c
    #define SQUARE(x) ((x) * (x))  // 正确
    #define SQUARE(x) x * x        // 错误:可能导致运算顺序问题
  (2) 带参宏
- 参数替换:`#define MAX(a, b) ((a) > (b) ? (a) : (b))`
- 副作用问题:
  - 参数可能是表达式(如自增操作),导致多次求值:
    ```c
    int a = 5, b = 10;
    int max = MAX(a++, b);  // 展开后:((a++) > (b) ? (a++) : (b)) → a被递增两次
    ```

---

 3. 条件编译
- 用途:根据条件选择性地编译代码块。
- 常见指令:
  - `#ifdef` / `#ifndef`:检查宏是否已定义。
    ```c
    #ifdef DEBUG
        printf("Debug mode");
    #endif
    ```
  - `#if`:根据常量表达式判断:
    ```c
    #if (VERSION >= 2)
        // 编译新功能
    #elif (VERSION == 1)
        // 旧功能
    #else
        #error "Unsupported version"
    #endif
    ```
  - 防止头文件重复包含:
    ```c
    #ifndef MY_HEADER_H
    #define MY_HEADER_H
    // 头文件内容
    #endif
    ```

---

4. 文件包含(`#include`)
- 两种形式:
  - `#include <stdio.h>`:系统头文件,按标准路径查找。
  - `#include "myheader.h"`:用户自定义头文件,优先从当前目录查找。
- 嵌套包含问题:避免循环依赖,合理使用条件编译。

---

5. 预定义宏
- 常用内置宏:
  - `__LINE__`:当前行号。
  - `__FILE__`:当前文件名。
  - `__DATE__`:编译日期(如 "Jan 23 2023")。
  - `__TIME__`:编译时间(如 "12:34:56")。
  - `__func__`:当前函数名(C99)。

---6. 预处理操作符
- `#` 运算符:将宏参数转换为字符串。
  ```c
  #define STR(s) #s
  printf("%s", STR(Hello));  // 输出 "Hello"
  ```
- `##` 运算符:拼接符号。
  ```c
  #define CONCAT(a, b) a##b
  int var12 = 100;
  printf("%d", CONCAT(var, 12));  // 输出 100
  ```

---

7. 错误与警告指令
- #error`:强制终止编译并输出错误信息。
  ```c
  #ifndef C_VERSION
    #error "C99 compiler required"
  #endif
  ```
- #warning`(非标准):生成编译警告(部分编译器支持)。

---

8. 其他指令
- #line`:修改编译器报告的行号和文件名。
  ```c
  #line 100 "new_file.c"  // 后续代码的行号从100开始
  ```
- `#pragma`:编译器特定指令(如优化、对齐)。
  ```c
  #pragma pack(1)  // 设置结构体按1字节对齐
  ```

9. 可变参数宏(C99)
- 使用 `__VA_ARGS__` 处理可变参数:
  ```c
  #define LOG(fmt, ...) printf(fmt, __VA_ARGS__)
  LOG("Value

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值