程序环境和预处理(下)

简介: 程序环境和预处理(下)

3.2.4 #和##

使用 # , 把一个宏参数变成对应的字符串

1. #include <stdio.h>
2. #define PRINT(n) printf("the value of " #n " is %d\n", n)
3. //#n相当于字符串("a","b"),插入到这个位置,三个都是字符串,//不加引号(;)
4. 
5. int main()
6. {
7.  int a = 10;
8.  PRINT(a);
9.  int b = 20;
10.   PRINT(b);
11.   printf("the value of " "b" " is %d\n", b);//the value of 是一个字符串,b是一个字符串,is %d\n 是一个字符串
12.   return 0;
13. }

注意:字符串是有自动连接的特点

## 可以把位于它两边的符号合成一个符号。 它允许宏定义从分离的文本片段创建标识符。

 

1. #include <stdio.h>
2. #define CAT(Class, num) Class##num
3. 
4. int main()
5. {
6.  int Class1 = 100;
7.  printf("%d\n", CAT(Class, 1));//Class1
8.  return 0;
9. }

打印结果:100

3.2.5 带副作用的宏参数

当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能 出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。

1. #include <stdio.h>
2. 
3. int main()
4. {
5.  int a = 2;
6.  int b = 0;
7.  b = ++a;//a = 3; b = 3//这个就有副作用
8.  a = 2;
9.  b = a + 1;//a = 2; b = 3 这个没有副作用
10.   return 0;
11. }
1. #include <stdio.h>
2. #define MAX(x, y) ((x) > (y)? (x): (y))
3. int main()
4. {
5.  int a = 3;
6.  int b = 5;
7.  int m = 0;
8.  m = MAX(a++, b++);//((a++) > (b++) ? (a++) : (b++))后置++,先使用后++ 3>5 a=4 b=6  m=5    b=7
9.  printf("%d\n", m);
10.   printf("a = %d, b = %d", a, b);
11.   return 0;
12. }

m=6 a=4 b=7

注意是替换

3.2.6 宏和函数对比

宏通常被应用于执行简单的运算

1.用于调用函数和从函数返回的代码可能比实际执行小型计算工作所需要的时间更多。 所以宏比函数在程序的规模和速度方面更胜一筹 。

2. 更为重要的是函数的参数必须声明为特定的类型。 所以函数只能在类型合适的表达式上使用。反之   宏是类型无关的 。

缺点: 1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序 的长度。2. 宏是没法调试的。(宏是替换)(把代码插入到相应的位置)3.宏由于类型无关,也就不够严谨4.宏可能会带来运算符优先级的问题,导致程容易出现错误。

宏的参数可以出现类型,但是函数不可以

1. #include <stdio.h>
2. #define MALLOC(num,type) (type*)malloc(num*sizeof(type))
3. int main()
4. {
5.  int* p1 = (int*)malloc(10 * sizeof(int));
6.  int* p2 = MALLOC(10, int);
7. //p1 p2效果一样
8.  return 0;
9. }

3.2.7 命名约定

习惯:把宏名全部大写 函数名不要全部大写

3.3 #undef

这条指令用于移除一个宏定义。

#undef NAME

// 如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。

1. #include <stdio.h>
2. #define MAX(x, y) ((x) > (y) ? (x) : (y))
3. 
4. int main()
5. {
6. #undef MAX
7.  int m = 0;
8.  m = MAX(2, 3);//此时,这个代码无法用MAX这个宏
9.  printf("%d\n", m);
10.   return 0;
11. }

3.4 命令行定义

许多 C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。 编译时进行赋值,代码中并没有进行赋值

例如:当我们根据同一个源文件要编译出不同的一个程序的不同版本的时候,这个特性有点用处。(假 定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数组,但是另外一个机器内存大写,我们需要一个数组能够大写。)

3.5 条件编译

条件编译指令:在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃编译

比如:调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译。  

1. #include <stdio.h>
2. int main()
3. {
4.  int i = 0;
5.  for (i = 0; i < 10; i++)
6.  {
7. #if 0
8. //1的时候就可以运行,不能是变量
9.    printf("%d\n", i);//此时,这个代码就不运行了
10. #endif
11. //这是预处理命令
12.     //其他
13.   }
14.   return 0;
15. }

常见的条件编译指令:

1.

#if 常量表达式

//...

#endif

// 常量表达式由预处理器求值。

2. 多个分支的条件编译

#if 常量表达式

     //...

#elif 常量表达式

     //...

#elif 常量表达式

     //...

#else

    //...

#endif

3. 判断是否被定义

(1)定义

#if defined(symbol)

     //...

#endif

例如:#define M 100;symbol就是指M

(2)定义

#ifdef symbol

     //...

#endif

(3)没有定义

#if !defined(symbol)

     //...

#endif

(4)没有定义

#ifndef symbol

     //...

#endif

4. 嵌套指令

#if defined(OS_UNIX)

     #ifdef OPTION1

         unix_version_option1 ();

     #endif

     #ifdef OPTION2

          unix_version_option2 ();

     #endif

#elif defined(OS_MSDOS)

     #ifdef OPTION2

             msdos_version_option2 ();

     #endif

#endif

3.6 文件包含

#include 指令可以使另外一个文件被编译。

预处理器先删除这条指令,并用包含文件的内容替换。 如果一个源文件被包含10 次,那就实际被编译 10 次。

3.6.1 头文件被包含的方式

(1)本地文件包含 : #include "filename"  

 先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标

准位置查找头文件。

(2)库文件包含 : #include <filename.h>

 查找头文件直接去标准路径下去查找

库文件也可以用""的形式包含,但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。

3.6.2 嵌套文件包含

如果文件被多次包含,这样就会造成文件内容的重复

解决办法:

(1)

#ifndef __TEST_H__

#define __TEST_H__

// 头文件的内容

#endif   //__TEST_H__ 根据文件名改变

(2)

#pragma once

以上两种办法可以避免头文件的重复引入

相关文章
|
API 索引
ElasticSearch索引模板
ElasticSearch索引模板
518 1
|
Java 关系型数据库 数据安全/隐私保护
八股文-面向对象的理解
面向对象编程(Object-Oriented Programming,OOP)是一种程序设计方法,其核心思想是将问题抽象为由若干个对象,通过这些对象之间的调用、配合及协调,共同完成当前的问题。 面向对象的三大基本特征:封装、继承和多态。 面向对象的五大基本原则:单一职责原则、开放封闭原则、里氏替换原则、接口隔离原则、依赖反转原则。
655 1
八股文-面向对象的理解
|
XML NoSQL Java
Redis - 一篇走心的 RedisUtil 工具类
Redis - 一篇走心的 RedisUtil 工具类
4073 0
Redis - 一篇走心的 RedisUtil 工具类
|
机器学习/深度学习 人工智能 监控
足球预测:进球率预测法的接力人——AI预测
足球预测已有近200年历史,但依赖“自媒体人”推送的方式存在诸多问题。本文介绍了一种基于1990年大卫·杰克逊和K.R.莫舍斯基研究的进球率预测法,通过比较球队平均进球率来预测比赛结果。结合AI技术,该方法可批量处理数据并优化预测模型,提高预测准确性。文中还展示了AI预测的实际应用案例及代码实现,并强调了AI在赛事监控中的重要性。尽管AI预测效果显著,但仍需理性对待。
1412 1
|
9月前
|
存储 监控 安全
【亲测有用】数据中台数据安全管理能力演示
杭州奥零数据科技有限公司成立于2023年,专注于数据中台业务,维护开源项目AllData并提供商业版解决方案。AllData提供数据集成、存储、开发、治理及BI展示等一站式服务,支持AI大模型应用,助力企业高效利用数据价值。
【亲测有用】数据中台数据安全管理能力演示
|
12月前
|
机器学习/深度学习 人工智能 搜索推荐
AI音色克隆能模拟人的特质吗
AI技术迅猛发展,从文生文到文生视频,不断重塑生活与娱乐模式。音色克隆技术作为革命性成果,能精准捕捉并复制个人声音特征,应用于动画配音、游戏语音、音乐创作及教育领域,极大提升用户体验和内容创作效率。对于播客而言,这既是挑战也是机遇,借助该技术可提升作品趣味性和丰富度,提高创作效率,吸引更多流量。未来,AI音色克隆技术将在更多领域发挥重要作用。
|
11月前
|
数据挖掘 BI 数据安全/隐私保护
三大CRM品牌深度评测:销售易、悟空、超兔
**销售易CRM**:专为中国市场设计,提供强大的本土化服务和定制化解决方案,涵盖客户管理、销售自动化、市场营销、服务支持及数据分析等功能。 **悟空CRM**:以“智慧、灵活、高效”为核心,用户界面友好,支持销售、营销和服务的全面自动化,并具备高级数据分析和协作工具。 **超兔CRM**:专注于数据分析和集成性,提供360度客户视图、销售自动化、市场营销、客户服务及安全性保障,适用于各种规模的企业。 通过对比三者的功能和优势,企业可以更明智地选择适合自己的CRM系统,从而更好地管理客户关系,提升业务绩效。
|
XML Java 数据库连接
技术笔记:Maven的pom.xml(坐标的gav、依赖的scope)
技术笔记:Maven的pom.xml(坐标的gav、依赖的scope)
681 0
|
存储 Java 数据建模
Java零基础-三维数组详解!
【10月更文挑战第21天】Java零基础教学篇,手把手实践教学!
517 4
|
tengine 应用服务中间件 Linux
【Linux环境】如何在Nginx(或Tengine)服务器上安装ssl证书----介绍nginx服务器类型证书的下载与安装操作
【Linux环境】如何在Nginx(或Tengine)服务器上安装ssl证书----介绍nginx服务器类型证书的下载与安装操作
920 0