https://blog.csdn.net/u012611878/article/details/52534622

 
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012611878/article/details/52534622

c++中define用法

define在c++语言中用法比较多,这里对其进行整理。

1.无参宏定义

无参宏的宏名后不带参数。 
其定义的一般形式为:

#define  标识符  字符串
  • 1

其中的“#”表示这是一条预处理命令。凡是以“#”开头的均为预处理命令。“define”为宏定义命令。“标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。 
例如:

#define MAXNUM 99999
  • 1

这样MAXNUM就被简单的定义为99999。

2.有参宏定义

C++语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。 
对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。 
带参宏定义的一般形式为:

 #define  宏名(形参表)  字符串
  • 1

在字符串中含有各个形参。在使用时调用带参宏调用的一般形式为:宏名(实参表); 
例如:

#define add(x, y) (x + y)
int main()
{
cout << "1 plus 1 is " << add(1, 1.5) << ".\n";
//输出“1 plus 1 is 2.5.”
system("pause");
return(0);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这个“函数”定义了加法,但是该“函数”没有类型检查,有点类似模板,但没有模板安全,可以看做一个简单的模板。

注意:该“函数”定义为(a + b),在这里加括号的原因是,宏定义只是在预处理阶段做了简单的替换,如果单纯的替换为a + b时,当你使用5 * add(2, 3)时,被替换为5 * 2 + 3,值为13,而非5 * (2 + 3),值为25。

3.宏定义中的特殊操作符 
define 中的特殊操作符有#,##和… and __VA_ARGS__ 
(1)# 
假如希望在字符串中包含宏参数,ANSI C允许这样作,在类函数宏的替换部分,#符号用作一个预处理运算符,它可以把语言符号转化程字符串。例如,如果x是一个宏参量,那么#x可以把参数名转化成相应的字符串。该过程称为字符串化。

例如:

#incldue <stdio.h>
#define PSQR(x) printf("the square of" #x "is %d.\n",(x)*(x))
int main(void)
{
int y =4;
PSQR(y);
//输出:the square of y is 16.
PSQR(2+4);
//输出:the square of 2+4 is 36.
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

(2)##

##运算符可以用于类函数宏的替换部分。另外,##还可以用于类对象宏的替换部分。这个运算符把两个语言符号组合成单个语言符号。 
例如:

#include <stdio.h>
#define XNAME(n) x##n
#define PXN(n) printf("x"#n" = %d\n",x##n)
int main(void)
{
int XNAME(1)=12;//int x1=12;
PXN(1);//printf("x1 = %d\n", x1);
//输出:x1=12
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

(3)可变参数宏 …和__VA_ARGS__

__VA_ARGS__ 是一个可变参数的宏,很少人知道这个宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持)。 
实现思想就是宏定义中参数列表的最后一个参数为省略号(也就是三个点)。这样预定义宏__VA_ARGS__就可以被用在替换部分中,替换省略号所代表的字符串。

例如:

#define PR(...) printf(__VA_ARGS__)
int main()
{
int wt=1,sp=2;
PR("hello\n");
//输出:hello
PR("weight = %d, shipping = %d",wt,sp);
//输出:weight = 1, shipping = 2
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

省略号只能代替最后面的宏参数。 
#define W(x,…,y)错误! 
但是支持#define W(x, …),此时传入的参数个数必须能够匹配。

这里再介绍几个系统的宏:

1) __VA_ARGS__ 是一个可变参数的宏,很少人知道这个宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持)。宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的”,”去掉的作用,否则会编译出错, 你可以试试。 
2) __FILE__ 宏在预编译时会替换成当前的源文件名 
3) __LINE__宏在预编译时会替换成当前的行号 
4) __FUNCTION__宏在预编译时会替换成当前的函数名称

4.宏定义中的多行定义

非常经典。

#define MACRO(arg1, arg2) do { /
/* declarations */ /
stmt1; /
stmt2; /
/* ... */ /
} while(0) /* (no trailing ; ) */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

记得要在每一个换行的时候加上一个”/”

5.宏定义中的条件编译

在大规模的开发过程中,特别是跨平台和系统的软件里,define最重要的功能是条件编译。

#ifdef WINDOWS
......
(#else)
......
#endif
#ifdef LINUX
......
(#else)
......
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

可以在编译的时候通过#define设置编译环境。

6.如何取消宏

//定义宏
#define [MacroName] [MacroValue]
//取消宏
#undef [MacroName]
  • 1
  • 2
  • 3
  • 4
  • 5

7.防止重复包含头文件 
由于头文件包含可以嵌套,那么C文件就有可能包含多次同一个头文件,就可能出现重复定义的问题的。 
通过条件编译开关来避免重复包含(重复定义) 
例如:

#ifndef __headerfileXXX__
#define __headerfileXXX__

文件内容

#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

小结及说明

1) 宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。 
2) 宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起置换。 
3) 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用#undef命令。只要函数定义在#undefine之后,则函数无法使用#define的内容。 
4) 宏名在源程序中若用引号括起来,则预处理程序不对其作宏代换。 
5) 宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由预处理程序层层代换。 
例如:

 #define PI 3.1415926
#define S PI*y*y /* PI是已定义的宏名*/
  • 1
  • 2

6) 习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母。 
7) 可用宏定义表示数据类型,使书写方便。 
应注意用宏定义表示数据类型和用typedef定义数据说明符的区别。 
宏定义只是简单的字符串代换,是在预处理完成的,而typedef是在编译时处理的,它不是作简单的代换,而是对类型说明符重新命名。被命名的标识符具有类型定义说明的功能。 
请看下面的例子:

#define PIN1 int *
typedef (int *) PIN2;
  • 1
  • 2

从形式上看这两者相似, 但在实际使用中却不相同。 
下面用PIN1,PIN2说明变量时就可以看出它们的区别: 
PIN1 a,b;在宏代换后变成: 
int *a,b; 
表示a是指向整型的指针变量,而b是整型变量。 
然而: 
PIN2 a,b; 
表示a,b都是指向整型的指针变量。因为PIN2是一个类型说明符。由这个例子可见,宏定义虽然也可表示数据类型, 但毕竟是作字符代换。在使用时要分外小心,以避出错。 
8) 对“输出格式”作宏定义,可以减少书写麻烦。 
例如:

#define P printf
#define D "%d\n"
#define F "%f\n"
main(){
int a=5, c=8, e=11;
float b=3.8, d=9.7, f=21.08;
P(D F,a,b);
P(D F,c,d);
P(D F,e,f);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

9)带参宏定义中,宏名和形参表之间不能有空格出现。 
10)在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义。而宏调用中的实参有具体的值。要用它们去代换形参,因此必须作类型说明。这是与函数中的情况不同的。在函数中,形参和实参是两个不同的量,各有自己的作用域,调用时要把实参值赋予形参,进行“值传递”。而在带参宏中,只是符号代换,不存在值传递的问题。 
11)在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。

c/c++中define用法详解及代码示例的更多相关文章

  1. 【转】 #define用法详解

    #define用法详解   1.#define 的作用 在C或C++语言源程序中允许用一个标识符来表示一个字符串,称为“宏”.被定义为“宏”的标识符称为“宏名”.在编译预处理时,对程序中所有出现的“宏 ...

  2. AngularJS select中ngOptions用法详解

    AngularJS select中ngOptions用法详解   一.用法 ngOption针对不同类型的数据源有不同的用法,主要体现在数组和对象上. 数组: label for value in a ...

  3. C++中的STL中map用法详解(转)

    原文地址: https://www.cnblogs.com/fnlingnzb-learner/p/5833051.html C++中的STL中map用法详解   Map是STL的一个关联容器,它提供 ...

  4. C#的String.Split 分割字符串用法详解的代码

    代码期间,把代码过程经常用的内容做个珍藏,下边代码是关于C#的String.Split 分割字符串用法详解的代码,应该对码农们有些用途. 1) public string[] Split(params ...

  5. laravel 框架配置404等异常页面的方法详解(代码示例)

    本篇文章给大家带来的内容是关于laravel 框架配置404等异常页面的方法详解(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 在Laravel中所有的异常都由Handl ...

  6. UIWebView用法详解及代码分享

    今天我们来详细UIWebView用法.UIWebView是iOS内置的浏览器控件,可以浏览网页.打开文档等 能够加载html/htm.pdf.docx.txt等格式的文件. 用UIWebView我们就 ...

  7. C++中const用法详解

    本文主要内容来自CSDN论坛: http://bbs.csdn.net/topics/310007610 我做了下面几点补充. 补充: 1. 用const声明全局变量时, 该变量仅在本文件内可见, 类 ...

  8. Spring中@Async用法详解及简单实例

    Spring中@Async用法 引言: 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的:但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类 ...

  9. AngularJS中transclude用法详解

    这篇文章主要介绍了AngularJS中transclude用法,详细分析了transclude的具体功能.使用技巧与相关注意事项,需要的朋友可以参考下 本文实例讲述了AngularJS中transcl ...

随机推荐

  1. dcos的问题汇总

    . group 'docker' does not exist 需要手工创建docker组,这一步本来应该是在安装docker的时候来完成的,但是采用yum install的方式不行,需要添加一个do ...

  2. 块级&行内元素总结

    一.块级元素与行内元素的区别 块级元素与行内元素有几个关键区别: 格式 默认情况下: 块级元素会新起一行: 行内元素不会以新行开始. 内容模型 一般块级元素可以包含行内元素和其他块级元素.这种结构上的 ...

  3. Boost库之asio io_service以及run、run_one、poll、poll_one区别

    一.io_service的作用 io_servie 实现了一个任务队列,这里的任务就是void(void)的函数.Io_servie最常用的两个接口是post和run,post向任务队列中投递任务,r ...

  4. Redis的安装和配置文件

    实验环境:Centos6.8 Redis版本:3.0.6 下载Redis,并放到/usr/local/soft下: yum -y install gcc automake autoconf libto ...

  5. JavaScript代码存在形式

    <!-- 方式一 --> <script type"text/javascript" src="JS文件"></script> ...

  6. Reporting Services无法连接ORACLE,提示:System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或更高版本

    Reporting Services无法连接ORACLE,在服务器安装ORACLE 11客户端版本后仍然提示以下错误: System.Data.OracleClient 需要 Oracle 客户端软件 ...

  7. SpringSecurity04 利用JPA和SpringSecurity实现前后端分离的认证和授权

    1 环境搭建 1.1 环境说明 JDK:1.8 MAVEN:3.5 SpringBoot:2.0.4 SpringSecurity:5.0.7 IDEA:2017.02旗舰版 1.2 环境搭建 创建一 ...

  8. Flask01 初识flask、创建flask应用、flask启动配置

    1 什么是flask Flask是一个使用 Python 编写的轻量级 Web 应用框架.其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 . 百度百科:点击前往 中文文档: ...

  9. Struts2学习第三课 Struts2详解

    接着上次的课程 这次我们看struts.xml 修改如下:这里是加上命名空间,默认的是不加,我们手动加上时就要在访问时加上命名空间. <?xml version="1.0" ...

  10. Java引用的四种状态

    强引用 用的最广,我们平时写代码时,new一个Object存放在堆内存,然后用一个引用指向它.它就是强引用. 如果一个对象具有强引用,那么垃圾回收期绝不会回收它,当内存空间不足,java虚拟机宁愿抛出 ...