C语言学习笔记——预处理命令

简介: C语言学习笔记——预处理命令

1.预处理命令基本介绍

  1. 使用库函数之前,应该用引入对应的#include头文件。这种以#号开头的命令称为预处理命令
  2. 这些在编译之前对源文件进行简单加工的过程,就称为预处理
  3. 预处理主要是处理以#开头的命令,例如#include <stdio.h>等。预处理命令要放在所有函数之外,而且一般都放在源文件的前面
  4. 预处理是 C语言的一个重要功能,由预处理程序完成。当对一个源文件进行编译时,系统将自动调用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译
  5. C语言提供了多种预处理功能,如宏定义、文件包含、条件编译等,合理地使用它们会使编写的程序便于阅读、 修改、移植和调试,也有利于模块化程序设计

2.预处理命令代码示例

开发一个 C 语言程序,让它暂停 5 秒以后再输出内容 "helllo, 尚硅谷!~",并且要求跨平台,在 Windows 和Linux 下都能运行,如何处理

#include<stdio.h>
#if _WIN32 //如果是 windows 平台, 就执行 #include <windows.h> 
#include <windows.h> 
#elif __linux__ //否则判断是不是 linux ,如果是 linux 就引入<unistd.h> 
#include <unistd.h> 
#endif 
int main() { //不同的平台下调用不同的函数 
#if _WIN32 //识别windows 平台 
    Sleep(5000); //毫秒
#elif __linux__ //识别 linux 平台 
    sleep(5); //秒 
#endif 
    puts("hello, 江南"); //输出 
    return 0;
}

00664af0493a4395bfa36f7b3eb1d2e3.png

3.C 语言宏定义

3.1.基本介绍

#define 叫做宏定义命令,它也是 C语言预处理命令的一种。所谓宏定义,就是用一个标识符来表示一个字符 串,如果在后面的代码中出现了该标识符,那么就全部替换成指定的字符串

3.2.代码示例

#include<stdio.h>
#define NUM 9;//在预处理中,定义NUM = 9
void main() {
  int a = 7;
  a += NUM;//a = a + NUM
  printf("a = %d", a);
}

e11795fa74c047a0b410014c530ab2a0.png

4.宏定义的形式

4.1.#define 宏名 字符串

  1. #表示这是一条预处理命令,所有的预处理命令都以 # 开头。宏名是标识符的一种,命名规则和变量相同。字符串可以是数字、表达式、if 语句、函数等
  2. 这里所说的字符串是一般意义上的字符序列,不要和 C语言中的字符串等同,它不需要双引号
  3. 程序中反复使用的表达式就可以使用宏定义

4.2宏定义代码示例

#include<stdio.h>
#define testNUM (num*7 + 7)//define进行的是简单替换,如果是关系式,需要加上小括号
void main() {
  int num = 7;
  num = testNUM * 7 + testNUM * 9;//如果不加上括号,将会变成num*7 + 7 * 7 + num*7 + 7 * 9 (define 的简单替换)
  printf("num = %d", num);
}

5f04a8b67bcb47378c8153621ffbde40.png

4.3.宏定义注意事项和细节

1.宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的替换。字符串中可以含任何字符,它可以是常数、表达式、if 语句、函数等,预处理程序对它不作任何检查,如有错误,只能在编译已被宏展开后的源程序时发现。(宏定义对程序运行的正确与否,预处理程序不管, 它只负责运行你的宏定义,是否出错只有编译后才知道)

2.宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起替换(经常会导致错误!)

3.宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用#undef命令(如果取消之前的宏定义,可以用#undef)

#define PI 3.14159 
int main(){ 
    printf("PI=%f", PI); 
    return 0;
} 
#undefPI //取消宏定义 
void func(){ // Code
    printf("PI=%f", PI);//错误,这里不能使用到 PI 了 
}

4.代码中的宏名如果被引号包围,那么预处理程序不对其作宏代替(以下代码示例)

#include <stdio.h> 
#define jiangnan 100 
int main(){
    int num = jiangnan;//jiangnan被进行替换,使得语句变成 int num = 100 
    printf("jiangnan\n"); //jiangnan在双引号内,不进行替换,输出jiangnan
    return 0;
}

5f1ef24e7dcc473abcc821fb983d3236.png

5.宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名,在宏展开时由预处理程序层层代换(以下代码示例)

#include<stdio.h>
#define jiangnan 100
#define jiangnan1 jiangnan * 100 * 100//宏定义允许嵌套,jiangnan被替换成100
void main() {
  printf("%d", jiangnan1);
}

6f69dc3bfaf5433f8b5724b290a69562.png

6.习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母

7.可用宏定义表示数据类型,使书写方便(代码示例)

#include<stdio.h>
#define D double
#define I int
void main() {
  D a = 1234.567;//D被替换为double
  I b = 1234567;//I被替换为int
  printf("a = %f\nb = %d", a, b);
}

bd4c0bb84f7d4bf9bcb89e9e4525c4b0.png

8.宏定义表示数据类型和用 typedef定义数据说明符的区别:宏定义只是简单的字符串替换,由预处理器来处理;而 typedef 是在编译阶段由编译器处理的,它并不是简单的字符串替换,而给原有的数据类型起一个新的名字, 将它作为一种新的数据类型

5.带参数的宏定义

5.1.基本介绍

1.C语言允许宏带有参数。在宏定义中的参数称为“形式参数”,在宏调用中的参数称为“实际参数”,这点和函数有些类似(不完全相同)

2.对带参数的宏,在展开过程中不仅要进行字符串替换,还要用实参去替换形参

3.带参宏定义的一般形式为 #define 宏名(形参列表) 字符串 ,在字符串中可以含有各个形参

4.带参宏调用的一般形式为 : 宏名(实参列表);

5.2.带参数的宏定义代码示例

#include<stdio.h>
//MAX为带参数的宏定义
//a和b为形式参数
//(a > b) ? a : b是带参数的宏对应字符串,该字符串可以使用形式参数
#define MAX(a,  b) (a > b) ? a : b
int main() {
  int x, y, max;
  printf("请输入两个数字:\n");
  scanf_s("%d%d", &x, &y);
  //max调用MAX(x,y)带参宏定义
  //在预处理阶段,由预处理器完成,并由实际参数代替形式参数
  //替换后(x > y) ? x : y
  max = MAX(x, y);
  printf("max = %d\n", max);
}

aa0dce2155fe4c5b8e99f239af4c252d.png

5.3.带参宏定义的注意事项和细节

  1. 参宏定义中,形参之间可以出现空格,但是宏名和形参列表之间不能有空格出现
#define MAX(a,b) (a > b) ? a : b

2.在带参宏定义中,不会为形式参数分配内存,因此不必指明数据类型。(与函数区别,函数的形式参数必须申明数据类型)而在宏调用中,实参包含了具体的数据, 要用它们去替换形参,因此实参必须要指明数据类型

3.在宏定义中,字符串内的形参通常要用括号括起来以避免出错

#include<stdio.h>
#define sq1(a) (a) * (a)
#define sq2(b) b * b
void main() {
  int num1, num2, result1, result2;
  printf("请输入两个数字:\n");
  scanf_s("%d%d", &num1, &num2);
  result1 = sq1(num1 + 1);//sq(a) = (num1 + 1) * (num1 + 1)
  result2 = sq2(num2 + 1);//sq(a) = num2 + 1 * num2 + 1 = 2 * num2 +1
  printf("%d + 1的平方为%d\n错误示范(不加括号):%d + 1的平方为%d ", num1, result1, num2, result2);
}

35262ed3fe89481a962bc76b49685e93.png

5.4.带参宏定义和函数的区别

  1. 宏展开仅仅是字符串的替换,不会对表达式进行计算;宏在编译之前就被处理掉了,它没有机会参与编译,也不会占用内存
  2. 函数是一段可以重复使用的代码,会被编译,会给它分配内存,每次调用函数,就是执行这块内存中的代码
  3. 代码示例
#include<stdio.h>
#define sq2(num) num * num
int sq1(int num) {
  return num * num;
}
void main() {
  int num1 = 1, num2 = 1;
  for (num1 = 1; num1 <= 7; ) {
    //先执行sq1(num1++),sq1(num1++)中先进行sq1(num1)再进行num++
    //因此需要进行num1 - 1才能进行正确输出
    printf("%d的平方为%d\n", num1 - 1, sq1(num1++));
  }
  for (num2 = 1; num2 <= 7; ) {
    //在执行sq2(num2++)时,进行了两次#define sq2(num) num * num
    //因为#define进行的是简单替换,因此每次进行sq2(num2++)时
    //进行了两次num2++
    printf("%d的平方为%d\n", num2 - 2, sq2(num2++));
  }
}

408f02b294524e7e82d0d632163cbc66.png




相关文章
|
2月前
|
编译器 C语言
C语言--预处理详解(1)
【10月更文挑战第3天】
|
2月前
|
编译器 Linux C语言
C语言--预处理详解(3)
【10月更文挑战第3天】
|
2月前
|
自然语言处理 编译器 Linux
【C语言篇】编译和链接以及预处理介绍(上篇)1
【C语言篇】编译和链接以及预处理介绍(上篇)
42 1
|
1月前
|
C语言
【c语言】你绝对没见过的预处理技巧
本文介绍了C语言中预处理(预编译)的相关知识和指令,包括预定义符号、`#define`定义常量和宏、宏与函数的对比、`#`和`##`操作符、`#undef`撤销宏定义、条件编译以及头文件的包含方式。通过具体示例详细解释了各指令的使用方法和注意事项,帮助读者更好地理解和应用预处理技术。
24 2
|
2月前
|
编译器 Linux C语言
【C语言篇】编译和链接以及预处理介绍(下篇)
【C语言篇】编译和链接以及预处理介绍(下篇)
33 1
【C语言篇】编译和链接以及预处理介绍(下篇)
|
2月前
|
C语言
C语言学习笔记-知识点总结上
C语言学习笔记-知识点总结上
81 1
|
2月前
|
C语言
C语言--预处理详解(2)
【10月更文挑战第3天】
|
2月前
|
编译器 C语言
C语言预处理详解
C语言预处理详解
|
2月前
|
存储 C语言
【C语言篇】编译和链接以及预处理介绍(上篇)2
【C语言篇】编译和链接以及预处理介绍(上篇)
38 0
|
3月前
|
存储 C语言
C语言程序设计核心详解 第七章 函数和预编译命令
本章介绍C语言中的函数定义与使用,以及预编译命令。主要内容包括函数的定义格式、调用方式和示例分析。C程序结构分为`main()`单框架或多子函数框架。函数不能嵌套定义但可互相调用。变量具有类型、作用范围和存储类别三种属性,其中作用范围分为局部和全局。预编译命令包括文件包含和宏定义,宏定义分为无参和带参两种形式。此外,还介绍了变量的存储类别及其特点。通过实例详细解析了函数调用过程及宏定义的应用。