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系列了…

python 共轭转置_python矩阵运算,转置,逆运算,共轭矩阵实例

我就废话不多说了&#xff0c;大家还是直接看代码吧&#xff01;#先定义两个矩阵Xnp.array([[1,2104,5,1,45],[1,1416,3,2,40],[1,1534,3,2,30],[1,852,2,1,36]])ynp.array([45,40,30,36])#内积以后发现cnp.dot(X.T,X)carray([[ 4, 5906, 13, 6, 151],[ 5906, 9510932, 21074, …

python解析pcap包已text格式输出_python分析pcap包

前两天需要分析一个pcap包&#xff0c;写了一段python脚本&#xff0c;将每个包的基本信息(源/目的MAC、源/目的IP、源/目的端口)提取出来。在实现过程中为了省事用了dpkt开发包&#xff0c;不过只用了几个简单的函数&#xff0c;具体的信息提取部分都是自己实现的。值得注意的…

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;等…

python3.70_Python3 基本数据类型

Python3 基本数据类型Python 中的变量不需要声明。每个变量在使用前都必须赋值&#xff0c;变量赋值以后该变量才会被创建。在 Python 中&#xff0c;变量就是变量&#xff0c;它没有类型&#xff0c;我们所说的"类型"是变量所指的内存中对象的类型。等号()用来给变量…

jdbc mysql驱动_MySQL JDBC驱动程序如何处理准备好的语句

jdbc mysql驱动准备的语句类型 在研究《 高性能Java持久性》一书中的“ 语句缓存”一章时&#xff0c;我有机会比较了Oracle&#xff0c;SQL Server&#xff0c;PostgreSQL和MySQL处理预准备语句的方式。 感谢Jess Balint &#xff08;MySQL JDBC驱动程序撰稿人&#xff09;在…

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

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

使用Spring AOP和番石榴速率限制器的节气门方法

外部服务或API可能有使用限制&#xff0c;或者它们不能失败就无法处理大量请求。 这篇文章解释了如何创建一个基于Spring Framework的方面&#xff0c;该方面可以用来限制使用Guava速率限制器的任何建议方法调用。 以下实现需要Java 8&#xff0c;Spring AOP和Guava。 让我们从…

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" &…

python kotlin_在Python,Java和Kotlin中标记参数和重载

python kotlin在多种语言之间跳来跳去可以帮助您注意到不同语言的习惯用法和最佳做法之间的某些差异。 比较有趣的差异之一与一个函数执行多项操作有关。 Python 我们先来看一下Python。 Python实际上无法重载&#xff0c;因为用相同的名称定义一个新的函数/方法只会覆盖前一个…

python调用zabbixapi接口_python调用zabbix api获取主机信息

import requestsimport jsonheaders {Content-Type: application/json}class GetZabbix:def __init__(self):#用户信息self.username "****"self.password "****"self.url "http://192.168.0.*/zabbix/api_jsonrpc.php"self.token self.get…

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

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

python回归预测例子_案例实战 | 逻辑回归实现客户流失预测(附Python代码与源数据)...

关注一下~&#xff0c;更多商业数据分析案例等你来撩前言利用逻辑回归进行客户流失预警建模中涵盖了许多比较细的知识点&#xff0c;思维导图只展示了极小的一部分&#xff0c;相关知识点链接将穿插在文中。(源数据源代码空降文末获取)数据读入数据属性说明数据探索简洁版本&am…

latex中算法命令_比较Java 8中的命令式和功能性算法

latex中算法命令Mario Fusco受欢迎的推文令人印象深刻&#xff0c;显示了类似算法的命令性和功能性方法之间的主要区别实际上是&#xff1a; 势在必行–功能分离pic.twitter.com/G2cC6iBkDJ — Mario Fusco&#xff08;mariofusco&#xff09; 2015年3月1日 两种算法都做同样…

四大语言加强统治地位,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计算宝宝每次吃奶的时间间隔 -…

java8流分组 性能_Java性能教程– Java 8流有多快?

java8流分组 性能在此JAX Magazine的预览预览中&#xff0c;JAX伦敦发言人Angelika Langer为使用Java流的任何人回答了最重要的问题&#xff1a;它们真的更快吗&#xff1f; Java 8是JDK收集框架的主要补充&#xff0c;即流​​API。 与集合相似&#xff0c;流表示元素序列。 …

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

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