C++ #define和typedef的用法及区别

一、define和typedef的区别

1、typedef关键字:typedef是用来声明类型别名的。

typedef

typedef type-declaration synonym;

The typedef keyword defines a synonym for the specified type-declaration. The identifier in the type-declaration becomes another name for the type, instead of naming an instance of the type. You cannot use the typedef specifier inside a function definition.

 

A typedef declaration introduces a name that, within its scope, becomes a synonym for the type given by the decl-specifiers portion of the declaration. In contrast to the class, struct, union, and enum declarations, typedef declarations do not introduce new types — they introduce new names for existing types.

typedef关键字定义指定类型声明的同义词。类型声明中的标识符将成为该类型的另一个名称,而不是命名该类型的实例。不能在函数定义中使用typedef说明符。
typedef声明引入了一个名称,该名称在其作用域内成为声明的类型的同义词(直白点就是给已有的类型取别名,并具有相同的作用域)。与class、struct、union和enum声明不同,typedef声明不引入新类型,而是为现有类型引入新名称。

Example

// Example of the typedef keyword
typedef unsigned long ulong;

ulong ul;     // Equivalent to "unsigned long ul;"

typedef struct mystructtag
{
   int   i;
   float f;
   char  c;
} mystruct;

mystruct ms;   // Equivalent to "struct mystructtag ms;"

typedef int (*funcptr)();  // funcptr is synonym for "pointer
                           //    to function returning int"

funcptr table[10];   // Equivalent to "int (*table[10])();"

 

2、#define 可用来定义一个常量(包括无参常量和有参常量)。它本身并不在编译过程中执行,而是在预处理阶段就已经完成了,因此不作任何正确性检查,只进行不关含义的字符串替换。

The #define Directive

You can use the #define directive to give a meaningful name to a constant in your program. The two forms of the syntax are:

Syntax

#define identifier token-stringopt

#define identifier[( identifieropt, ... , identifieropt )] token-stringopt

The #define directive substitutes token-string for all subsequent occurrences of an identifier in the source file. The identifier is replaced only when it forms a token. (SeeC++ Tokens in the C++ Language Reference.) For instance, identifier is not replaced if it appears in a comment, within a string, or as part of a longer identifier.

A #define without a token-string removes occurrences of identifier from the source file. The identifier remains defined and can be tested using the #if defined and #ifdef directives.

The token-string argument consists of a series of tokens, such as keywords, constants, or complete statements. One or more white-space characters must separate token-string from identifier. This white space is not considered part of the substituted text, nor is any white space following the last token of the text.

Formal parameter names appear in token-string to mark the places where actual values are substituted. Each parameter name can appear more than once in token-string, and the names can appear in any order. The number of arguments in the call must match the number of parameters in the macro definition. Liberal use of parentheses ensures that complicated actual arguments are interpreted correctly.

The second syntax form allows the creation of function-like macros. This form accepts an optional list of parameters that must appear in parentheses. References to the identifier after the original definition replace each occurrence of identifier( identifieropt, ..., identifieropt ) with a version of the token-string argument that has actual arguments substituted for formal parameters.

The formal parameters in the list are separated by commas. Each name in the list must be unique, and the list must be enclosed in parentheses. No spaces can separate identifier and the opening parenthesis. Use line concatenation — place a backslash (\) before the newline character — for long directives on multiple source lines. The scope of a formal parameter name extends to the new line that ends token-string.

When a macro has been defined in the second syntax form, subsequent textual instances followed by an argument list constitute a macro call. The actual arguments following an instance of identifier in the source file are matched to the corresponding formal parameters in the macro definition. Each formal parameter in token-string that is not preceded by a stringizing (#), charizing (#@), or token-pasting (##) operator, or not followed by a ## operator, is replaced by the corresponding actual argument. Any macros in the actual argument are expanded before the directive replaces the formal parameter. (The operators are described in Preprocessor Operators.)

The following examples of macros with arguments illustrate the second form of the #define syntax:

// Macro to define cursor lines 
#define CURSOR(top, bottom) ((top) << 8) | bottom))

// Macro to get a random integer with a specified range 
#define getrandom(min, max) \
    ((rand()%(int)(((max) + 1)-(min)))+ (min))

Arguments with side effects sometimes cause macros to produce unexpected results. A given formal parameter may appear more than once in token-string. If that formal parameter is replaced by an expression with side effects, the expression, with its side effects, may be evaluated more than once. (See the examples under Token-Pasting Operator (##).)

The #undef directive causes an identifier’s preprocessor definition to be forgotten. See The #undef Directive for more information.

If the name of the macro being defined occurs in token-string (even as a result of another macro expansion), it is not expanded.

A second #define for a macro with the same name generates an error unless the second token sequence is identical to the first.

Microsoft Specific

Microsoft C/C++ allows the redefinition of a macro, but generates a warning, provided the new definition is lexically identical to a previous definition. ANSI C considers macro redefinition an error. For example, these macros are equivalent for C/C++ but generate warnings:

#define test( f1, f2 ) ( f1 * f2 )
#define test( a1, a2 ) ( a1 * a2 )

END Microsoft Specific

This example illustrates the #define directive:

#define WIDTH       80
#define LENGTH      ( WIDTH + 10 )

The first statement defines the identifier WIDTH as the integer constant 80 and defines LENGTH in terms of WIDTH and the integer constant 10. Each occurrence of LENGTH is replaced by (WIDTH + 10). In turn, each occurrence of WIDTH + 10 is replaced by the expression (80 + 10). The parentheses around WIDTH + 10 are important because they control the interpretation in statements such as the following:

var = LENGTH * 20;

After the preprocessing stage the statement becomes:

var = ( 80 + 10 ) * 20;

which evaluates to 1800. Without parentheses, the result is:

var = 80 + 10 * 20;

which evaluates to 280.

Microsoft Specific

Defining macros and constants with the /D compiler option has the same effect as using a #define preprocessing directive at the beginning of your file. Up to 30 macros can be defined with the /D option.

END Microsoft Specific

 

所以,从定义里面我们可以总结出以下几个区别:

 

Diff                    #define  typedef
执行时间不同define在预处理阶段在编译阶段
功能不同只作为常量字符串的替换为已经存在的类型取别名

 

二、define和typedef使用上的区别示例

结构决定用途,同样,define和typedef的定义决定了他们在使用上的区别,

(1)连续定义变量的区别:

#include <iostream>
using std::cout;
using std::endl;

#define PI (3.1415926)       // define 定义常量
#define Add(x,y) ((x)+(y))   // define 定义宏函数

#define PINT  int*           // define 定义数据类型(实质只是常量字符串的相等)
typedef int* pint;           // typedef为变量取别名

int main(int argc,char* argv[])
{
	int a = 10,b = 11,c = 12,d = 13;

	PINT pa = NULL ,pb = 0;   // pa 是一个指针变量,pb是一个int 变量。 在预处理阶段进行字符串替换,实际变成了 int *pa = NULL,pb = 0;
	pa = &a;
	pb = b;

	cout << "*pa :" << *pa << "   pb:" << b << endl;

	pint pc = NULL,pd = NULL; // pc 和 pd 都是指针变量

	pc = &c;
	pd = &d;

	cout << "*pc:" << *pc << "   *pd:" << *pd << endl;

	system("pause");

	return 0;
}

 

==》 特意把 预编译生成的 .i 文件拿出来进行比较,发现这一阶段确实进行了宏定义的替换

// ..... 忽略文件前N行

using std::cout;
using std::endl;

typedef int* pint;

int main(int argc,char* argv[])
{
	int a = 10,b = 11,c = 12,d = 13;

	int* pa = 0 ,pb = 0;   // 执行了宏定义的替换
	pa = &a;
	pb = b;

	cout << "*pa :" << *pa << "   pb:" << b << endl;

	pint pc = 0,pd = 0;   // typedef 实际在预编译阶段并没有处理

	pc = &c;
	pd = &d;

	cout << "*pc:" << *pc << "   *pd:" << *pd << endl;

	system("pause");

	return 0;
}

最后程序执行输出结果如下:

*pa :10   pb:11
*pc:12   *pd:13

 

vs中生成和查看 预处理 *.i文件的方法:

 

 

(2)在 cosnt 常量的操作上区别:

#include <iostream>
using std::cout;
using std::endl;

#define PINT  int*
typedef int* pint;

int main(int argc,char* argv[])
{
	int a = 10,b = 11,c = 12,d = 13;

	const PINT pa = NULL; // pa为常量指针,const 用来修饰pa所指向的内存空间,只读;但是pa可修改,可存放其他内存地址
	pa = &a;
	cout << "*pa :" << *pa  << endl;

	PINT const pb = &b;   // pb为指针常量,const 用来修饰指针pb,pb不可再指向其他内存空间;
	// pb = &a; // error

	cout << "*pb :" << *pb  << endl;

	const pint pc = &c;   // 指针常量。不可使pc再指向其他的内容,但可以修改pc当前指向内存空间
	// pc = &d; // 会报错

	cout << "*pc:" << *pc  << endl;

	pint const pd = &d; //  指针常量
	// pd = &c; // error:表达式必须是可修改的左值
	*pd = 22;
	cout << "*pd:" << *pd  << endl;

	system("pause");

	return 0;
}

执行结果:

*pa :10
*pb :11
*pc:12
*pd:22

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

键盘会跳舞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值