第六十二天学习记录:C语言进阶:C语言预处理2

news2025/1/9 1:25:22
带副作用的宏参数

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

MAX宏可以证明具有副作用的参数引起的问题。

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>

#define MAX(X,Y) ((X)>(Y)?(X):(Y))

int main()
{
	int a = 10;
	int b = 11;
	int max = MAX(a++, b++);
	printf("%d\n", max);
	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}

在这里插入图片描述

宏和函数对比

宏通常被应用于执行简单的运算。
宏的优势:
1、用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数再程序的规模和速度方面更胜一筹
2、更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之宏可以适用于整型,长整型,浮点型等可以用于>来比较的类型。宏是类型无关的
3、宏的参数可以出现类型,函数不行。
宏的劣势:
1、每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
2、宏是没法调试的。
3、宏由于类型无关,也就不够严谨。
4、宏可能会带来运算优先级的问题,导致程序容易出错。

属性#define定义宏函数
代码长度每次使用时,宏代码都会被插入到程序中。除了非常小的宏之外,程序的长度会大幅度增长函数代码只出现于一个地方;每次使用这个函数时,都调用那个地方的同一份代码
执行速度更快存在函数的调用和返回的额外开销,所以相对慢一些
操作符优先级宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写的时候多些括号。函数参数只在函数调用的时候求值一次,它的结果值传递给函数。表达式的求值结果更容易预测。
参数类型宏的参数与类型无关,只要对参数的操作是合法的,它就可以使用于任何参数类型。函数的参数是与类型有关的,如果参数的类型不同,就需要不同的函数,即使他们执行的任务是不同的。
调试宏是不方便调试的函数是可以逐语句调试的
递归宏是不能递归的函数是可以递归的

命名约定

一般来讲函数和宏的使用语法很相似。所以语言本身没法帮我们区分二者。平时的一个习惯是:
把宏名全部大写,函数名不要全部大写。

#undef

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

#include <stdio.h>
#define MAX 100
int main()
{
	printf("%d\n", MAX);
#undef MAX
	//printf("%d\n", MAX);这句无法使用
	return 0;
}

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

命令行定义

许多C的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。例如:当我们根据同一个源文件要编译出不同的一个程序的不同版本的时候,这个特性有点用处。(假定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数组,但是另外一个机器内存大写,我们需要一个数组能够大写。)(windows不方便操作)

条件编译

在编译一个程序的时候我们如果要将一句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。
比如说:
调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译。

#include <stdio.h>

#define DEBUG

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		arr[i] = 0;
#ifdef DEBUG
		printf("%d ", arr[i]);
#endif DEBUG
	}
	return 0;
}

常见的条件编译指令

1、
#if 常量表达式
//…
#endif
//常量表达式由预处理器求值。
如:

#define __DEBUG__ 1
#if __DEBUG__
		//...
#endif
#include <stdio.h>

#define DEBUG

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		arr[i] = 0;
#if 0
		printf("%d ", arr[i]);
#endif
	}
	return 0;
}

问:#if 0 #endif 和if(0),有啥区别?什么时候用前者?为什么会存在前者?
ChatAI答:
#if 语句是一个预处理指令,用于在编译前进行条件判断。#if 0 表示条件为假,因为 0 是假值。因此,#if 0#endif 之间的代码不会被编译,类似于注释掉了代码。同时,由于是在编译期间就被判断为假,所以不会出现任何运行时开销。

与之相反, if(0) 是一个运行时判断,每次程序执行到该语句时都会进行判断,再决定是否执行包含在其内部的语句。也就是说,即使是假值,在运行时也会评估条件表达式,因此这会在程序的性能上带来一定的损失。

通常情况下,我们使用 #if 0 来注释掉一些不需要的代码,而不想直接删除这些代码。这种方法可以方便地在需要时恢复这些代码,而不必手动输入或复制粘贴代码。而使用 if(0) 注释掉代码可能会导致一些 IDE 或编辑器对注释代码的报错或语法高亮问题。

所以,一般情况下,我们应该优先使用 #if 0 来注释掉代码,只在某些特殊情况下(比如需要在运行期间动态控制该代码是否执行)才使用 if(0)

2、多分支的条件编译

#if 常量表达式
//...
#elif常量表达式
//...
#else
//...
#endif
int main()
{
#if 1==1
	printf("haha\n");
#elif 2==1
	printf("hehe\n");
#else
	printf("hey\n");
#endif
	return 0;
}

3、判断是否被定义
#if defined(symbol)&#ifdef(symbol)
#ifdef symbol

#if !defined(symbol)
#ifndef symbol

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

文件包含

我们已经知道,#include指令可以使另外一个文件被编译。就像它实际出现于#include指令的地方一样。
这种替换的方式很简单:预处理器先删除这条指令,并用包含文件的内容替换。这样一个源文件被包含10次,那就实际被编译10次。

头文件被包含的方式:

本地文件包含
#include “filename”
查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件。如果找不到就提示编译错误。
linux环境的标准头文件的路径:
/usr/include
VS环境的标准头文件路径:
c:\Program Files (x86)\Microsft Visual Studio 9.0\VC\include
注意按照自己的安装路径去找
库文件包含:

#include <filename>

查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。

在这里插入图片描述
在这里插入图片描述
推荐《高质量C/C++编程指南》中附录的考试试卷&《C语言深度解剖》

其他预处理指令

#error
#error 是一个预处理器指令,用于在编译时生成一个错误。如果使用了 #error 指令,编译时会停止并显示指定的错误消息。通常,这个指令用于在编译时检查一些条件,如果条件不满足就停止编译过程。例如,以下的代码使用 #error 指令,如果在编译时使用了 Visual Studio 2015,就会显示错误消息:

#if _MSC_VER == 1900
    #error "Visual Studio 2015 is not supported."
#endif

在上面的代码中,_MSC_VER 是一个预定义的宏,它表示正在使用的编译器的版本号。如果这个版本号是 1900,也就是 Visual Studio 2015 的版本号,就会生成一个错误,停止编译过程。
#pragma
#pragma 是一个预处理器指令,通常用于向编译器传递一些指示信息。这些信息可以是编译器特定的,也可以是标准化的。#pragma 指令的语法格式如下:

#pragma 指示词

其中,指示词是一个标识符或字符串,用于指定特定的指示操作。#pragma 指令通常用于控制编译器的行为,如开启或关闭某些特性、控制代码生成优化、指定编译器警告等。例如,以下是使用 #pragma 指令控制编译器最优化级别和关闭警告的示例:

#pragma optimize(3)
#pragma warning(disable: 4996, disable: 4101)

在上面的代码中,#pragma optimize(3) 指令告诉编译器使用最高优化级别来生成代码,而 #pragma warning(disable: 4996, disable: 4101) 则告诉编译器关闭警告编号为 4996 和 4101 的警告信息。请注意,具体的 #pragma 指令和其语法格式可能因编译器而异。
#line

#line 是一个预处理器指令,用于设置当前源文件的行号和文件名。这个指令可以用于生成源代码映射文件(source map),也可以用于编写代码生成工具。#line 指令的语法格式如下:

#line 行号 "文件名"

其中,行号和文件名是可选的参数,如果省略这些参数,则会重置行号和文件名为默认值。例如,以下是使用 #line 指令设置源文件的行号和文件名的示例:

#line 42 "example.cpp"
cout << "Hello, World!" << endl;

在上面的代码中,#line 42 "example.cpp" 指令将行号设置为 42,文件名设置为 example.cpp。因此,任何编辑工具或调试器都会将这行代码映射到 example.cpp 的第 42 行。

问:C语言inline

ChatAI答:
在 C 语言中,inline 是一种关键字,用于告知编译器对于函数的调用是否应该被内联。内联函数可以实现函数的调用处替换为函数本身的代码,这一过程可以减少程序运行时函数调用的开销,从而提高程序的运行速度。

使用 inline 声明的函数并不一定会被内联,具体取决于编译器的实现。编译器可能会根据该函数的复杂度、代码大小等因素来作出内联的决策。当然,我们也可以使用 -O3 等编译选项来告诉编译器尽可能地内联函数。

以下是一些使用 inline 的示例:

inline int add(int a, int b){
    return a + b;
}

在上述代码中,add 函数被声明为内联函数,以便在需要时可以被内联。

需要注意的是,在 C99 标准中,inline 的语义有所不同。在 C99 中,inline 更多地被认为是一种链接指令,它告诉编译器,这个函数可能在多个源文件中出现,但所有对该函数的调用都应该被替换为一个函数的实例,以便减少链接时的重复代码。

百度笔试题:
写一个宏,计算结构体中某变量相对于首地址的偏移,并给出说明
考察: offsetof 宏的实现

问:写一个宏offsetof,计算结构体中某变量相对于首地址的偏移,并给出说明
ChatAI答:
定义一个宏 offsetof 可以帮助我们以一种类型安全的方式计算结构体中某个变量的偏移量。由于 offsetof 的具体实现与操作系统和编译器的体系结构和实现有关,因此在此仅提供一种可能的实现方式,具体如下:

#define offsetof(st, m) \
    (size_t)(&((st *)0)->m)

其中,st 是指结构体类型,m 是在这个结构体中的成员变量。给定一个结构体的变量 obj 和成员变量 m,我们可以通过以下方式获取偏移量:

#include <stddef.h> // for offsetof macro

// Example structure
struct Example {
    int x;
    double y;
    char z[10];
};

int main() {
    struct Example obj;
    size_t offset = offsetof(struct Example, y);
    printf("Offset of y: %zu\n", offset);
    
    return 0;
}

输出:

Offset of y: 4

在上述示例中,我们计算了结构体 Example 中成员变量 y 相对于结构体首地址的偏移量。请注意,我们将指针类型强制转换为结构体类型 st 的指针类型,然后通过 & 符号获取成员变量 m 的地址,最后将其转换为 size_t 类型,以表示偏移量。此实现方法也可以用于计算结构体中嵌套结构体的偏移量。

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <stddef.h>

struct S
{
	char c1;
	int a;
	char c2;
};

#define OFFSETOF(struct_name,member_name) \
	(int)&(((struct_name*)0)->member_name)

int main()
{
	//printf("%d\n", offsetof(struct S, c1));
	//printf("%d\n", offsetof(struct S, a));
	//printf("%d\n", offsetof(struct S, c2));
	printf("%d\n", OFFSETOF(struct S, c1));
	printf("%d\n", OFFSETOF(struct S, a));
	printf("%d\n", OFFSETOF(struct S, c2));
	return 0;
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

安全认证:

1. 认证概述 为什么要有认证&#xff1f; 防止非法路由器接入企业内网的ospf路由器&#xff0c;保护内网安全 2. 认证方式 认证方式分为接口认证和区域认证&#xff0c;接口认证和区域认证没有本质的区别&#xff0c;接口认证是当区域内链路过多的情况下&#xff0c;接口认证…

Java网络开发(Tomcat)—— Servlet学习 Web相关背景知识 JavaWeb项目初步

本文目录 引出一、软件架构BS和CS二、实现Web服务的条件和步骤三、Tomcat搭建Web项目初步1.pom.xml文件配置2.web.xml文件更新3.Tomcat运行环境配置4.项目文件层级解析 四、JavaWeb项目文件分类&#xff08;1&#xff09;静态文件—存放位置&#xff08;2&#xff09;动态文件-…

【用人话讲算法】leetcode无重复字符的最长子串

【用人话讲算法】leetcode无重复字符的最长子串 文章目录 【用人话讲算法】leetcode无重复字符的最长子串题目简单思路&#xff08;暴力&#xff09;优化思考怎么写代码&#xff1f;怎么到下一个&#xff1f;whilefor 思路总结while和for循环总结 题目 题目的意思是说&#xff…

一台电脑上安装多个版本的python,运行互不干扰,显示位置的一些命令,

首先需要知道一些命令&#xff1a; pip show 包名 可以使用pip show 包名的方式显示位置 pip list pip方式显示的是当前环境下的库 os.environ.get&#xff08;&#xff09; python中os模块获取环境变量的一个方法 Python os.environ.get&#xff08;&#xff09;的用法 …

使用Git LFS上传Unity大型资源文件

在使用Unity制作结课作业时&#xff0c;使用到git工具进行版本控制与多人协作。在提交项目至远程仓库的过程中&#xff0c;git bash提示了以下报错&#xff1a; remote: warning: File Assets/Models/Z_India_5.29.fbx is 57.31 MB; this is larger than GitHubs recommended m…

利用环回口建立IBGP邻居

利用环回口建立IBGP邻居 BGP的稳定性 IBGP : 1.一般使用环回口建立IBGP邻居 2.指定建立IBGP邻居的源地址为lookback地址 EBGP : 1.一般使用直连接口建立EBGP邻居关系 2.如果想使用环回口建立EBGP邻居&#xff0c;需要将TTL值修改为大于1&#xff0c;默认TTL1 修改命令…

在win10/11的右键菜单添加电源选项

前言&#xff1a; 今天&#xff0c;博主从网上找到了在右键菜单中添加电源选项的方法&#xff0c;觉得挺实用的所以来教大家 方法&#xff1a; 下载&#xff08;懒人专用&#xff0c;直接打开文件即可&#xff09;&#xff1a; csdn中下载&#xff08;启用和关闭文件都有&a…

基于 Docker_redis6.0.8 实现 Redis 集群扩缩容

文章目录 单机部署数据分区方案集群部署集群容错测试集群扩容测试集群缩容测试 LXL&#xff1a;这玩意太枯燥&#xff0c;看完需要耐心 ~~~ 单机部署 通过 dockerhub 查找 redis 镜像&#xff0c;选择 6.0.8 版本。创建挂载目录&#xff0c;并赋予权限。 mkdir -p /var/docker…

MicroPython应用基础-使用Thonny IDE

MicroPython应用基础-使用Thonny IDE 文章目录 MicroPython应用基础-使用Thonny IDE引言Thonny简介使用Thonny连接到MicroPython开发板使用Thonny的REPL窗口运行Python语句在Thonny中保存Python程序文件至MicroPython开发板中运行使用Thonny的注意要点 引言 在很长一段时间内&…

[论文分享]TimeMAE:解耦掩码自编码器时间序列的自监督表示

论文题目&#xff1a;TimeMAE: Self-Supervised Representations of Time Series with Decoupled Masked Autoencoders 论文地址&#xff1a;https://arxiv.org/abs/2303.00320 代码地址&#xff1a;https://github.com/Mingyue-Cheng/TimeMAE 1 摘要 利用自监督预训练增强基于…

Flume和Kafka的组合使用

一.安装Kafka 1.1下载安装包 通过百度网盘分享的文件&#xff1a;复制链接打开「百度网盘APP 即可获取」 链接&#xff1a;https://pan.baidu.com/s/1vC6Di3Pml6k1KMbnK0OE1Q?pwdhuan 提取码&#xff1a;huan 也可以访问官网&#xff0c;下载kafka2.4.0的安装文件 1.2解…

Hadoop3.1.3安装(单机、伪分布)

系列文章目录 Ubuntu常见基本问题 Hadoop3.1.3安装&#xff08;单机、伪分布&#xff09; Hadoop集群搭建 文章目录 系列文章目录一、环境1、创建hadoop用户 二、更新apt三、安装SSH、配置SSH无密码登陆四、安装Java环境五、安装 Hadoop3.1.3六、Hadoop单机配置(非分布式)七、…

chatgpt赋能python:Python为什么被吹得这么神?

Python为什么被吹得这么神&#xff1f; Python是一个开源、跨平台的高级编程语言&#xff0c;由Guido van Rossum于1989年在荷兰创造。Python在近几年因其方便易用、高效稳定和丰富的生态体系而备受欢迎。Python被广泛应用于数据科学、人工智能、机器学习、Web开发、自动化测试…

启动虚拟机并安装Linux系统

我们刚刚新建的虚拟机相当于一个裸机&#xff0c;还没有安装操作系统在里面&#xff0c;下面我们来看一下怎么进行Linux系统的安装。 在VMware Workstation工具的主界面选择虚拟机Spark01&#xff0c;单击鼠标右键在弹出的菜单中选择“设置”打开“虚拟机设置”对话框。如下图…

程序设计综合实习(C语言):学生成绩文件管理

一、目的 1&#xff0e;掌握文件指针的概念和运用 2&#xff0e;掌握文件的相关操作&#xff1a;打开、读、写、关闭 3&#xff0e;掌握文件的定位操作 二、实习环境 Visual Stdio 2022 三、实习内容与步骤 1&#xff0e;定义一个结构体数组&#xff0c;存放10个学生的学号&a…

ABB Drive Composer Pro 2.8.1 Crack

Drive Composer 是 ABB 通用架构驱动器的启动和维护工具。该工具用于查看和设置驱动器参数&#xff0c;以及监控和调整过程性能。 Drive Composer入门版提供了设置参数、基本监控、从 PC 对驱动器进行本地控制以及事件记录器处理等基本功能。 Drive Composer pro是成熟的调试和…

蓝桥杯ABC组 数论知识合集

Note Of Note 同余方程中是可以正常进行分数的约分和去分母的 e x g c d exgcd exgcd 在使用时要保证 a , b a,b a,b 都是非负数 a a a 与 b b b 互质不代表 a , b a,b a,b 都为质数&#xff08; 4 4 4 和 5 5 5 互质&#xff0c;但是 4 4 4 不是质数&#xff09;两个相邻…

第3章“程序的机器级表示”:控制

文章目录 3.6 控制3.6.1 条件码3.6.2 访问条件码3.6.3 跳转指令及其编码3.6.4 翻译条件分支3.6.5 循环do-while 循环while循环for循环 3.6.6 switch 语句 3.6 控制 截止目前&#xff0c;考虑了 访问数据和 操作数据 的方法。程序执行的另一个很重要的部分就是控制被执行操作的…

Maven高级5-私服

1. 简介 一台独立的服务器&#xff0c;用于解决团队内部的资源共享与资源同步问题&#xff08;模拟了中央服务器&#xff09;&#xff1b; https://help.sonatype.com/repomanager3/download 命令行启动服务器 在nexus.exe目录&#xff08;bin目录&#xff09;下启动cmd&#…

【认知提升思维篇】之 反刍思维--恶性思考的根源

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;普本…