#define详解

这篇具有很好参考价值的文章主要介绍了#define详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. #define定义标识符

语法:

#define name stuff

举个例子:

#define MAX 1000    //为1000创建一个名字MAX
#define reg register          
//为 register这个关键字,创建一个简短的名字reg
#define CASE break;case        
//在写case语句的时候自动把 break写上。
// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
date:%s\ttime:%s\n" ,\
__FILE__,__LINE__ ,       \
__DATE__,__TIME__ )   

在define定义标识符的时候,不要在最后加上 ; ,否则容易导致问题

#define详解
此处出现了语法错误。


2. #define定义宏

#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。

下面是宏的申明方式:

#define name( parament-list ) stuff
其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中。

注意:
参数列表的左括号必须与name紧邻。
如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。

如:

#define SQUARE( x ) x * x
//SQUARE是宏名,x是参数,x*x 是替换的内容

这个宏接收一个参数 x .
如果在上述声明之后,你把

SQUARE( 5 );

置于程序中,预处理器就会用5*5这个表达式替换上面的表达式:

观察下面的代码段:

int a = 5;
printf("%d\n" ,SQUARE( a + 1) );

乍一看,你可能觉得这段代码将打印36这个值。事实上,它将打印11.

SQUARE( a + 1) 替换为 a+1*a+1, 即 5 + 1 * 5 + 1 = 11

由替换产生的表达式并没有按照预想的次序进行求值。先乘法再宏定义的加法,乘法运算先于宏定义的加法
宏是先替换再计算,不是先计算再替换。

这个问题,的解决办法是在宏定义表达式加上三对括号就可以了。

#define SQUARE( x)   ( ( x ) + ( x ) )

提示:

所有用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中 的操作符或邻近操作符之间不可预料的相互作用


3. #define 替换规则

在程序中扩展#define定义符号和宏时,需要涉及几个步骤。

  1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
  2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
  3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上 述处理过程。

注意:

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

4. #和##

#的作用
char* p = "hello ""bit\n";
printf("hello"," bit\n");
printf("%s", p);     //hell0 bit

这里只有当字符串作为宏参数的时候才可以把字符串放在字符串中。
另外一个技巧是:
使用 # ,把一个宏参数变成对应的字符串。

比如:

#include<stdio.h>
#define PRINT(N, FORMAT) printf("the value of "#N" is "FORMAT"\n", N)
int main()
{
	float f = 3.14f;
	PRINT(f, "%lf");
	int b = 20;
	PRINT(b,"%d");

	return 0;
}

代码中的 #N 会预处理器处理为:“N”

#define详解


##的作用

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

#include<stdio.h>

#define CAT(Class, Num) Class##Num

int main()
{
	int Class106 = 100;
	printf("%d\n", CAT(Class, 106));
	//printf("%d\n", Class106);    //预处理后
	return 0;
}

#define详解

注:
这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。


5. 带副作用的宏参数

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

x+1;//不带副作用
x++;//带有副作用

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

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

#define MAX(x,y) ((x)>(y)?(x):(y))
int main()
{
	int a = 5;//6 7
	int b = 4;//5
	int m = MAX(a++, b++);
	//int m = ((a++) > (b++) ? (a++) : (b++));
	
	printf("m=%d ", m);//6
	printf("a=%d b=%d\n", a, b);//7 5
	return 0;
}
a和b后置加加,先用后加,
当(a++)和(b++)比较后,(a++)>(b++) , a变为6,b为5,m=(a++)=6,
a++又用了一次,此时a+1变为7.

6. 宏和函数对比

宏通常被应用于执行简单的运算。比如在两个数中找出较大的一个。

那为什么不用函数来完成这个任务?
原因有二:

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

宏的缺点:

当然和函数相比宏也有劣势的地方:文章来源地址https://www.toymoban.com/news/detail-403670.html

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

到了这里,关于#define详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 错误C2065:未声明的标识符 - 解决C++中的未声明标识符错误

    错误C2065:未声明的标识符 - 解决C++中的未声明标识符错误 在C++编程中,错误C2065是一种常见的编译错误,它表示使用了一个未声明的标识符。通常情况下,这个错误是由于忘记包含必要的头文件或者拼写错误导致的。本文将介绍如何解决这个错误,并提供相应的源代码示例

    2024年02月06日
    浏览(52)
  • 初识标识符

    abstract assert boolean break byte case catch char class const continue default do double else enum extends final finally float for goto if implementis import instanceof int interface long native new package private protected public return strictfp short static super switch synchronized this throw throws transient try void volatile while   java所有的组成

    2024年02月11日
    浏览(61)
  • go学习-指针 标识符

    1.指针 (1).基本介绍 1)基本数据类型,变量存的值,也叫值类型 2)获取变量的地址用,比如 var num int ,获取num的地址:num 3)指针类型,变量存的是一个地址,这个地址指向的空间存的才是真正值,比如: var ptr *int =num 4)获取指针类型所指的值,使用*,比如:var *ptr int,使用

    2024年02月11日
    浏览(52)
  • 第6关:Python的标识符

    2023年04月09日
    浏览(48)
  • C# 标识符命名规则和约定

    目录 命名规则 命名约定 C# 编码约定 命名约定 帕斯卡拼写法 驼峰式大小写 如何使用模式匹配以及 is 和 as 运算符安全地进行强制转换 标识符是分配给类型(类、接口、结构、记录、委托或枚举)、成员、变量或命名空间的名称。 有效标识符必须遵循以下规则: 标识符必须

    2024年02月12日
    浏览(53)
  • 电脑的唯一标识符,VC++如何读出?

    电脑有没有唯一标识符,查了一些资料,开始以为CPUID可以,但是实际上统一类型的CPU用的是相同的ID: 那么BIOS ID是否是唯一的呢?主板上 的BIOS 芯片是主板上 唯一 贴有标签的芯片,如果电脑开机时登录bios可以看到里面的一些基本信息: bios编号也可以用win cmd的命令获得,

    2024年02月12日
    浏览(58)
  • Android设备的各种唯一标识符 ID

    只有Android手机才有, IMEI号是一串15位的号码,比如像这样 359881030314356 需要权限 android.permission.READ_PHONE_STATE 通常用户会因为你向他们要了这个权限而给你一个差评,因为他们觉得你就是在窃取他们的隐私,很明显,你就是在收集一些数据 这个是不靠谱的,因为有时候它是

    2024年02月11日
    浏览(62)
  • ora-12154无法解析指定的连接标识符

    用户反映查询的时候报错ora-12154 这个系统只做历史数据查询使用,使用并不平凡,该数据库曾做过一次服务器间的迁移。 用户描述,所有oracle客户端查询该视图都报tns错误,一般ora-12154会发生在连接数据库时,因为tns配置不正确而报错,但是这个报错发生在进行查询过程中

    2024年01月23日
    浏览(55)
  • c# 此程序集中已使用了资源标识符

    严重性    代码    说明    项目    文件    行    禁止显示状态 错误    CS1508    此程序集中已使用了资源标识符“BMap.NET.WindowsForm.BMapControl.resources”    BMap.NET.WindowsForm    D:MySourceDecompileBMap.NET.WindowsFormCSC    1    活动   运行程序时,提示有错误:此程序

    2024年02月15日
    浏览(48)
  • Java 和 UUID:编写可靠的唯一标识符

    UUID(通用唯一识别码)是一种用于唯一标识分布式系统中的对象的标准。它是一个128位的字符串,包含了时间戳和随机数字,可以保证在分布式系统中每个对象都有一个独特的标识符,而不会发生冲突。 UUID最常见的用途是在不同的系统和平台之间传递唯一的标识符,以便于

    2024年02月04日
    浏览(71)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包