预处理大致解析(参见《C语言深度解剖》更好得到学习)

预处理大致解析(参见《C语言深度解剖》更好得到学习)

  • 一、预定义符号
  • 二、#define
  • 三、其他预处理指令
    • <font face = "楷体" size = 5 color = blue>//库文件包含 //#include < filename.h > //直接在库文件所在的标志路径查找,找不到报编译错误
    • 这样是不是可以说,对于库⽂件也可以使⽤ “” 的形式包含? 答案是肯定的,可以,但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。

一、预定义符号

1.C语言设置了一些预定义符号,可以直接使用,这些符号在预处理阶段会被处理!
  __ FILE __ //进行编译的源文件
  __ LINE __ //文件当前的行号
  __ DATE __ //文件被编译的日期
  __ TIME __ //文件编译的时间
  __ STDC __ //如果编译器遵循ANSI C,其值为1,否则未定义!

//测试 - - -

int main() {
	printf("%s\n", __FILE__);
	printf("%d\n", __LINE__);
	printf("%s\n", __DATE__);
	printf("%s\n", __TIME__);
	return 0;
}

1

二、#define

1.#define 定义常量
#define M 100
//注意后面不加(;),加了可能在替换是会出错!

#define forever for(;;)
int main() {
	//for循环的判断部分什么都不写,可能会造成死循环!
	forever;
	return 0;
}

2.#define 定义宏
//宏的定义是参数进行文本替换,只是在预处理阶段的替换,没有什么类型啥的说法!
//语法:#define name( parament-list ) stuff
//说明:左括号必须与name紧邻,如果隔开,解释成为stuff的一部分!
//#define 定义的宏括号问题!
#define SQUARE(X) XX
int main() {
printf(“%d\n”, SQUARE(5));
int a = 2;
printf(“%d\n”, SQUARE(a+1));
return 0;
}

1
//明显第二个有问题,宏定义的本质是等效替换,第二个替换就出问题了,成 //a+1a+1 = 2+2+1 = 5,所以这类定义一定要在替换的参数上加(),则可以这样定义
#define SQUEARE(X) ((X)*(X))
//这样,在任何情况下,没有问题!

//带副作用的宏问题!
//这个主要是++引起的问题!
//直接看列子

int main() {
	int a = 1;
	int b = 2;
	int max1 = MAX(a, b);
	//上面文本替换
	//((a)>(b)?(a):(b))
	printf("max1 = %d\n", max1); //2
	//++带来的副作用
	int max2 = MAX(a++, b++);
	//((a++)>(b++)?(a++):(b++))
	//a = 1,b = 2
	//a++,b++;-->  a = 2,b == 3,
	//因为b大
	//所以返回b++;(先使用,后++)
	//表达式 == 3:
	// a = 2,b = 4;
	printf("max2 = %d\n", max2); //3
	printf("a = %d\n", a);
	printf("b = %d\n", b);
	return 0;
}

int main() {
int a = 1;
int b = 2;
int max1 = MAX(a, b);
//上面文本替换
//((a)>(b)?(a):(b))
printf(“max1 = %d\n”, max1); //2
//++带来的副作用
int max2 = MAX(a++, b++);
//((a++)>(b++)?(a++):(b++))
//a = 1,b = 2
//a++,b++;–> a = 2,b == 3,
//因为b大
//所以返回b++;(先使用,后++)
//表达式 == 3:
// a = 2,b = 4;
printf(“max2 = %d\n”, max2); //3
printf(“a = %d\n”, a);
printf(“b = %d\n”, b);
return 0;
}

//上面副作用的代码已解释额!
---------------------------------

//宏替换规则
// 在程序中扩展#define定义符号和宏时,需要涉及⼏个步骤。
// * 在调⽤宏时,⾸先对参数进⾏检查,看看是否包含任何由#define定义的符号。如果是,它们⾸先被替换。
// * 替换⽂本随后被插⼊到程序中原来⽂本的位置。对于宏,参数名被他们的值所替换。
// * 最后,再次对结果⽂件进⾏扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。

 宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

----------------------------------------
//和宏函数的对比
//宏函数通常执行简单的运算
#define MAX(x,y) ((x)>(y)?(x):(y))
//写简单代码宏的优势
//1.⽤于调⽤函数和从函数返回的代码可能⽐实际执⾏这个⼩型计算⼯作所需要的时间更多。所以宏⽐函数在程序的规模和速度⽅⾯更胜⼀筹。
2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使⽤。反之这个宏怎可以适⽤于整形、⻓整型、浮点型等可以⽤于 > 来⽐较的类型。宏是类型⽆关的。
//在这儿说一下,宏的最大优势就是无论什么,都可以文本替换

//和函数相比宏的劣势
//1.每次使⽤宏的时候,⼀份宏定义的代码将插⼊到程序中。除⾮宏⽐较短,否则可能⼤幅度增加程序的⻓度。Y//2.宏不能调试
//3.宏与类型无关,不够严谨
//4.宏处理不好一定会带来优先级问题!


//下面对malloc进行宏的处理
#define MALLOC(TYPE,NUM) (TYPE*) malloc(sizeof(TYPE)*NUM)

example: int * tmp = MALLOC(int,10);
1

//下面介绍两个不常见的用法!
//#运算符
//#运算符将宏的⼀个参数转换为字符串字⾯量。它仅允许出现在带参数的宏的替换列表中。#运算符所执⾏的操作可以理解为”字符串化“
//也就是说在宏的替换参数前面加#,会把参数转换成字符串

//这儿宏定义所谓的  #参数  把参数替换后解析成一个字符串
#define PRINT(val,format) printf("the val of "   #val  " is "  format "\n",val)

int main() {
	int a = 1;
	int b = 2;
	PRINT(a, "%d");
	PRINT(b, "%d");
	return 0;
}

//这儿宏定义所谓的 #参数 把参数替换后解析成一个字符串
#define PRINT(val,format) printf("the val of " #val " is " format “\n”,val)
int main() {
int a = 1;
int b = 2;
PRINT(a, “%d”);
PRINT(b, “%d”);
return 0;
}

1

//##运算符问题
//##的作用是把它两边的符号合成一个符号
//可以是把宏的参数替换后,进行合成
//下面看列子
//写宏函数

//\续航符后面什么也不要(空格也不行)
//下面##把type和max连接起来
#define GENERIC_MAX(type)\
type type##_max(type x,type y)\
{\
	return ((x)>(y)?(x):(y));\
}

//声明函数
GENERIC_MAX(int)
GENERIC_MAX(float)

int main() {
	//直接使用
	int a = 1;
	int b = 2;
	int max = int_max(a, b);
	printf("max = %d\n", max);
	return 0;
}

//\续航符后面什么也不要(空格也不行)
//下面##把type和max连接起来
#define GENERIC_MAX(type)
type type##_max(type x,type y)
{
return ((x)>(y)?(x):(y));
}
//声明函数
GENERIC_MAX(int)
GENERIC_MAX(float)
int main() {
//直接使用
int a = 1;
int b = 2;
int max = int_max(a, b);
printf(“max = %d\n”, max);
return 0;
}

1

三、其他预处理指令

1、#undef NAME
//如果现存的⼀个名字需要被重新定义,那么它的旧名字首先要被移除。

2、许多C的编译器提供了⼀种能⼒,允许在命令⾏中定义符号。⽤于启动编译过程。
例如:当我们根据同⼀个源⽂件要编译出⼀个程序的不同版本的时候,这个特性有点⽤处。(假定某个程序中声明了⼀个某个⻓度的数组,如果机器内存有限,我们需要⼀个很⼩的数组,但是另外⼀个机器内存⼤些,我们需要⼀个数组能够⼤些。)

#include <stdio.h>
int main()
{
 int array [ARRAY_SIZE];
 int i = 0;
 for(i = 0; i< ARRAY_SIZE; i ++)
 {
 array[i] = i;
 }
 for(i = 0; i< ARRAY_SIZE; i ++)
 {
 printf("%d " ,array[i]);
 }
 printf("\n" );
 return 0;
}
//linux 环境演⽰: gcc -D ARRAY_SIZE=10 programe.c //可以指定参数的大小
//条件编译 ``
//条件编译
//
//#define __DEBUG__
//
//int main() {
//	int arr[10] = { 0 };
//	for (int i = 0; i < 10; i++) {
//		arr[i] = i;
//		//条件编译
//#ifdef __DEBUG__
//		printf("%d ", arr[i]);
//#endif
//	}
//	return 0;
//}


//int main() {
//	//#if(真/假)
//    //#end if 
//#if(0)
//	printf("haha\n");
//#endif
//	return 0;
//}

//#define M 3
//int main() {
//#if(M == 1)
//	printf("haha\n");
//#elif(M == 3)
//	printf("hehe\n");
//#else
//	printf("Hello World\n");
//#endif
//	return 0;
//}

//#define M 0
//
//int main() {
//#if defined(M)
//	printf("haha");
//#endif
//
//#if !defined(M)
//	printf("hehe");
//#endif
//	return 0;
//}

1

//头文件包含问题(重要)
//本地文件包含
//#include “filename”
//搜索路径:先在源文件路径下查找,如果找不到,直接到库函数的头文件所在路径查找,找不到,报错!
//Linux环境标准头文件路径
// /usr/include
//VS标准头文件的路径
//C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include
//VS2013默认路径

//库文件包含
//#include < filename.h >
//直接在库文件所在的标志路径查找,找不到报编译错误

这样是不是可以说,对于库⽂件也可以使⽤ “” 的形式包含?
答案是肯定的,可以,但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。

//防止头文件重复包含问题
#include 包含的头文件,会在预编译期间直解替换成头文件的内容,如果头文件多次包含,就会多次替换,造成重复包含问题!
//怎么解决?那就是使用预编译指令条件编译
#ifndef __TEST_H__
#define __TEST_H__
//头⽂件的内容 
#endif //__TEST_H__
或直接使用#pragma once防止头文件重复包含
完结!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/427737.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

谷歌seo推广效果怎么判断?

要想判断谷歌SEO推广效果&#xff0c;核心在于观察和分析几个关键指标&#xff0c;一个网站最重要的自然就是流量&#xff0c;没有流量的网站说到底就是一个被雪藏的花架子&#xff0c;没什么意义&#xff0c;所以看流量自然就是最重要的指标&#xff0c;SEO做得好&#xff0c;…

阿里云服务器2核4G租用价格_2核4G支持人数新能测评

阿里云2核4G服务器多少钱一年&#xff1f;2核4G配置1个月多少钱&#xff1f;2核4G服务器30元3个月、轻量应用服务器2核4G4M带宽165元一年、企业用户2核4G5M带宽199元一年。可以在阿里云CLUB中心查看 aliyun.club 当前最新2核4G服务器精准报价、优惠券和活动信息。 阿里云官方2…

远程IT技术支持软件有哪些

什么是远程支持软件 远程支持软件允许 IT 部门和管理员通过内部网络或互联网从远程位置连接和控制设备&#xff0c;以解决技术问题并自动执行日常任务。企业使用远程支持软件来解决技术问题并增强安全性&#xff0c;而无需技术人员物理访问需要支持的设备。 远程支持解决方案…

PHP项目中composer和Git的组合使用

highlight: 在国内由于众所周知的原因&#xff0c;composer的package可能无法访问&#xff0c;解决办法是使用中国的全镜像&#xff1a; composer config -g repositories.packagist composer http://packagist.phpcomposer.com 在需要使用composer package的地方创建composer…

车载测试中:internal lock 的意思

internal lock的用法讲解 英语单词 \\"internal lock\\" 的用法讲解 \\"Internal lock\\" 是一个常见的词组&#xff0c;通常用于讨论机械、锁具和安全等方面的话题。下面将介绍这个词组的几种用法。 1. 机械锁中的 \\"internal lock\\" 在机…

YOLOv8 DeepSORT实现智能交通监控-改进yolo单目测距及速度测量-流量计数

YOLOv8 DeepSORT是一种基于目标检测和跟踪技术的智能交通监控系统。它基于YOLOv8&#xff0c;通过加入DeepSORT算法实现目标跟踪&#xff0c;同时还改进了YOLOv8的单目测距及速度测量技术和流量计数功能。 该系统可以通过摄像头或视频源实时捕获图像&#xff0c;并自动检测和跟…

python钉钉机器人加签

代码 import requests import json import time import hmac import hashlib import base64 import urllib.parsedef get_url(access_token, secret):t,s get_sign(secret)webhook_url fhttps://oapi.dingtalk.com/robot/send?access_token{access_token}&timestamp{t}…

TSMaster 2024 最新版本实用功能更新,收藏不迷路

TSMaster 作为一款软硬件解耦&#xff0c;快速迭代&#xff0c;并不断被软件定义的国产工业软件&#xff0c;保持每周一次的更新频率&#xff0c;已经渗透到全球汽车产业企业。在2024年初&#xff0c;进行了大规模的功能更新&#xff01;不仅对软件进行了全面优化&#xff0c;同…

python入门基础:深入了解Python开发工具,PyCharm安装运行详解

Python 是一种解释型语言&#xff0c;通常不需要像 C 或 C 那样的传统编译过&#xff0c;上一篇写了关于下载安装Python解释器的安装以及使用的详解&#xff0c;接下来写有一些工具更有助于开发、编译和打包 Python 项目的&#xff1a;vscode、pycharm、pip。 以下是关于的 Py…

六西格玛培训对薪资的真实影响:不只是数字的变化

近年来&#xff0c;提升自身能力、寻求职业突破成为了许多职场人士的共同追求。其中&#xff0c;六西格玛培训作为一种先进的质量管理方法&#xff0c;受到了不少企业和个人的青睐。那么&#xff0c;六西格玛培训对个人的薪资水平究竟有何影响&#xff1f;本文&#xff0c;天行…

AwesomeTechnologyWeekly值的关注的中文社区优质技术周刊一览

作为开发者&#xff0c;我们每天都需要吸收大量的信息补充我们的知识体系. Awesome Technology Weekly Zh-Hans 项目收集了中文技术社区各个领域的高质量的中文技术月/周/日刊&#xff0c;定时刷新获取最新一期中文技术月/周/日刊进行展示. 访问网站开始关注吧~&#xff1a;ht…

Python - getpass

文章目录 关于 getpass基本使用语法说明其它 关于 getpass getpass 是 Python 自带标准库 Python 官方文档 - getpass https://docs.python.org/3/library/getpass.html 基本使用 我们在看视频教程中&#xff0c;老师如果不想在代码中暴露 token、密码之类的信息&#xff0c…

巧【二叉搜索树的最近公共祖先】【二叉搜索树的性质】Leetcode 235. 二叉搜索树的最近公共祖先

【二叉搜索树的最近公共祖先】【二叉搜索树性质】Leetcode 235. 二叉搜索树的最近公共祖先 【巧】解法1 利用二叉搜索树有序的性质解法2 采用二叉树求最近公共祖先的方法——后序遍历 ---------------&#x1f388;&#x1f388;235. 二叉搜索树的最近公共祖先 题目链接&#x…

帮公司面试了个要25K的测试,我问了他这些问题...

深耕IT行业多年&#xff0c;我们发现&#xff0c;对于一个程序员而言&#xff0c;能去到一线互联网公司&#xff0c;会给我们以后的发展带来多大的影响。 很多人想说&#xff0c;这个我也知道&#xff0c;但是进大厂实在是太难了&#xff0c;简历投出去基本石沉大海&#xff0…

YOLOV9训练集制作+Train+Val记录

一、YOLO数据集格式分布 在YOLO中&#xff0c;数据集的分布如图&#xff0c;在dataset文件夹下有imags&#xff08;图片&#xff09;和labels&#xff08;标签&#xff09;。在images和labels文件夹下又分别存放三个文件夹&#xff0c;分别对应测试集、训练集、验证集&#xff…

如何使用Python Flask发布web页面至公网并实现远程访问【内网穿透】

文章目录 1. 安装部署Flask2. 安装Cpolar内网穿透3. 配置Flask的web界面公网访问地址4. 公网远程访问Flask的web界面 本篇文章主要讲解如何在本地安装Flask&#xff0c;以及如何将其web界面发布到公网进行远程访问。 Flask是目前十分流行的web框架&#xff0c;采用Python编程语…

Java中的排序算法

引言&#xff1a; 当谈到编程语言中的排序&#xff0c;Java 作为一种广泛使用的编程语言&#xff0c;提供了许多强大的排序算法来满足不同的需求。排序是一种将一组数据按照特定顺序重新排列的过程&#xff0c;通常是按照升序或降序排列。在 Java 中&#xff0c;我们可以利用内…

【airtest】自动化入门教程(二)airtest操作

目录 一、touch 二、wait 三、swipe 四、exists 五、text 六、keyevent 七、snapshot 八、sleep 九、断言 9.1 assert_exists 9.2 assert_not_exists 9.3 assert_equal 9.4 assert_not_equal 前言&#xff1a;本文主要针对aritest部分的基础操作,aritest是一个跨平…

AJAX 学习笔记(Day3)

「写在前面」 本文为黑马程序员 AJAX 教程的学习笔记。本着自己学习、分享他人的态度&#xff0c;分享学习笔记&#xff0c;希望能对大家有所帮助。推荐先按顺序阅读往期内容&#xff1a; 1. AJAX 学习笔记&#xff08;Day1&#xff09; 目录 3 AJAX 原理 3.1 XMLHttpRequest 3…

遭遇CC攻击如何做防护策略

CC&#xff08;Challenge Collapsar&#xff09;攻击是一种常见而具有破坏力的拒绝服务攻击&#xff08;DDoS&#xff09;&#xff0c;对网络安全造成威胁。为了保护网络免受这类恶意攻击&#xff0c;采取有效的防护策略至关重要。本文将介绍一些可以帮助保护您的网络免受CC攻击…