• typedef 摘自百度百科


    1、typedef的最简单使用
    1
    typedef long byte_4;
    给已知数据类型long起个新名字,叫byte_4。
    2、 typedef与结构结合使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    typedef struct tagMyStruct
    {
     
     
    int iNum;
     
    long lLength;
     
     
    }MyStruct;
    这语句实际上完成两个操作:
    1) 定义一个新的结构类型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    struct tagMyStruct
    {
     
     
    int iNum;
     
     
    long lLength;
     
    };
    分析:tagMyStruct称为“tag”,即“标签”,实际上是一个临时名字,struct关键字和tagMyStruct一起,构成了这个结构类型,不论是否有typedef,这个结构都存在。
    我们可以用struct tagMyStruct varName来定义变量,但要注意,使用tagMyStruct varName来定义变量是不对的,因为struct 和tagMyStruct合在一起才能表示一个结构类型。
    2) typedef为这个新的结构起了一个名字,叫MyStruct。
    typedef struct tagMyStruct MyStruct;
    因此,MyStruct实际上相当于struct tagMyStruct,我们可以使用MyStruct varName来定义变量。
    答案与分析
    C语言当然允许在结构中包含指向它自己的指针,我们可以在建立链表等数据结构的实现上看到无数这样的例子,上述代码的根本问题在于typedef的应用。
    根据我们上面的阐述可以知道:新结构建立的过程中遇到了pNext域的声明,类型是pNode,要知道pNode表示的是类型的新名字,那么在类型本身还没有建立完成的时候,这个类型的新名字也还不存在,也就是说这个时候编译器根本不认识pNode。
    解决这个问题的方法有多种:
    1)、
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    typedef struct tagNode
    {
     
     
    char* pItem;
     
     
    struct tagNode* pNext;
     
    }*pNode;
    2)、
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    typedef struct tagNode* pNode;
     
     
    struct tagNode
    {
     
    char* pItem;
     
    pNode pNext;//这边不用pNode* ,pNode 已经表示了struct tagNode*
    };
    注意:在这个例子中,你用typedef给一个还未完全声明的类型起新名字。C语言编译器支持这种做法。
    3)、规范做法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    struct tagNode
    {
     
    char* pItem;
     
    struct tagNode* pNext;
    };
     
    typedef struct tagNode* pNode;
    3. typedef & #define的问题
    有下面两种定义pStr数据类型的方法,两者有什么不同?哪一种更好一点?
    1
    2
    3
    typedef char* pStr;
     
    #define pStr char*
    答案与分析:
    通常讲,typedef要比#define要好,特别是在有指针的场合。请看例子:
    1
    2
    3
    4
    5
    6
    7
    typedef char* pStr1;
     
    #define pStr2 char* 
     
    pStr1 s1,s2;
     
    pStr2 s3,s4;
    在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char,不是我们所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一个类型起新名字。
    上例中define语句必须写成 pStr2 s3, *s4; 这样才能正常执行。
    #define用法例子:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include <stdio.h>
    #define f(x) x*x
    int main(void)
    {
        int a=6, b=2, c;
        c = f(a) / f(b);
        printf("%d ", c);
        return 0;
    }
    以下程序的输出结果是: 36。
    因为如此原因,在许多C语言编程规范中提到使用#define定义时,如果定义中包含表达式,必须使用括号,则上述定义应该如下定义才对:
    1
    #definef(x)((x)*(x))
    当然,如果你使用typedef就没有这样的问题。
    4. typedef & #define的另一例
    下面的代码中编译器会报一个错误,你知道是哪个语句错了吗?
    1
    2
    3
    4
    5
    6
    typedef char *pStr;
    char string[4]="abc";
    const char *p1=string;
    const pStr p2=string;
    p1++;
    p2++;
    答案与分析:
    是p2++出错了。这个问题再一次提醒我们:typedef和#define不同,它不是简单的文本替换。上述代码中const pStr p2并不等于const char * p2。const pStr p2和pStr const p2本质上没有区别,都是对变量进行只读限制,只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已。因此,const pStr p2的含义是:限定数据类型为char *的变量p2为只读,因此p2++错误。
    #define与typedef引申谈
    1) #define宏定义有一个特别的长处:可以使用 #ifdef ,#ifndef等来进行逻辑判断,还可以使用#undef来取消定义。
    2) typedef也有一个特别的长处:它符合范围规则,使用typedef定义的变量类型其作用范围限制在所定义的函数或者文件内(取决于此变量定义的位置),而宏定义则没有这种特性。
    5. 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作简化自有其价值,比如:
    下面是三个变量的声明,我想使用typdef分别给它们定义一个别名,请问该如何做?
    >1:int *(*a[5])(int, char*);
    >2:void (*b[10]) (void (*)());
    >3. double(* (*pa)[9])();
    答案与分析:
    对复杂变量建立一个类型别名的方法很简单,你只要在传统的变量声明表达式里用类型名替代变量名,然后把关键字typedef加在该语句的开头就行了。
    >1:int *(*a[5])(int, char*);
    //pFun是我们建的一个类型别名
    typedef int *(*pFun)(int, char*);
    //使用定义的新类型来声明对象,等价于int* (*a[5])(int, char*);
    pFun a[5];
    >2:void (*b[10]) (void (*)());
    //首先为上面表达式加粗部分声明一个新类型
    typedef void (*pFunParam)();
    //整体声明一个新类型
    typedef void (*pFun)(pFunParam);
    //使用定义的新类型来声明对象,等价于void (*b[10]) (void (*)());
    pFun b[10];
    >3. double(* [1]  (*pa)[9])() [2]  ;
    //首先为上面表达式蓝色部分声明一个新类型
    typedef double(*pFun)();
    //整体声明一个新类型
    typedef pFun (*pFunParam)[9];
    //使用定义的新类型来声明对象,等价于double(*(*pa)[9])();
    pFunParam pa;
    pa是一个指针,指针指向一个数组,这个数组有9个元素,每一个元素都是“doube(*)()”--也即一个指针,指向一个函数,函数参数为空,返回值是“double”。
     
    typedef常见用法

    1.常规变量类型定义

    例如:typedef unsigned char uchar
    描述:uchar等价于unsigned char类型定义 uchar c声明等于unsigned char c声明

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

    扩展: typedef int array[M][N];
    描述: array等价于 int [M][N]定义; array a声明等价于int a[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); func等价于 int (void)类型函数
    描述1: func f声明等价于 int f(void)声明,用于文件的函数声明
    描述2: func *pf声明等价于 int (*pf)(void)声明,用于函数指针的生命,见下一条

    6.函数指针
    例如: typedef int (*func)(void)
    描述: func等价于int (*)(void)类型
    func pf等价于int (*pf)(void)声明,pf是一个函数指针变量

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

    代码简化

    编辑
    上面讨论的 typedef 行为有点像 #define 宏,用其实际类型替代同义字。不同点是 typedef 在编译时被解释,因此让编译器来应付超越预处理器能力的文本替换。例如:
    typedef int (*PF) (const char *, const char *);
    这个声明引入了 PF 类型作为函数指针的同义字,该函数有两个 const char * 类型的参数以及一个 int 类型的返回值。如果要使用下列形式的函数声明,那么上述这个 typedef 是不可或缺的:
    PF Register(PF pf);
    Register() 的参数是一个 PF 类型的回调函数,返回某个函数的地址,其署名与先前注册的名字相同。做一次深呼吸。下面我展示一下如果不用 typedef,我们是如何实现这个声明的:
    int (*Register (int (*pf)(const char *, const char *)))
    (const char *, const char *);
    很少有程序员理解它是什么意思,更不用说这种费解的代码所带来的出错风险了。显然,这里使用 typedef 不是一种特权,而是一种必需。持怀疑态度的人可能会问:"OK,有人还会写这样的代码吗?",快速浏览一下揭示 signal()函数的头文件 ,一个有同样接口的函数。注意这里Register被定义为一个函数而不是函数指针,如果要定义为函数指针应该这样写:int (*(*Register) (int (*pf)(const char *, const char *))) (const char *, const char *);
    typedef 和存储类关键字(storage class specifier)
    这种说法是不是有点令人惊讶,typedef 就像 auto,extern,mutable,static,和 register 一样,是一个存储类关键字。这并不是说 typedef 会真正影响对象的存储特性;它只是说在语句构成上,typedef 声明看起来象 static,extern 等类型的变量声明。下面将带到第二个陷阱:
    typedef register int FAST_COUNTER; // 错误
    编译通不过。问题出在你不能在声明中有多个存储类关键字。因为符号 typedef 已经占据了存储类关键字的位置,在 typedef 声明中不能用 register(或任何其它存储类关键字)。
  • 相关阅读:
    /etc/security/limits.conf 配置
    docker 私有仓库镜像的存储位置
    docker 私有仓库镜像的存储位置
    docker 数据映射方案
    docker 数据映射方案
    docker 容器开启ssh服务
    docker 容器开启ssh服务
    docker 容器扩盘
    docker 容器扩盘
    Python爬虫从入门到精通——基本库re的使用:正则表达式
  • 原文地址:https://www.cnblogs.com/zhouweibaba/p/10165398.html
Copyright © 2020-2023  润新知