`
阿尔萨斯
  • 浏览: 4363946 次
社区版块
存档分类
最新评论

Typedef与define解析

 
阅读更多

typedef常见用法

注:本文可能多次提得同一内容,这是我从别的地方整理的,希望能够谅解,不想看的直接跳过就行


1.常规变量类型定义例如:typedef unsigned char uchar描述:uchar等价于unsigned char类型定义 uchar c声明等于unsignedchar c声明

2.数组类型定义例如:typedef int array[2];注:可理解为typedef int[] array描述:array等价于 int[2]定义; array a声明等价于inta[2]声明

扩展:typedefint array[M][N];描述:array等价于 int[M][N]定义; array a声明等价于inta[M][N]声明

3.指针类型定义例如: typedef int *pointer;描述:pointer等价于 int*定义; pointer p声明等价于int*a声明

例如:typedef int *pointer[M];描述:pointer等价于 int*[M]定义; pointer p声明等价于int*a[M]声明明

4.函数地址说明描述:C把函数名字当做函数的首地址来对待,我们可以使用最简单的方法得到函数地址例如:函数:int func(void); unsigned long funcAddr=(unsigned long)func funcAddr的值是func函数的首地址

5.函数声明例如:typedef int func(void); (注:可理解为typedefint (void ) func func等价于 int(void)类型函数描述1 func f声明等价于 int f(void)声明,用于文件的函数声明描述2 func *pf声明等价于 int (*pf)(void)声明,用于函数指针的生命,见下一条6.函数指针例如:typedef int (*func)(void) 注:可理解为typedef int (void ) * func描述:func等价于int (*)(void)类型 func pf等价于int(*pf)(void)声明,pf是一个函数指针变量

7.识别typedef的方法:a).第一步。使用已知的类型定义替代typdef后面的名称,直到只剩下一个名字不识别为正确typedefu32(*func)(u8);从上面的定义中找到 typedef__u32u32;typedef __u8 u8继续找到 typedef unsigned int__u32;typedef unsigned char __u8;替代位置名称 typedef unsignedint(*func)(void);现在只有func属于未知b).第二步.未知名字为定义类型,类型为取出名称和typedef的所有部分,如上为func等价于unsignedunsignedint(*)(unsignedchar);c).第三部.定义一个变量时,变量类型等价于把变量替代未知名字的位置所得到的类型func f等价于unsigned unsignedint(*f)(unsigned char);

下面我们利用上面的方法分析一个稍微复杂点的函数声明:

int (*Instance (int (*pf)(const char *,const char *)))(const char *, const char *);

咋一看还真有点蒙,但是我们试着使用上面的方法整理一下这个声明:

typedef int(*Parameter)(constchar *, const char *);

Parameter等价于int(*)(const char *, const char *);

所以上面的声明等价于Parameter Instance(Parameter pf);

这样一抽象,我们理解起来就相对容易多了.

Typedefdefine的区别

1首先,二者执行时间不同

关键字typedef在编译阶段有效,由于是在编译阶段,因此typedef有类型检查的功能。

Define则是宏定义,发生在预处理阶段,也就是编译之前,它只进行简单而机械的字符串替换,而不进行任何检查。

#define用法例子:

#define f(x) x*xmain( ){ int a=6,b=2,c; c=f(a) / f(b); printf("%d\\n",c);}

程序的输出结果是: 36,根本原因就在于#define只是简单的字符串替换。

2功能不同

Typedef用来定义类型的别名,这些类型不只包含内部类型(int,char等),还包括自定义类型(如struct),可以起到使类型易于记忆的功能。

如: typedef int (*PF) (const char *, const char *);

定义一个指向函数的指针的数据类型PF,其中函数返回值为int,参数为const char *。

typedef 有另外一个重要的用途,那就是定义机器无关的类型,例如,你可以定义一个叫REAL 的浮点类型,在目标机器上它可以i获得最高的精度:typedef long double REAL;在不支持 long double 的机器上,该 typedef 看起来会是下面这样:typedef double REAL;并且,在连 double 都不支持的机器上,该 typedef 看起来会是这样:typedef float REAL;

#include<stdio.h>
typedef int arr[10];
void main()
{
 arr a;
 int i;
 for(i=0;i<10;i++)
 {
  a=i*i;
  printf("%d\n",a);
 }
}


#define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。

3作用域不同

#define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用。

而typedef有自己的作用域。

void fun()
{
#define A int
}

void gun()
{
//在这里也可以使用A,因为宏替换没有作用域,
//但如果上面用的是typedef,那这里就不能用A ,不过一般不在函数内使用typedef
}

4对指针的操作

二者修饰指针类型时,作用不同。

Typedef int * pint;

#define PINT int *

Const pint p//p不可更改,p指向的内容可以更改,相当于 int * const p;

Const PINT p//p可以更改,p指向的内容不能更改,相当于 const int *p;或 int const *p


pint s1, s2; //s1和s2都是int型指针
PINT s3, s4; //相当于int * s3,s4;只有一个是指针。

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 tagPOINT1p1;

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

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

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

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

用途三:

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

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

1. 原声明:int *(*a[5])(int,char*);可以写成int * (int,char *) * [] a;
变量名为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(constPSTR, const PSTR);

const PSTR实际上相当于constchar*吗?不是的,它实际上相当于char* const。
原因在于const给予了整个指针本身以常量性,也就是形成了常量指针char* const。
简单来说,记住当const和typedef一起出现时,typedef不会是简单的字符串替换就行。

陷阱二:
typedef在语法上是一个存储类的关键字(如autoexternmutablestaticregister等一样),虽然它并不真正影响对象的存储特性,如:
typedefstatic intINT2; //不可行
编译将失败,会提示“指定了一个以上的存储类”。

#define,const,typedef三者联系与区别

一. #define与const

联系:都可以用来定义常量

区别:

1. const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应)。

2. 前者在堆栈分配了空间,而后者只是把具体数值直接传递到目标变量罢了。或者说,const的常量是一个Run-Time的概念,他在程序中确确实实的存在并可以被调用、传递。而#define常量则是一个Compile-Time概念,它的生命周期止于编译期:在实际程序中他只是一个常数、一个命令中的参数,没有实际的存在。


3.const常量存在于程序的数据段,#define常量存在于程序的代码段。

4. 有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试

看如下例子:

E), #define INT32 int

unsigned INT32 i =10;

F),typedef int int32;

unsigned int32 j =10;

其中F)编译出错,为什么呢?E)不会出错,这很好理解,因为在预编译的时候INT32

被替换为int,而unsigned int i = 10;语句是正确的。但是,很可惜,用typedef取的别

名不支持这种类型扩展。另外,想想typedefstatic int int32 行不行?为什么?

答:估计编译器将这句话理解为:unsigned ( int) int j =10; 即INT32代表的 int 和unsigned ( int ) 是两次声明,所以出错。

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

typedef小结

typedef关键字

typedef的真正意思是给一个已经存在的数据类型(注意:是类型不是变量)取一个别

名,而非定义一个新的数据类型。

比如:华美绝伦的芍药,就有个别名---“将离”。中国古代男女交往,往往以芍药相赠,表达惜别之情,送芍药就意味着即将分离。这样新的名字与原来的名字相比,就更能表达出想要表达的意思。

typedef struct student

{

//code

}Stu_st,*Stu_pst;//命名规则请参考本章前面部分

A),structstudent stu1;和Stu_st stu1;没有区别。

B),structstudent *stu2;和Stu_pst stu2;和Stu_st*stu2;没有区别。

typedef作用范围

typedef的最大作用范围只能是其所在的编译单元,不同C文件里面的typedef相互独立,完全没有任何限制。不同函数体也相互独立

#include <stdio.h>

typedef double INT32;

INT32 gb ='a';//sizeof gb =8double

int main()
{
    typedef intINT32;  
       INT32b = 'a';     //sizeof b=4  int
    return 0;
}

typedef的四个用途和三个陷阱

用途一:定义一种类型的别名,而不只是简单的宏替换。可以用作同时声明指针型的多个对象。比如:char*pa,pb;尤其在需要大量指针的地方,typedef的方式更省事。

用途二:定义struct详细分析见另一篇文章..

用途三:用typedef来定义与平台无关的类型。

比如定义一个叫REAL的浮点类型,

在目标平台一上,让它表示最高精度的类型为:typedeflongdoubleREAL;

在不支持longdouble的平台二上,改为:typedefdoubleREAL;

在连double都不支持的平台三上,改为:typedeffloatREAL;

也就是说,当跨平台时,只要改下typedef本身就行,不用对其他源码做任何修改。

标准库就广泛使用了这个技巧,比如size_t。另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健(虽然用宏有时也可以完成以上的用途)。

用途四:为复杂的声明定义一个新的简单的别名。

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

理解复杂声明可用的“右左法则”:

从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;

括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完。

举例: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 double INT32;

unsigned INT32 ga;//错误的写法

陷阱二:(const连用)

记住,typedef是定义了一种类型的新别名,不同于宏,它不是简单的字符串替换。比如:

先定义:typedefchar*PSTR;

然后:

intmystrcmp(constPSTR,constPSTR);

constPSTR相当于constchar*吗?不是的,它实际上相当于char*const。

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

指针本身是const的指针指向的变量是可以变的。

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

陷阱三:

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

typedefstaticintINT2;//不可行编译将失败,会提示“指定了一个以上的存储类”。

分享到:
评论

相关推荐

    关于typedef的用法总结

    ### 关于 `typedef` 的用法总结 #### 1. 定义类型的别名 `typedef` 最常见的用途之一...此外,与宏定义 `#define` 相比,`typedef` 更具安全性,因为它不会像宏那样发生意外的替换,这在实际开发中是非常有价值的。

    揭秘typedef四用途与两个陷阱

    详解typedef四用途与两陷阱 typedef语法 声明一个别名 区别于#define思维与用法 详细解析了typedef的四个重要用途与两个误区

    C语言之typedef的问题.doc

    今天,我们将详细地探讨 typedef 的一些用法和错误解析。 首先,让我们来了解 typedef 的基本概念。typedef 的主要作用是为一种数据类型定义一个新名字,这些数据类型包括内部数据类型(int、char 等)和自定义的...

    C语言笔试之Typedef应用

    当我们使用`typedef`关键字时,实际上是创建了一个新的类型名,这个新名称与已有的数据类型等价。例如,在给定的例子中,`typedef struct s * tPS;`声明了`tPS`是一个结构体`s`的指针的同义词。这样,我们可以使用`...

    C/C++中的typedef和#define详解

    在C/C++编程语言中,`typedef` 和 `#define` 都被用来创建类型别名或常量,但它们之间存在着本质的区别。`typedef` 是C/C++的关键字,而`#define` 是预处理器指令。理解这些差异对于编写清晰、可维护的代码至关重要...

    #define宏定义——只替换,不计算! #define VS typedef

    `#define`与`typedef`的区别: 1. `#define`是预处理器指令,进行简单的文本替换,不检查语法或类型。例如,`#define PI 3.1415926`,如果误写为`g`,预处理器仍会替换,导致编译错误。 2. `typedef`是在编译阶段...

    #define用法集锦.doc

    此外,`#define`定义的"函数"在某些情况下可能会引起语法错误,例如`#define pin (int*)`,会导致`pin a, b`这样的声明被错误地解析。为了避免这些问题,应使用函数或`typedef`。 3. **宏的单行定义** `#define`...

    用typedef定义类型的总结分析

    5. **typedef 与 #define 的区别**: 虽然 `typedef` 和 `#define` 都可以用于创建别名,但它们的处理方式不同。`#define` 是预处理器指令,它在预编译阶段进行文本替换,而 `typedef` 是在编译阶段处理,它实际上...

    C语言:栈(含括号匹配)仅供初学者

    根据提供的文件信息,本文将对C语言中的栈及其在括号匹配问题中的应用进行详细的解析。此内容适合初学者理解栈的基本概念、实现方法及如何使用栈解决实际问题。 ### C语言栈的基本概念 栈是一种特殊的线性表,只...

    宏定义解析

    ### 宏定义解析 #### 一、宏定义概述 宏定义是C语言中的一种特性,它允许程序员使用一个标识符来表示一个字符串,这个标识符被称为“宏名”。宏定义在预处理阶段由预处理器自动完成,即在编译之前进行宏代换,将...

    计算机网络课程设计IP数据包解析.doc

    IP协议 define了IP数据包的格式和结构,包括IP头部和数据部分。 3.6 捕获处理 捕获处理是指对接收到的数据包进行处理,包括对IP头部的解析和数据部分的处理。在捕获处理过程中,我们需要对IP头部的各个字段进行...

    13. C语言中的关键字解析.docx

    下面我们将深入探讨在C语言中经常使用的几个关键字:`#define`(宏定义)、`static`(静态变量)、`const`(只读变量)和`volatile`(易变变量)以及`typedef`。 一、宏定义(#define) 宏定义是C语言中的预处理器指令,...

    《数据结构》算法实现及解析.高一凡编着

    根据提供的文件信息,我们可以推断出这是一本关于数据结构及其算法实现与解析的书籍,作者为高一凡。下面将对数据结构的基本概念、重要性以及常见的数据结构类型进行详细的介绍,并概述几种典型的数据结构及其算法...

    C语言解析教程(原书第4版)(美) 凯利.pdf

    1.4 使用#define和#include 1.5 使用printf()和scanf() 1.6 控制流 1.7 函数 1.8 数组、字符串和指针 1.8.1 数组 1.8.2 字符串 1.8.3 指针 1.9 文件 1.10 与操作系统有关的内容 1.10.1 编写和运行c程序 1.10.2 ...

    UIP中文文档第七-uIP编译时配置选项.doc

    4. **CPU字节顺序**:`#define UIP_CONF_BYTE_ORDER` 配置CPU的字节顺序,对于不同架构的CPU,如小端或大端,需要正确设置此选项以确保数据正确解析。 5. **日志记录**:`#define UIP_CONF_LOGGING` 开启或关闭uIP...

    嵌入式软件经典笔试题及其答案

    在上述例子中,`typedef` 能确保 `dPS` 和 `tPS` 都被正确地解析为结构体指针,而 `#define` 可能导致意外的类型匹配。 2. 指针和数据运用: - 指针在C语言中是重要的概念,用于存储变量的地址。在数组和指针的...

    CRC16_32+C实例与硬件CRC精解

    本文旨在通过实例与硬件CRC的解析,深入浅出地讲解CRC的工作原理及其在现代微控制器(MCU)中的应用,尤其是针对CRC16与CRC32的标准。 #### CRC的基本概念 CRC校验基于多项式除法的原理,通过预设的生成多项式对...

    深入源码解析Python中的对象与类型

    ### 深入源码解析Python中的对象与类型 #### 引言 Python作为一种动态类型的语言,其对象和类型的概念是其核心基础之一。本文将深入探讨Python内部如何实现对象和类型,特别是对象的引用计数机制以及类型的定义。...

    我的航空售票系统代码

    根据给定的信息,本文将对“我的航空售票系统代码”中的关键知识点进行详细的解析与说明。此代码主要涉及了C语言的基本数据结构定义、内存管理、链表操作以及队列等概念。 ### 1. 基本宏定义 #### 1.1 `#define` ...

    c语言程序设计课程模拟试题(卷)与答案解析(2015秋季用最新).doc

    【C语言程序设计课程模拟试题与答案解析】 一、单选题 1. C语言源程序文件的缺省扩展名为`.c`,这是C语言的标准文件后缀。 2. `x && y` 为真(非零)的条件是 `x` 和 `y` 均为真,即两个逻辑值都是非零(非假)。...

Global site tag (gtag.js) - Google Analytics