文章目录
#define 的用法
#define为宏定义语句,通常用它来定义常量,以及用它来实现宏。它本身并不在编译过程进行,而是在这之前的 预处理过程 中已经完成了,但也因此难以发现潜在的错误以及其他代码维护问题。
#define命令是C语言中的一个宏定义命令,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。
该命令有两种格式:
- 一种是简单的宏定义
- 一种是带参数的宏定义
(1)简单的宏定义
#define <宏名> <字符串>
例:
#define PI 3.1415926
(2)带参数的宏定义
#define <宏名> (<参数表>) <宏体>
例:
#define A(x) x
一个标识符被宏定义后,该标识符便是一个宏名。这时,在程序中出现的是宏名,在该程序被编译前,先将宏名用被定义的字符串替换,这称为宏替换,替换后才进行编译,宏替换是简单的替换。
define中的三个特殊符号:#,##,#@
#define Conn(x,y) x##y
#define ToString(x) #x
#define ToChar(x) #@x
- x##y表示什么?表示x连接y,举例说:
int n = Conn(123,456); // 结果就是 n=123456;
char* str = Conn("asdf", "adf"); // 结果就是 str = "asdfadf";
- #x表示什么?表示给x加双引号,举例说:
char* str = ToString(123132); // 就成了str="123132";
标准用法只有以上两种!
- 再来看#@x,其实就是给x加上单引号,结果返回是一个const char。举例说:
char a = ToChar(1); // 结果就是 a='1';
#@x的存在,在不同编译器上,结果不同。反正gcc环境下不能运行通过,直接报错。(VS2017 好像能编译通过)
typedef 的用法
typdef常用来定义一个标识符以及关键字的别名,它是语言编译过程的一部分,但它并不实际分配内存空间。
例如,C 语言在 C99 之前并未提供布尔类型,但我们可以使用 typedef 关键字来定义一个简单的布尔类型,如下面的代码所示:
typedef int BOOL;
#define TRUE 1
#define FALSE 0
定义好之后,就可以像使用基本类型数据一样使用它了,如下面的代码所示:
BOOL bflag=TRUE;
在实际使用中,typedef 的应用主要有如下4种。
- 为基本数据类型定义新的类型名
系统默认的所有基本类型都可以利用 typedef 关键字来重新定义类型名,示例代码如下所示:
typedef unsigned int COUNT;
- 为自定义数据类型(结构体、共用体和枚举类型)定义简洁的类型名称
以结构体为例,下面我们定义一个名为 Point 的结构体:
struct Point
{
double x;
double y;
double z;
};
在调用这个结构体时,我们必须像下面的代码这样来调用这个结构体:
struct Point oPoint1={100,100,0};
struct Point oPoint2;
在这里,结构体 struct Point 为新的数据类型,在定义变量的时候均要向上面的调用方法一样有保留字 struct,而不能像 int 和 double 那样直接使用 Point 来定义变量。现在,我们利用 typedef 定义这个结构体,如下面的代码所示:
typedef struct tagPoint
{
double x;
double y;
double z;
} Point;
在上面的代码中,实际上完成了两个操作:
1、定义了一个新的结构类型,代码如下所示:
struct tagPoint
{
double x;
double y;
double z;
} ;
其中,struct 关键字和 tagPoint 一起构成了这个结构类型,无论是否存在 typedef 关键字,这个结构都存在。
2、使用 typedef 为这个新的结构起了一个别名,叫 Point,即:
typedef struct tagPoint Point
因此,现在你就可以像 int 和 double 那样直接使用 Point 定义变量,如下面的代码所示:
Point oPoint1={100,100,0};
Point oPoint2;
- 为数组定义简洁的类型名称
它的定义方法很简单,与为基本数据类型定义新的别名方法一样,示例代码如下所示:
typedef int INT_ARRAY_100[100];
INT_ARRAY_100 arr;
- 为指针定义简洁的名称
对于指针,我们同样可以使用下面的方式来定义一个新的别名:
typedef char* PCHAR;
PCHAR pa;
对于上面这种简单的变量声明,使用 typedef 来定义一个新的别名或许会感觉意义不大,但在比较复杂的变量声明中,typedef 的优势马上就体现出来了,如下面的示例代码所示:
int *(*a[5])(int,char*);
对于上面变量的声明,如果我们使用 typdef 来给它定义一个别名,这会非常有意义,如下面的代码所示:
// PFun是我们创建的一个类型别名
typedef int *(*PFun)(int,char*);
// 使用定义的新类型来声明对象,等价于int*(*a[5])(int,char*);
PFun a[5];
typedef 两大陷阱
陷阱一:
记住,typedef是定义了一种类型的新别名,不同于宏,它不是简单的字符串替换。比如:
先定义:
typedef char* PSTR;
然后:
int mystrcmp(const PSTR, const PSTR);
const PSTR
实际上相当于const char*
吗?不是的,它实际上相当于char* const
。
原因在于const给予了整个指针本身以常量性,也就是形成了常量指针char* const
。
简单来说,记住当const和typedef一起出现时,typedef不会是简单的字符串替换就行。
陷阱二:
typedef在语法上是一个存储类的关键字(如auto、extern、mutable、static、register等一样),虽然它并不真正影响对象的存储特性,如:
typedef static int INT2; //不可行
编译将失败,会提示“指定了一个以上的存储类”。
const 解析
const 与 define 的区别在下方!!!
C / C++:const 关键字的作用 <---- 点击进入
知识点习题(内含:const 与 #define 的对比)
test.c
文件中包括如下语句:
#define ptr1 int*
typdef int* ptr2;
ptr1 n1, n2;
ptr2 n3, n4;
正确答案:
- 前者展开后的结果是:
int* n1, n2;
,类似于int* n1, int n2;
- 后者展开后的结果是:
int* n3, n4;
,类似于int* n1, int* n2;
答案解析:
区别的实质是 #define 与 typedef的含义不同
- #define 只是简单的字符串替代。
- typedef则是类型的重新定义,其所定义的类型是一体的。
- 用宏定义求三个数中的最大值
#define MAX(a,b,c) (a>b ? (a>c?a:c) : (b>c?b:c))
- 用const定义一个常量和用define定义一个常量有什么区别?
正确答案:
- 编译器处理阶段不同
- 宏定义是一个“编译时”概念,在预处理阶段展开(在编译时把所有用到宏定义值的地方用宏定义常量替换),不能对宏定义进行调试,生命周期结束于编译时期
- const 常量是一个“运行时”概念,在程序运行使用,类似于一个只读行数据。(只读变量)
- 存储方式不同
- 宏定义是直接替换,不会分配内存,存储与程序的代码段中
- const常量需要进行内存分配
- 类型和安全检查不同
- 宏定义是字符替换,没有数据类型的区别,同时这种替换 没有类型安全检查,可能产生边际效应等错误(------>边际效应不懂就点)
- const常量是常量的声明,有类型区别,需要在编译阶段进行类型检查
- 定义域不同
void f1 ()
{
#define N 12
const int n 12;
}
void f2 ()
{
cout << N <<endl; // 正确,N已经定义过,不受定义域限制
cout << n <<endl; // 错误,n定义域只在f1函数中
}
- 是否可以做函数参数
- 宏定义不能作为参数传递给函数
- const常量可以在函数的参数列表中出现
- 定义后能否取消
- 宏定义可以通过 #undef 来使之前的宏定义失效
- const常量定义后将在定义域内永久有效
void f1()
{
#define N 12
const int n = 12;
#undef N //取消宏定义后,即使在f1函数中,N也无效了
#define N 21//取消后可以重新定义
}
C++ 语言可以用const 来定义常量,也可以用#define 来定义常量,但是前者比后者有更多的优点。
- 如何用宏定义得到一个字的高位和低位字节?