关于#define、typedef、using(C++11)

一、#define

在题主的例子的这种用法里, 它就是个文本替换工具, 预处理器完成的, 无脑替换, 跟word里的replace一模一样, 不关编译器的事. 只需要注意下面几点:

1.宏定义

#是宏,处理的时候位于编译前阶段,宏处理器基本上对你的C/C++程序不会有任何的感知。它只处理宏的语法。而编译阶段的“程序”得到的是宏处理完的结果。#define 没有参加编译,在预处理的时候就被替换掉了。

当替换列表中含有多个字符的时候,最好的将替换列表用圆括号括起来。也可以定义一些简单的函数,但因为只是简单的替换,有时候会发生一些错误,谨慎使用,在替换列表中的各个参数最好使用圆括号括起来。

#define maxnum  1000
#define add(a,b) (a+b) //此处要打括号,不然执行A*add(a,b)*C 会变成 A*a+b*C

3.条件编译

通过#define  (windows、debug),可以选择需要编译的代码,这在跨平台或者调试的时候有很好的帮助。

#ifdef  windows
...
#else
...
#endif
#ifdef debug
...
...
#endif

4、文件包含

在引入头文件的过程中,由于头文件往往会互相包含,因此容易出现重复包含定义的问题。

#ifndef _HEADFILE_
#define _HEADFILE_
...
...
#endif

需要注意的是:宏定义 是字符串的置换替代,当参数是表达式或带有负号时会引入2义性,所以要多用括号。

#include "stdio.h"
#define SQR(x) x*x

int main()
{
	int a, k =3;
	a = SQR(k + 1);      //k+1*k+1=2k+1
        a = ++SQR(k + 1);    //++k+1*k+1=4+1*4+1
	printf("%d\n", a);
}

 

二、typedef

typedef是编译阶段的一部分,参加编译和链接。它的意义是单一的。

Bjarne Stroustrup在其著作的《the design and evolution of c++》中有一个关于typedef的小例子

typedef int P();
typedef int Q();
class X {
    static P(Q); // 将Q定义为P,等价于static int Q(), Q周围的括号是多余的
                
                 // Q不再是一个类型而变成了一个函数

    static Q(P); // 将Q定义为函数,采用P类型的论证方式
                 // 返回值为int,等价于static int Q(int ())
};

解释将在后面第一点提出。

这是一个极好的例子, 先问一下 typedef int P()到底做了什么? 其实是:

declares a function type P as returning an int and taking no arguments.                                                                                  将函数类型P声明为int型返回,并且不带参数。

 我们再来看下书中对其的定义

Typedef does not work like typedef [type] [new name]. The [new name] part does not always come at the end.   Typedef不像 typedef [type] [new name]那样工作。 [new name]部分并不总是在最后。

You should look at it this way: if [some declaration] declares a variable, typedef [same declaration] would define a type.  你应该这样看:如果[some declaration]声明一个变量,typedef [same declaration]会定义一个类型。

看我标黑的这句话, 总结一下就是: 任何声明变量的语句前面加上typedef之后,原来是变量的都变成一种类型不管这个声明中的标识符号出现在中间还是最后

 

然后,我们需要了解需要理解『模板』和『类型』之间的不同。仔细体会这句话:模板是用来产生类型的。在传统 C++中,typedef 可以为类型定义一个新的名称,但是却没有办法为模板定义一个新的名称。因为,模板不是类型。例如:

template< typename T, typename U, int value>
class SuckType {
public:
    T a;
    U b;
    SuckType():a(value),b(value){}
};
template< typename U>
typedef SuckType<std::vector<int>, U, 1> NewType; // 不合法

而C++11 使用 using 引入了下面这种形式的写法,并且同时支持对传统 typedef 相同的功效,我们下面再说。

回到typedef,我们先简单看个例子理解下它的好处:

 

更多小知识:

1.typedef 定义的新类型, 使用时可以省略括号。 

typedef int NUM;
NUM a = 10; // 也可写成`NUM(a) = 10;`
NUM(b) = 12; // 也可写成`NUM b = 12;`

现在回过头看: 

typedef int P();
static P(Q); 

P是一个新定义的function类型, 它返回值为int, 无参数; P(Q)实际上等价于P Q, 声明Q是一个返回值为int, 无参数的函数.

 

2.使用

typedef int x; // 定义了一个名为x的int类型
typedef int *p; //定义了一个名为p的指针类型, 它指向int
typedef struct{char c;} s; // 定义名为s的struct结构体类型
typedef class{private: int x; public: int y;} MyClass;  //类类型


//下面这些的标识符不一定放在最后
typedef int A[];  // 定义一个名为A的ints数组的类型
typedef int f(); // 定义一个名为f, 参数为空, 返回值为int的函数类型
typedef int g(int); // 定义一个名为g, 含一个int参数, 返回值为int行的函数类型

这玩意有什么用呢?
我们都知道C++语言里, 函数都是先声明后使用的(除非在使用之前定义), 看以下例子:

#include <iostream>
#include <stdio.h>
#include <string>

typedef int P(); // 简单的
typedef void Q(int *p, const std::string& s1, const std::string& s2, size_t size, bool is_true); // 复杂的

class X {
public:
	P(eat_shit); // 等价于声明`int eat_shit();`
	Q(bullshit); // 等价于声明`void bullshit(int *p, const string& s1, const string& s2, size_t size, bool is_true);`
};

int main() {
	X *xx;
	std::cout <<"shit ret:"<< xx->eat_shit() << std::endl;
	int a[] = { 1, 3, 4, 5, 7 };
	xx->bullshit(a, "foo", "bar", sizeof(a) / sizeof(int), true);

	system("pause");
	return 0;
}

int X::eat_shit() {
	return 888;
}

void X::bullshit(int *p, const std::string& s1, const std::string& s2, size_t size, bool is_true) {
	std::cout << "s1: " << s1 << ", s2: " << s2 << ", size: " << size << std::endl;
	std::cout << "elems:" << std::endl;
	for (int i = 0; i < size; i++) {
		std::cout << *p++, (i == size - 1) ? "" : ",";
	}
	std::cout << std::endl;
}

 

三、using 

另外,在 C++11 标准中,引入了新的为类型取别名的关键字 using,可以用来代替 typedef, 而且更好理解:

typedef int (*process)(void *);  // 定义了一个返回类型为 int,参数为 void* 的函数指针类型,名字叫做 process
using process = int(*)(void *); // 同上, 更加直观

template <typename T>
using NewType = SuckType<int, T, 1>;    // 合法
using iArr = int [6];
using pNode = Node *;
using pfunc = int (*) (int a, int b);

从可读性上 using 好于 typedef 。此外 typedef 和 using 不是完全等价的,using 可以用来 给模板取别买,typdef 则不行。

template <typename T>
using Vec = MyVector<T, MyAlloc<T>>;

// usage
Vec<int> ivec;

用起来非常自然。若是使用 typedef,则是这样:

template <typename T>
typedef MyVector<T, MyAlloc<T>> Vec;

// usage
Vec<int> vec;

编译的时候,会得到类似 error: a typedef cannot be a template 的错误信息。

总结起来,你只要弄懂了复杂声明,typedef 就很好理解!

此外,在 C++11 中推荐使用 using 代替 typedef

 

 


参考链接:GNU Documents

https://whu-pzhang.github.io/define-and-typedef/

https://www.zhihu.com/question/29798061/answer/144423125 作者:平头哥半导体

<think>好的,用户让我讲解一下C/C++中的typedef#define的用法和特点。我得先理清楚这两个概念的基本定义,然后比较它们的区别,再举一些例子帮助理解。 首先,typedef是用于给类型起别名的,这个我记得没错。比如用来简化复杂的类型声明,比如结构体或者函数指针。而#define是预处理指令,用来做宏定义的,不仅仅是类型,还可以定义常量、代码片段等等。不过用户可能更关心它们在类型定义上的区别。 接下来,我需要详细说明两者的用法。typedef的语法是typedef 原类型 新类型名;而#define则是#define 新名称 原内容。这里要注意作用域的不同,typedef受限于作用域,比如在函数内部定义的typedef只能在函数内部使用,而#define在定义之后直到文件末尾都有效,除非用#undef取消。这点很重要,可能会影响代码的可维护性。 然后,类型检查方面,typedef是在编译阶段处理的,编译器会检查类型是否正确,而#define是简单的文本替换,没有类型检查,容易出错。例如,用#define定义的指针类型可能会有问题,比如#define PINT int*,然后用PINT a, b; 实际上b会是int类型,而不是指针,而typedef就不会有这个问题。 另外,在处理复杂类型时,typedef的优势更明显,比如定义函数指针或者结构体。例如typedef void (*FuncPtr)(int); 这样用起来更清晰,而用宏的话会比较麻烦,容易出错。 还要提到调试方面的差异,typedef在编译时会有实际的类型信息,而宏因为预处理阶段就被替换了,所以调试时看到的还是替换后的内容,可能不利于排查问题。 用户可能想知道什么时候该用typedef,什么时候用#define。比如需要类型安全、简化复杂类型时用typedef,而需要灵活替换或者定义常量、宏函数时用#define。同时,现代C++中可能更推荐使用using来代替typedef,但用户的问题主要是C/C++,所以可能需要提到C++中的using,但重点还是typedef#define的区别。 还要注意,用户可能在实际编码中遇到一些常见的错误,比如错误地使用#define导致的问题,或者typedef的正确用法。例子需要简单明了,比如指针的例子,或者结构体的例子,这样用户更容易理解。 最后,总结两者的区别,强调它们各自的适用场景,帮助用户根据需求选择合适的方法。同时要提醒用户避免滥用#define定义类型,以免引入潜在的错误。</think>在C/C++中,`typedef`和`#define`都用于创建别名,但实现方式和特性有本质区别。以下是详细对比: --- ### 一、typedef 的用法和特点 #### 1. 基本语法 ```c typedef 原类型 新类型名; ``` 示例: ```c typedef int Integer; // 给int类型起别名 typedef struct { int x; } Point; // 简化结构体声明 ``` #### 2. 核心特点 - **类型安全**:由编译器处理,会进行类型检查 - **作用域规则**:遵循变量作用域(如函数内定义的`typedef`仅在函数内有效) - **支持复杂类型**: ```c typedef int (*FuncPtr)(int, int); // 函数指针类型 typedef int Matrix[3][3]; // 多维数组类型 ``` #### 3. 典型应用场景 - 简化复杂类型(如函数指针、嵌套结构体) - 提高代码可移植性(如`typedef int32_t`适配不同平台) --- ### 二、#define 的用法和特点 #### 1. 基本语法 ```c #define 新名称 原内容 ``` 示例: ```c #define PI 3.14159 // 定义常量 #define MAX(a,b) ((a)>(b)?(a):(b)) // 定义宏函数 ``` #### 2. 核心特点 - **文本替换**:由预处理器处理,直接替换代码 - **无作用域限制**:从定义处到文件末尾有效(可用`#undef`取消) - **潜在风险**: ```c #define PINT int* // 错误用法示例 PINT p1, p2; // 实际展开为 int* p1, p2(p2是int类型) ``` #### 3. 典型应用场景 - 定义常量、宏函数 - 条件编译配合`#ifdef`等指令 - 代码片段复用 --- ### 三、关键对比表格 | 特性 | typedef | #define | |---------------------|----------------------------------|--------------------------| | **处理阶段** | 编译阶段 | 预处理阶段 | | **类型检查** | ✔️ 有类型安全检查 | ❌ 纯文本替换 | | **作用域** | 遵循变量作用域规则 | 全局(除非用`#undef`) | | **指针定义安全性** | ✔️ `typedef int* PINT; PINT a,b` | ❌ `#define PINT int*` 有歧义 | | **调试信息** | 保留类型信息 | 替换后丢失原始标识符 | | **支持复杂类型** | ✔️ 天然支持 | ❌ 需要嵌套括号保证优先级 | --- ### 四、示例对比分析 #### 1. 指针定义问题 ```c // typedef 安全 typedef int* PINT_T; PINT_T p1, p2; // p1和p2都是int指针 // #define 危险 #define PINT_D int* PINT_D p3, p4; // 实际为 int* p3, p4(p4是int类型) ``` #### 2. 复杂类型适配 ```c // typedef 清晰定义函数指针 typedef void (*Callback)(int status); Callback func1; // 直接声明 // #define 需要显式写出类型 #define CALLBACK_D void (*)(int) CALLBACK_D func2; // 实际为 void (*func2)(int) ``` --- ### 五、使用建议 1. **优先使用typedef**:需要类型别名或复杂类型时 2. **限制#define**:仅在定义常量、宏函数或条件编译时使用 3. **C++补充**:C++11中可用`using`替代`typedef`(更直观): ```cpp using Vector = std::vector<int>; // 比typedef更清晰 ``` 通过合理选择二者,既能提高代码可读性,又能避免因文本替换导致的隐蔽错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值