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

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四用途与两陷阱 typedef语法 声明一个别名 区别于#define思维与用法 详细解析了typedef的四个重要用途与两个误区

    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 ...

    餐厅信息管理系统.rar

    希望大家下载后好好的阅读代码,难的地方还有解析,应该会帮到你的 1 葱花炒鸡蛋 4.000000 2 红烧鲤鱼 66.000000 3 麻辣豆腐 4.000000 4 东坡肘子 33.000000 5 北京烤鸭 108.000000 6 鲤鱼跃龙门 666.000000 7 南京...

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

    在学习C语言和C++过程中,我们接触了许多的关键字,下边我们再来回顾一下使用频率比较高的几个关键字:#define(宏定义)、static(静态变量)、const(只读变量)volatile(直接从内存读数据)、typedef

    c语言http请求解析表单内容

    cgi.h 代码如下:#ifndef CGI_H#define CGI_H #include &lt;stdio&gt;#include &lt;string&gt;#include typedef struct Node{ char *name; char *value; struct Node *next;}Node; typedef struct Index{ Node *head; char ...

    NTLM PPROXY client的实现(C source code)

    汗啊,因为验证例子是从某个程序中抽取出来,里面包含一个很简单的http的解析。我本来想update资源,没有找到 update的方法。需要说明的是这部分和真正的算法没有直接关系。放在这里,太长了,如果需要的网友,请到...

    C语言进阶-牟海军.pdf

     与strlen、sizeof、const、volatile、void、void*、#define、typedef、realloc、malloc、calloc等相关的一些陷阱知识点的解析;  时间复杂度、冒泡排序法、选择排序法、快速排序法、归并排序法、顺序排序法、二...

    C语言进阶 作者 Wrestle.Wu

     与strlen、sizeof、const、volatile、void、void*、#define、typedef、realloc、malloc、calloc等相关的一些陷阱知识点的解析;  时间复杂度、冒泡排序法、选择排序法、快速排序法、归并排序法、顺序排序法、...

    C/C++程序员面试指南.杨国祥(带详细书签).pdf

    面试题6:#define CHAR char*和typedef char* CHAR各有什么优劣 面试题7:谈谈你对typedef的认识 4.2 const(常量) 面试题8:关键字const是什么 面试题9:说明以下a声明的含义 面试题10:const、define定义常量的...

    数据结构图实验报告.docx

    实验内容: 建立无向图的邻接矩阵 图的深度优先搜索 图的广度优先搜索 三、实验步骤及结果: 建立无向图的邻接矩阵: 源代码: #include "stdio.h" #include "stdlib.h" #define MAXSIZE 30 typedef struct 数据...

    Dev-CPP:轻量级CC ++ IDE

    Red Panda Dev C ++(小熊猫Dev-C ++,旧名称Dev-C ++ 2000)是Orwell Dev-C ++的改进版。 自2015年以来,Orwell Dev-C ++已停止更新,因此我对此进行了分叉。... 正确显示#define / typedef / enum /

    你必须知道的495个C语言问题.pdf

    1.13 对于用户定义类型,typedef 和#define有什么区别? 1.14 我似乎不能成功定义一个链表。我试过typedef struct{char *item; NODEPTR next;}* NODEPTR; 但是编译器报了错误信息。难道在C语言中结构不能包含指向...

    C语言深度揭秘

    1.17.3,typedef与#define的区别.........................45 1.17.4,#define a int[10]与 typedef int a[10];.... 46 第二章 符号................. 49 2.1,注释符号......50 2.1.1,几个似非而是的注释问题.......

    驱动程序设计基础专题-filedisk源码分析

    然后根据解析命令行,调用相应的函数来完成我们所需要的操作。 顺便联想一下。int main(int argc,char * argv[])参数的使用。 比如命令行输入: cfile computer C_Langle .此时argc=3. argv[]指向3个字符串,这些...

    封装h264,/mpeg4 为MP4文件

    /* Mark:mp4封装解析接口 /* author: machh /* date:2012.5.12. /******************************************/ #ifndef _MP4_INTERFACE_ #define _MP4_INTERFACE_ #include "MP4Writer.h" #include "MP4Reader.h" ...

    《你必须知道的495个C语言问题》

    1.13 对于用户定义类型,typedef 和#define有什么区别? 7 1.14 我似乎不能成功定义一个链表。我试过typedef struct{char *item; NODEPTR next;}* NODEPTR; 但是编译器报了错误信息。难道在C语言中结构不能包含...

    一些C面试题,希望能对大家有帮助

    ARP (Address Resolution Protocol)(地址解析協議) 18.IP地址的编码分为哪俩部分? IP地址由两部分组成,网络号和主机号。不过是要和“子网掩码”按位与上之后才能区分哪些是网络位哪些是主机位。 2.用户输入M,N...

    你必须知道的495个C语言问题

    1.13 对于用户定义类型,typedef和#define有什么区别? 1.14 我似乎不能成功定义一个链表。我试过typedefstruct{char*item;NODEPTRnext;}*NODEPTR;但是编译器报了错误信息。难道在C语言中结构不能包含指向自己的...

Global site tag (gtag.js) - Google Analytics