C语言 #define 和 typedef 区别

点击蓝字

d889684f52b1130b8f2733fd78d548ad.png

关注我们

来源于网络,侵删

在C语言编程中,typedef 和 #define是最常用语句,可能很多工作过几年的工程师都没有去深究过它们的一些用法和区别。

typedef的用法

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

typedef  int    INT;
typedef  (int*)   pINT;
typedef unsigned int  uint32_t

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的出现使它也渐渐成为了起别名的工具。

有时很容易搞不清楚 #define 与 typedef 两者到底该用哪个好,如#define INT int这样的语句,用typedef一样可以完成,用哪个好呢?

我主张用typedef,因为在早期的许多C编译器中这条语句是非法的,只是现今的编译器又做了扩充。为了尽可能地兼容,一般都遵循#define定义“可读”的常量以及一些宏语句的任务,而typedef则常用来定义关键字、冗长的类型的别名。

d542bd085ce4bbd5d97ccf0d32547ee5.png

宏定义只是简单的字符串代换(原地扩展),而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;        //同时声明了两个指向字符变量的指针

虽然下面(代码)方式也可行,但相对来说没有用typedef的形式直观,尤其在需要大量指针的地方,typedef的方式更省事。

char   *pa,   *pb;

用途二:

用在旧的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是定义了一种类型的新别名,不同于宏,它不是简单的字符串替换。比如先定义:

typedef   char*   PSTR;

然后:

int   mystrcmp(const   PSTR,   const   PSTR);

const   PSTR实际上相当于const   char*吗?

不是的,它实际上相当于char*   const。

原因在于const给予了整个指针本身以常量性,也就是形成了常量指针char*   const。

简单来说,记住当const和typedef一起出现时,typedef不会是简单的字符串替换就行。

陷阱二:

typedef在语法上是一个存储类的关键字(如auto、extern、static、register等一样),虽然它并不真正影响对象的存储特性,如:

typedef   static   int   INT2;   //不可行

编译将失败,会提示“指定了一个以上的存储类”。

2d86c4d3d292498aa220b7639edb6a9b.gif

如果你年满18周岁以上,又觉得学【C语言】太难?想尝试其他编程语言,那么我推荐你学Python,现有价值499元Python零基础课程限时免费领取,限10个名额!
▲扫描二维码-免费领取

0ba9ed44deecc33a6858ac0fa824fb09.gif

戳“阅读原文”我们一起进步

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/337237.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Batch –用JavaConfig替换XML作业配置

最近&#xff0c;我协助一个客户启动并运行了Spring Batch实现。 该团队决定继续使用针对批处理作业的基于JavaConfig的配置&#xff0c;而不是传统的基于XML的配置。 随着这越来越成为配置Java应用程序的一种常用方法&#xff0c;我觉得是时候更新Keyhole的Spring Batch系列了…

sql limit 子句_Java 8流中的常见SQL子句及其等效项

sql limit 子句功能编程允许使用通用语言进行准声明性编程 。 通过使用功能强大的流畅API&#xff08;例如Java 8的Stream API &#xff09;或jOOλ的顺序Stream扩展Seq或更复杂的库&#xff08;例如javaslang或functionaljava&#xff09; &#xff0c;我们可以以一种非常简洁…

C++编程中的核心知识点!

点击蓝字关注我们来源于网络&#xff0c;侵删尊重函数接口&#xff0c;尽量不作内部改动C代码语句分为&#xff1a;内置类型&#xff0c;名字&#xff0c;变量&#xff0c;操作符&#xff0c;标量&#xff0c;字符串&#xff0c;预处理指示&#xff08;如#include&#xff09;等…

C++ 语言的单元测试与代码覆盖率

点击蓝字关注我们来源于网络&#xff0c;侵删前言测试是软件开发过程中一个必须的环节&#xff0c;测试确保软件的质量符合预期。对于工程师自己来说&#xff0c;单元测试也是提升自信心的一种方式。直接交付没有经过测试的代码是不太好的&#xff0c;因为这很可能会浪费整个团…

C++ 模板(Template)总结,长点小知识

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删前言&#xff1a;大家好&#xff0c;今天给大家分享一篇关于 C 模板总结概述.模板&#xff08;Template&#xff09;指 C 程序设计设计语言中采用…

归并排序 java_归并排序(Java实现)

package primary_01;/** 归并排序* 1.算法实现* 2.验证算法的正确性* 3.分析算法的复杂度*/public class Merge_sort {public static void main(String[] args) {int arr [] {2,1,6,5,9,8,2020,199};sort(arr, 0, arr.length-1);for (int i : arr) {System.out.print(i" &…

C++17 常用新特性:带初始化的 if 和 switch 语句

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删早在2016-03-14&#xff0c;Thomas Kppe 在https://wg21.link/p0305r0中就提出了在if和switch使用初始化语句&#xff0c;使用时的代码示例如下&…

四大语言加强统治地位,Rust威胁C/C++

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删TIOBE 公布了 2022 年 10 月的编程语言排行榜。截至今日&#xff0c;Python、Java、C 和 C 语言已经在 TIOBE 榜单的前 4 位盘踞了相当长的一段时…

sql计算留存_SQL无所不能:DBA宝妈宝爸系列分享

原文链接&#xff1a;https://www.modb.pro/db/22315 目前程序从功能上其实已经完全满足客户(当然我这里的客户都是指媳妇儿)需求&#xff0c;具体可参考&#xff1a;使用SQL计算宝宝每次吃奶的时间间隔 - Part1&#xff1a;分析函数的使用使用SQL计算宝宝每次吃奶的时间间隔 -…

对 int 变量赋值的操作是原子的吗?为什么?

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删这个是在面试的时候遇到的问题&#xff0c;当时没有答出来。回到家以后查了查&#xff0c;整理记录下来。原问题&#xff1a;什么指令集支持原子…

C语言知识总结:if-else判断语句和switch-case选择语句

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删1、if-else判断语句一个基本的if语句由一个关键字if开头&#xff0c;跟上在括号&#xff08;&#xff09;里边的是表示逻辑条件的表达式&#xf…

hibernate性能_改善Hibernate应用程序性能的7种方法

hibernate性能Hibernate是将Java Web应用程序与SQL数据库集成的好工具&#xff0c;但是当应用程序的响应时间开始显着增加时&#xff0c;我们应该怎么做&#xff1f; 当我们怀疑应用程序是否会随着客户群的增长而扩展时&#xff0c;我们该怎么办&#xff1f; 在花大价钱托管或…

为什么永远不会有语言取代C/C++?

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删数以百计的 C/C 替代品将会出现&#xff0c;但 C/C 将永远与我们同在&#xff01;每个 CPU 都带有一种称为 ISA&#xff08;指令集架构&#xff…

sql 非主键每年自增_或许你不知道的 10 条 SQL 技巧

上文我们简述了 SQL 的一些进阶技巧&#xff0c;一些朋友觉得不过瘾&#xff0c;我们继续来下篇&#xff0c;再送你 10 个技巧一、 使用延迟查询优化 limit [offset], [rows]经常出现类似以下的 SQL 语句:SELECT * FROM film LIMIT 100000, 10offset 特别大!这是我司出现很多慢…

C语言最全入门笔记

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删c语言入门C语言一经出现就以其功能丰富、表达能力强、灵活方便、应用面广等特点迅速在全世界普及和推广。C语言不但执行效率高而且可移植性好&am…

junit测试spring_使用Spring JUnit规则进行参数化集成测试

junit测试springSpring 4.2附带了全新的JUnit规则&#xff1a; SpringClassRule和SpringMethodRule 。 使用JUnit规则的主要优点是让开发人员摆脱SpringJUnit4ClassRunner并在Spring集成测试中利用不同的JUnit运行器。 我认为Spring JUnit Rules的最大机会是易于创建参数化的集…

无处不在_Java无处不在:使用DukeScript在任何地方运行一次编写

无处不在在相当长一段时间内&#xff0c;Java都未能兑现“一次编写&#xff0c;随处运行”的承诺。 DukeScript希望通过在跨平台应用程序中实现视图和逻辑的清晰分离来改变这种状况。 在本文中&#xff0c;一个简单的场景用于介绍DukeScript的基础。 多年以来&#xff0c;Java…

C语言为何不会过时

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删评价任何一门编程语言&#xff0c;都是招人骂的。永远是这样。就像是春寒料峭的季节&#xff0c; 街上穿棉袄和穿单衣的擦肩而过&#xff0c;双方…

教你如何用一句话激怒IT人

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删在IT行业摸爬滚打这么久&#xff0c;你是否听过下面这些话&#xff1a;“这个很简单&#xff0c;我也是做程序出身的。”“预算砍掉一半&#xf…

java的网络编程有用吗_十大有用但又偏执的Java编程技术

java的网络编程有用吗经过一段时间的编码&#xff08;以我为例&#xff0c;大约20年左右&#xff0c;当您玩得开心时光飞逝&#xff09;&#xff0c;人们开始接受这些习惯。 因为&#xff0c;你知道... 任何可能出错的事情都会发生。 这就是为什么人们会采用“防御性编程”的原…