C语言-程序环境和预处理(2)--带副作用的宏参数,宏与函数的对比,#undef,条件编译,文件包含

这篇具有很好参考价值的文章主要介绍了C语言-程序环境和预处理(2)--带副作用的宏参数,宏与函数的对比,#undef,条件编译,文件包含。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

上一篇文章–《C语言-程序环境和预处理(1)》讲述了程序的翻译环境和执行环境,编译、连接,预定义符号,#define,#符号和##符号的相关知识。
链接: 《C语言-程序环境和预处理(1)》
本篇文章,讲述带副作用的宏参数,宏与函数的对比,#undef,条件编译,文件包含的相关知识。


1.带副作用的宏参数

我们来看一个代码:
最后输出的a,b,m分别是多少?

#define MAX(x,y) ((x)>(y)?(x):(y))
int main()
{
	int a = 3;
	int b = 5;
	int m = MAX(a++, b++);
	printf("%d\n", m);
	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}

你第一次做的时候是否和我一样,最后输出的m是5,a是4,b是6。如果是这样的,那么恭喜你和我一样,做错了。

正确的解析如下:

	int m = ((a++) > (b++) ? (a++) : (b++));
	          3    >  5    
	        a=4     b=6     no        6
	  m=6                             b=7

正确答案应该是a=4,b=7,m=6.

其运算步骤应该是这个样子的,不是一开始我们想当然的那样,这就是带有副作用的宏参数。

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

2.宏与函数的对比

宏和函数运行时的步骤对比如下:
C语言-程序环境和预处理(2)--带副作用的宏参数,宏与函数的对比,#undef,条件编译,文件包含,C语言知识点,c语言,算法,开发语言

函数的缺点:

  1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。
  2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之宏可以适用于整形、长整型、浮点型等可以用于比较的类型。

宏的缺点:

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

除此之外,宏可以完成一件函数永远做不到的事情:
我们如果某些时候想给某个东西传类型,那么函数显然无法做到,比如:
开辟空间:

int*p = (int*)malloc(10 * sizeof(int));
//我们想要便捷的写malloc(10,int),这样函数显然做不到,但是宏可以做到

 #define MALLOC(num, type)   (type*)malloc(num * sizeof(type))
int*p = MALLOC(10, int);//这样就可以了

提示:宏是不能递归的!!

2.1 宏的命名约定

一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。

那我们平时的一个习惯是:
把宏名全部大写
函数名不要全部大写

2.2 命令行定义

许多C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。

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

比如在Linux,gcc编译器中就可以这样使用。
这个功能很少见,不常使用,只需知道即可!

3.#undef宏讲解

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

#define	M 100

int main()
{
	int m = M;
	printf("m = %d\n", m);

#undef M
	int m = M;//错误,宏M已被删除不可使用
#define M 1000
	//删除后还可以重新定义M
	return 0;
}

4.条件编译

在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。

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

常见的条件编译有如下:

4.1 #if #endif

int main()
{
#if 1==2
	printf("hehe\n");
#endif
	return 0;
}

C语言-程序环境和预处理(2)--带副作用的宏参数,宏与函数的对比,#undef,条件编译,文件包含,C语言知识点,c语言,算法,开发语言

4.2 多个分支的条件编译

int main()
{
#if 1==1
	printf("我是帅哥\n");
#elif 2==1
	printf("我是帅人\n");
#elif 3==1
	printf("我太帅了\n");
#else
	printf("帅不可挡\n");
#endif
	return 0;
}

C语言-程序环境和预处理(2)--带副作用的宏参数,宏与函数的对比,#undef,条件编译,文件包含,C语言知识点,c语言,算法,开发语言

4.3 判断是否被定义

int main()
{
#if defined(M)
	printf("snan");
#endif
	return 0;
}

C语言-程序环境和预处理(2)--带副作用的宏参数,宏与函数的对比,#undef,条件编译,文件包含,C语言知识点,c语言,算法,开发语言
其还有一种写法:

int main()
{
//#if defined(M)
//	printf("snan");
//#endif

#ifdef M
	printf("snan");
#endif

	return 0;

}

当然还有#if !defined(M)表示如果没有定义就怎么怎么样
#ifndef M便是第二种写法的如果未定义怎么怎么样

4.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

就是#if套#if,就是稍微复杂,一条一条理清楚是不难的。

5.文件包含

5.1 头文件的包含

头文件的包含有两种形式:
1.包含本地文件(自己的.h文件)
#include “xxx.h”

查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件。如果找不到就提示编译错误。

2.包含标准库的头文件
#include <stdio.h>

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

提示:#include “stdio.h”这样写也是没问题的,但是会拉低代码的运行速率,降低查找头文件的效率!!

5.1 嵌套文件的包含

在工作中往往避免不了这样一种情况:
C语言-程序环境和预处理(2)--带副作用的宏参数,宏与函数的对比,#undef,条件编译,文件包含,C语言知识点,c语言,算法,开发语言
comm.h和comm.c是公共模块。
test1.h和test1.c使用了公共模块。
test2.h和test2.c使用了公共模块。
test.h和test.c使用了test1模块和test2模块。
这样最终程序中就会出现两份comm.h的内容。这样就造成了文件内容的重复。

如何避免这样的问题,提高编译器的工作效率呢?
每个头文件的开头写

#ifndef __TEST_H__
#define __TEST_H__
//这部分是头文件的内容
#endif //__TEST_H__

TEST_H是根据头文件的名字来的(不是只能写TEST_H)

或者:

#pragma once

就可以避免头文件的重复引入。我们在VS上创建一个头文件时,它会自动输入这句话。

6.其他预处理指令

1.#error

编译程序时,只要遇到#error就会生成一个编译错误的提示消息,并停止编译

2.#pragma

可以设定编译程序完成一些特定的动作(可以通过编译程序的菜单设定,也可以直接写在源代码中),它允许向编译程序传送各种指令。例如:编译程序可能有一种选择,它支持对程序执行的跟踪,可用#pragma语句指定一个跟踪选项

3.#line

可以改变当前行数和文件名称,他们时在编译程序中预先定义的标识符命令的基本形式
#line number[“filename”]

4.#pragma pack()

C语言预处理指令#pragma pack()用于控制结构体、联合体和类成员的对齐方式。在C语言中,编译器通常会根据特定的对齐规则将结构体、联合体和类成员对齐到特定的边界上,以提高内存访问效率。#pragmapack()指令通过设置对齐边界值,控制编译器对结构体、联合体和类成员的对齐方式。该指令的参数是一个非负整数,默认情况下通常是4或8,表示对齐边界值为4字节或8字节。具体来说,当设置对齐边界值为n时,编译器会将结构体、联合体和类成员按照n字节对齐,即每个成员的起始地址必须是n的倍数。这可以防止因为内存对齐不当而导致的性能下降或错误,尤其在与硬件交互或与其他系统进行通信时很重要。

当然还有更多的预处理指令,大家可以自己学习。文章来源地址https://www.toymoban.com/news/detail-716059.html

到了这里,关于C语言-程序环境和预处理(2)--带副作用的宏参数,宏与函数的对比,#undef,条件编译,文件包含的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C语言--程序环境和预处理(宏)

    目录 前言 本章重点:  1. 程序的翻译环境和执行环境 2. 详解编译+链接 2.1 翻译环境​编辑 2.2 编译本身也分为几个阶段 2.3 运行环境 3. 预处理详解 3.1 预定义符号 3.2 #define 3.2.1 #define 定义标识符 3.2.2 #define 定义宏 2.2.3 #define 替换规则 3.2.4 #和##  3.2.5 带副作用的宏参数 3.2.6 宏

    2024年02月07日
    浏览(37)
  • 【C语言进阶】程序环境和预处理

    🔥 博客主页: 小王又困了 📚 系列专栏: C语言 🌟 人之为学,不日近则日退  ❤️ 感谢大家点赞👍收藏⭐评论✍️ 目录 一、程序的翻译环境和执行环境 二、详解编译和链接 2.1翻译环境 2.2编译的过程  2.3运行环境 三、预处理详解  3.1预定义符号  3.2#define 3.2.1 #define 定

    2024年02月15日
    浏览(37)
  • 程序环境和预处理(含C语言程序的编译+链接)--2

    文章前言: 上章我们把      程序的翻译环境     程序的执行环境   C语言程序的编译+链接     预定义符号介绍    预处理指令   #define    宏和函数的对比     预处理操作符    #和##的介绍   的相关知识进行了梳理讲解,接下来被把剩余知识    命令定义     预处

    2024年02月14日
    浏览(45)
  • 程序环境和预处理(详解)

    🍕博客主页:️自信不孤单 🍬文章专栏:C语言 🍚代码仓库:破浪晓梦 🍭欢迎关注:欢迎大家点赞收藏+关注 本文重点 代码编译链接变成可执行程序程序的过程 掌握学习各种预处理知识 在ANSI C的任何一种实现中,存在两个不同的环境: 翻译环境,在这个环境中源代码被

    2023年04月27日
    浏览(33)
  • 【C】程序环境和预处理

    在ANSI C的任何一种实现中,存在两个不同的环境。 第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。 第2种是执行环境,它用于实际执行代码。 翻译环境我们主要解释一下编译和链接过程。 我们的源文件也就是我们的.c文件通过编译器生成一个目标文件(

    2024年02月16日
    浏览(32)
  • 程序环境和预处理(上)——“C”

    各位CSDN的uu们你们好呀,今天小雅兰的内容是C语言中的程序环境和预处理这个知识点,这块知识点是小雅兰地C语言的最后一块知识点了,以后可能会更新一些C语言的书籍的阅读,比如:《C  Primer  Plus》和《C语言深度剖析》。好啦,让我们进入程序环境和预处理的世界吧。

    2023年04月09日
    浏览(38)
  • 程序环境和预处理(下)——“C”

    各位CSDN的uu们你们好呀,今天小雅兰的内容是程序环境和预处理的下篇知识点,那么,这篇博客写完后,C语言的知识点就到这里就结束啦,后续会专注于刷题和读书,也是关于C语言的,会写一些数据结构和C++的内容,好啦,让我们进入程序环境和预处理的世界吧 预处理详解

    2023年04月16日
    浏览(34)
  • 程序员进阶之路:程序环境和预处理

      目录   前言 程序的翻译环境和执行环境 翻译环境 运行环境 预处理(预编译) 预定义符号 #define #define 定义标识符 #define 定义宏  #define 替换规则  #和##  #的作用 ##的作用  带副作用的宏参数  宏和函数对比 命名约定  #undef 命令行定义 条件编译  文件包含  嵌套文件包

    2024年02月16日
    浏览(37)
  • 015+limou+C语言深入知识——(7)编译环境和运行环境以及预处理指令

    在这个环境中,源代码被转化为可执行的机器指令(二进制指令) 单文件简易版本 多文件简易版本 编译链接详细版本 VS2022集成IDE(windows下)的编译器叫cl.exe,链接器叫link.exe gcc编译器(windows下)的几个有关编译环境的命令 (1)符号表会把全局变量和具有外部链接的函数

    2023年04月11日
    浏览(32)
  • 【C语言深入】深入理解程序的预处理过程

    我们平时所写的每一个.c文件都会经过编译和连接的过程之后才会形成一个可执行程序: 今天我们就来详细的看看编译和连接这两个过程的具体细节。 程序的翻译环境与执行环境 在ANSI C的任何一种实现中,存在两个不同的环境。 第1种是翻译环境,在这个环境中源代码被转换

    2023年04月08日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包