当前位置: 首页 > 编程日记 > 正文

C++中typedef和define的区别

typedef和#define的用法与区别

一、typedef的用法

在C/C++语言中,typedef常用来定义一个标识符及关键字的别名,它是语言编译过程的一部分,但它并不实际分配内存空间,实例像:

typedef    int       INT;
typedef    int       ARRAY[10];
typedef   (int*)   pINT;

typedef可以增强程序的可读性,以及标识符的灵活性,但它也有“非直观性”等缺点。

二、#define的用法

#define为一宏定义语句,通常用它来定义常量(包括无参量与带参量),以及用来实现那些“表面似和善、背后一长串”的宏,它本身并不在编

译过程中进行,而是在这之前(预处理过程)就已经完成了,但也因此难以发现潜在的错误及其它代码维护问题,它的实例像:

#define   INT             int
#define   TRUE         1
#define   Add(a,b)     ((a)+(b));
#define   Loop_10    for (int i=0; i<10; i++)

在Scott Meyer的Effective C++一书的条款1中有关于#define语句弊端的分析,以及好的替代方法,大家可参看。

三、typedef与#define的区别

从以上的概念便也能基本清楚,typedef只是为了增加可读性而为标识符另起的新名称(仅仅只是个别名),而#define原本在C中是为了定义常量

,到了C++,const、enum、inline的出现使它也渐渐成为了起别名的工具。有时很容易搞不清楚与typedef两者到底该用哪个好,如#define

INT int这样的语句,用typedef一样可以完成,用哪个好呢?我主张用typedef,因为在早期的许多C编译器中这条语句是非法的,只是现今的

编译器又做了扩充。为了尽可能地兼容,一般都遵循#define定义“可读”的常量以及一些宏语句的任务,而typedef则常用来定义关键字、冗

长的类型的别名。

宏定义只是简单的字符串代换(原地扩展),而typedef则不是原地扩展,它的新名字具有一定的封装性,以致于新命名的标识符具有更易定义变

量的功能。请看上面第一大点代码的第三行:

typedef    (int*)      pINT;
以及下面这行:
#define    pINT2    int*

效果相同?实则不同!实践中见差别:pINT a,b;的效果同int *a; int *b;表示定义了两个整型指针变量。而pINT2 a,b;的效果同int *a, b;

表示定义了一个整型指针变量a和整型变量b。

typedef的四个用途和两个陷阱

用途一:
定义一种类型的别名,而不只是简单的宏替换。可以用作同时声明指针型的多个对象。比如:
char*   pa,   pb;     //   这多数不符合我们的意图,它只声明了一个指向字符变量的指针,  
//   和一个字符变量;
以下则可行:
typedef   char*   PCHAR;     //   一般用大写
PCHAR   pa,   pb;                 //   可行,同时声明了两个指向字符变量的指针
虽然:
char   *pa,   *pb;
也可行,但相对来说没有用typedef的形式直观,尤其在需要大量指针的地方,typedef的方式更省事。

用途二:
用在旧的C代码中(具体多旧没有查),帮助struct。以前的代码中,声明struct新对象时,必须要带上struct,即形式为:   struct   结构名   对象名,如:
struct   tagPOINT1
{
        int   x;
        int   y;
};
struct   tagPOINT1   p1;  

而在C++中,则可以直接写:结构名   对象名,即:
tagPOINT1   p1;

估计某人觉得经常多写一个struct太麻烦了,于是就发明了:
typedef   struct   tagPOINT
{
        int   x;
        int   y;
}POINT;

POINT   p1;   //   这样就比原来的方式少写了一个struct,比较省事,尤其在大量使用的时候

或许,在C++中,typedef的这种用途二不是很大,但是理解了它,对掌握以前的旧代码还是有帮助的,毕竟我们在项目中有可能会遇到较早些年代遗留下来的代码。

用途三:
用typedef来定义与平台无关的类型。
比如定义一个叫   REAL   的浮点类型,在目标平台一上,让它表示最高精度的类型为:
typedef   long   double   REAL;  
在不支持   long   double   的平台二上,改为:
typedef   double   REAL;  
在连   double   都不支持的平台三上,改为:
typedef   float   REAL;  
也就是说,当跨平台时,只要改下   typedef   本身就行,不用对其他源码做任何修改。
标准库就广泛使用了这个技巧,比如size_t。
另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健(虽然用宏有时也可以完成以上的用途)。

用途四:
为复杂的声明定义一个新的简单的别名。方法是:在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版。举例:

1.   原声明:int   *(*a[5])(int,   char*);
变量名为a,直接用一个新别名pFun替换a就可以了:
typedef   int   *(*pFun)(int,   char*);  
原声明的最简化版:
pFun   a[5];  

2.   原声明:void   (*b[10])   (void   (*)());
变量名为b,先替换右边部分括号里的,pFunParam为别名一:
typedef   void   (*pFunParam)();
再替换左边的变量b,pFunx为别名二:
typedef   void   (*pFunx)(pFunParam);
原声明的最简化版:
pFunx   b[10];

3.   原声明:doube(*)()   (*e)[9];  
变量名为e,先替换左边部分,pFuny为别名一:
typedef   double(*pFuny)();
再替换右边的变量e,pFunParamy为别名二
typedef   pFuny   (*pFunParamy)[9];
原声明的最简化版:
pFunParamy   e;  

理解复杂声明可用的“右左法则”:从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完。举例:
int   (*func)(int   *p);
首先找到变量名func,外面有一对圆括号,而且左边是一个*号,这说明func是一个指针;然后跳出这个圆括号,先看右边,又遇到圆括号,这说明(*func)是一个函数,所以func是一个指向这类函数的指针,即函数指针,这类函数具有int*类型的形参,返回值类型是int。
int   (*func[5])(int   *);
func右边是一个[]运算符,说明func是具有5个元素的数组;func的左边有一个*,说明func的元素是指针(注意这里的*不是修饰func,而是修饰func[5]的,原因是[]运算符优先级比*高,func先跟[]结合)。跳出这个括号,看右边,又遇到圆括号,说明func数组的元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型为int。

也可以记住2个模式:
type   (*)(....)函数指针  
type   (*)[]数组指针  
---------------------------------

陷阱一:
记住,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;   //不可行
编译将失败,会提示“指定了一个以上的存储类”。

转载于:https://www.cnblogs.com/fengfengqingqingyangyang/p/3270432.html

相关文章:

SVN 钩子操作-同步更新web目录

一个简单的钩子演示&#xff1a;也可以网上搜索其他高级的 本次想要达到的功能是&#xff1a;每次用户commit 到仓库后&#xff0c;仓库的钩子会自动把程序又更新的www/的web发布目录 1.现在web目录下创建一个test.com网站目录 [rootwentao13 ~]# cd /var/www/html/ [rootwenta…

【组队学习】【32期】深入理解计算机系统

深入理解计算机系统 航路开辟者&#xff1a;李岳昆、易远哲领航员&#xff1a;初晓宇航海士&#xff1a;叶前坤、沈豪 基本信息 开源内容&#xff1a;https://github.com/datawhalechina/team-learning-program/tree/master/ComputerSystems内容属性&#xff1a;公测课程内容…

linux中怎么退出执行过程,(进程)处理过程中的Linux:从执行到退出

Linux是一个多任务操作系统&#xff0c;表面上看&#xff0c;同时运行许多任务——即进程。每一个进程都在系统中留下足迹。这里介绍一些检查这些足迹的工具&#xff0c;并且还要说明蔓延的/proc目录到底是什么。欢迎归来。上周我们考察Linux调度程序如何区分要运行的进程的优先…

git - 简易指南

http://www.bootcss.com/p/git-guide/转载于:https://www.cnblogs.com/passer1991/p/3279333.html

python爬虫 智联招聘 工作地点

需求&#xff1a;智联上找工作的时候&#xff0c;工作地点在搜索页面只能看到城市-区。看不到具体的地址。&#xff08;离家近的工作肯定优先考虑&#xff09;思路&#xff1a;爬取搜索页面&#xff08;页面一&#xff09;然后进去其中一个内页&#xff0c;再爬工作地点&#x…

2021-2022-1 线性代数知识点总结的视频

01 线性方程组02 矩阵及其运算03 向量空间&#xff08;上&#xff09;03 向量空间&#xff08;下&#xff09;04 特征值与特征向量05 实对称矩阵与二次型2021 线性代数 第三章 习题课2021 线性代数 第四章 习题课2021-2022-1 线性代数考试要点2021-2022-1 线性代数知识点总结 …

win使用linux共享打印机,Ubuntu 12.04以及 Window 下使用共享打印机

目前针对各个办公室里面都已经配置打印机了&#xff0c;为了节约资源&#xff0c;该打印机想必已经被网管设置成了共享&#xff0c;然而我们应该如何进行使用呢&#xff1f;笔者目前常用的系统是Ubuntu 12.04,偶尔想打印东西的不想切换到XP系统下&#xff0c;就此简单介绍下如何…

poj3280Cheapest Palindrome(记忆化)

链接 真的1A了。。 一开始想复杂了 想着补全再删 没想好 后来想到递归 大的回文串是由小的推过来的 一直递归下去 对于当前的i&#xff0c;j可以选择保留或者删除 选个最小的 1 #include <iostream>2 #include<cstring>3 #include<algorithm>4 #include<…

【组队学习】【32期】推荐系统-新闻推荐系统实践

推荐系统-新闻推荐系统实践 航路开辟者&#xff1a;罗如意领航员&#xff1a;肖桐航海士&#xff1a;汪志鸿、吴忠强、赖敏材、王辰玥、毛伟、宋禹成、陈雨龙、管柯琴 基本信息 开源内容&#xff1a;https://github.com/datawhalechina/fun-rec内容属性&#xff1a;公测课程…

Redis配置到本地以及可视化工具的安装运用

配置到本地出错&#xff1a; 解决方法&#xff1a; 1、先把原来的服务删除了 2、在重新配置服务。 真的&#xff0c;我就是这样的。。 转载于:https://www.cnblogs.com/stujike/p/8672165.html

linux消息通信无法接收,进程间通信:消息队列有关问题:进程1接收不到进程2的消息...

进程间通信&#xff1a;消息队列有关问题&#xff1a;进程1接收不到进程2的消息进程间通信&#xff1a;消息队列有关问题&#xff1a;进程1接收不到进程2的消息日期&#xff1a;2014-05-16  浏览次数&#xff1a;20365 次进程间通信&#xff1a;消息队列问题&#xff1a;进程1…

java听课笔记(四)

程序流程控制 分支 顺序 循环 if switch&case 1 2 3 调整 break 1.6 前 switch(byte、short、char、int) 1.7 可放String 循环 while&#xff08;次数不确定&#xff09; do while for&#xff08;确定次数&#xff09; break&#xff08;跳出本层循环&#xff09; continu…

【组队学习】【32期】scikit-learn教程

scikit-learn教程 航路开辟者&#xff1a;江季领航员&#xff1a;李牧轩航海士&#xff1a;武帅、陈宇 基本信息 开源内容&#xff1a;&#xff1a;https://github.com/datawhalechina/machine-learning-toy-code/tree/main/ml-with-sklearn内容属性&#xff1a;公测课程内容…

css一个很好用的hover显示

之前一直想在样式实现&#xff0c;hover时显示其他div&#xff0c;今天终于找到了&#xff0c;(*^▽^*).a悬停时显示.b.a:hover .b {display: block;}转载于:https://www.cnblogs.com/HCXiao/p/8677713.html

c语言输入函数有什么作用,c语言的输入函数有哪些

c语言的输入函数有&#xff1a;1、scanf()函数、从标准输入stdin读取格式化输入&#xff1b;2、getchar()函数&#xff0c;从标准输入stdin获取一个字符&#xff1b;3、gets()函数&#xff0c;从标准输入stdin读取一行&#xff1b;4、getch()函数。【相关学习推荐&#xff1a;C…

dubbo学习过程、使用经验分享及实现原理简单介绍

一、前言部门去年年中开始各种改造&#xff0c;第一步是模块服务化&#xff0c;这边初选dubbo试用在一些非重要模块上&#xff0c;慢慢引入到一些稍微重要的功能上&#xff0c;半年时间&#xff0c;学习过程及线上使用遇到的些问题在此总结下。整理这篇文章差不多花了两天半时间…

AutoIt脚本在做自动化操作的时候,如何进行错误捕获?

我的自动化脚本在运行的时候&#xff0c;会生成一个界面&#xff0c;点击该页面上的按钮能够进行自动化操作。 经常遇到的一个问题是&#xff1a; 脚本运行一半&#xff0c;GUI程序出现了异常情况&#xff0c;这个时候&#xff0c;再次点击生成的界面上的按钮&#xff0c;不会有…

中国电子学会青少年编程能力等级测试图形化四级编程题:计算生肖

「青少年编程竞赛交流群」已成立&#xff08;适合6至18周岁的青少年&#xff09;&#xff0c;公众号后台回复【Scratch】或【Python】&#xff0c;即可进入。如果加入了之前的社群不需要重复加入。 我们将有关编程题目的教学视频已经发布到抖音号21252972100&#xff0c;小马老…

用c语言写一个两线程程序,如何用C语言实现多线程

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼Windows操作系统&#xff0c;C语言实现多线程&#xff1a;#include #include DWORD APIENTRY ThreadOne ( LPVOID threadArg ){printf ( "线程开始啦&#xff0c;参数是&#xff1a;%s\n" , (char *)threadArg );return …

中国电子学会青少年编程能力等级测试图形化四级编程题:抽奖

「青少年编程竞赛交流群」已成立&#xff08;适合6至18周岁的青少年&#xff09;&#xff0c;公众号后台回复【Scratch】或【Python】&#xff0c;即可进入。如果加入了之前的社群不需要重复加入。 我们将有关编程题目的教学视频已经发布到抖音号21252972100&#xff0c;小马老…

github README.md教程

github README.md教程 总结 github中README.md通过特殊字符标记和缩进来达到格式控制&#xff0c;也可以用HTML标签来实现格式控制。 教程一&#xff1a; Markdown 的目标是实现「易读易写」&#xff0c;兼容HTML。 但是&#xff0c;在 HTML 区块标签间的 Markdown 格式语法将不…

this.options[selectedIndex]的使用

<select id"sel" οnchange"javascript:getSelect();"> <option value"a">选择</option> <option value"bdd">be</option> <option value"c">ce</option> <option value"…

c语言怎么创建一个h文件,求助C语言大佬 , 只会写到一个.c文件里 ,不会用.h头文件...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼简答题】实验任务&#xff1a;学生成绩统计从键盘输入一个班(全班最多不超过30人)学生某门课的成绩&#xff0c;当输入成绩为负值时&#xff0c;输入结束&#xff0c;分别实现下列功能&#xff1a;(1)统计不及格人数并打印不及格学…

Makefile所有内嵌函数

一、文本处理函数以下是GNU make内嵌的文本&#xff08;字符串&#xff09;处理函数。1 $(subst FROM,TO,TEXT)函数名称&#xff1a;字符串替换函数—subst。函数功能&#xff1a;把字串“TEXT”中的“FROM”字符替换为“TO”。返回值&#xff1a;替换后的新字符串。示例…

青少年编程竞赛交流群周报(第041周)

2021年12月12日&#xff08;周日&#xff09;晚20:00我们在青少年编程竞赛交流群开展了第四十一期直播活动。 一、直播内容 我们直播活动的主要内容如下&#xff1a; 讲解了上次测试中小朋友们做错的题目 Scratch青少年编程能力等级测试模拟题&#xff08;四级&#xff09;。…

docker的用法

Docker是开发人员和系统管理员构建&#xff0c;发布和运行分布式应用程序的开放平台&#xff0c;可以在笔记本电脑、数据中心、虚拟机还有云服务器上运行。 使用Docker工具来提高生产率的方法&#xff1a;本地依赖&#xff1a;你需要在本地系统上快速试用 magento 吗&#xff1…

android 背景切换动画效果代码,关于Android shape gradient背景渐变

百度后&#xff0c;发现渐变色不仅可以根据xml来实现&#xff0c;也可以用java代码来实现&#xff0c;由于目前没有那么多时间&#xff0c;只记录xml实现的方法&#xff1b;以后在记录Java实现的代码。通过Shape gradient标签来实现首先来看gradient标签所有的渐变属性&#xf…

Linux Ubuntu 自动登录

我一直在用Ubuntu发型版本的Linux系统&#xff0c;很喜欢把它做得更加的方便易用&#xff0c;特别是Ubuntu的Server版本&#xff0c;因为没有Desktop的 GUI界面&#xff0c;也没有自动登录设置&#xff0c;无法通过简单的点击鼠标方式来设置自动登录&#xff0c;在查阅了一些资…

Win10 下 RabbitMQ 的 安装 配置

记录下本人在win10环境下安装RabbitMQ的步骤&#xff0c;以作备忘。 第一步&#xff1a;下载并安装erlang 原因&#xff1a;RabbitMQ服务端代码是使用并发式语言Erlang编写的&#xff0c;安装Rabbit MQ的前提是安装Erlang。下载地址&#xff1a;http://www.erlang.org/download…

【组队学习】【32期】算法的应用

算法的应用 航路开辟者&#xff1a;肖然领航员&#xff1a;李铭哲航海士&#xff1a;伊雪、左凯文 基本信息 开源内容&#xff1a;https://github.com/datawhalechina/team-learning-program/tree/master/AlgorithmRunning内容属性&#xff1a;公测课程内容说明&#xff1a;…