zoukankan      html  css  js  c++  java
  • typedef的用法--摘录

    http://zjf30366.blog.163.com/blog/static/411164582009817101543293/

    有种很方便的写法。

    typedef int *p;

    p pointer;

    这时直接把pointer带入原式中,取代p然后去掉typedef,得到的结果就是int * pointer;

    哈哈,这样直接替换就很直观多了。

    C语言语法简单,但内涵却博大精深;如果在学习时只是止步于表面,那么往往后期会遇到很多困难。typedef是C语言中一个很好用的工具,大量存在于已有代码中,特别值得一提的是:C++标准库实现中更是对typedef有着大量的使用。但很多初学者对其的理解仅局限于:typedef用来定义一个已有类型的"别名(alias)"。正是因为有了这样的理解,才有了后来初学者在typedef int myint和typedef myint int之间的犹豫不决。很多国内大学的C语言课之授课老师也都是如是说的,或者老师讲的不够透彻,导致学生们都是如是理解的。我这里想结合C语言标准文档以及一些代码实例,也说说typedef。

    int    *p;

    这样的代码是C语言中最最基础的一个语句了,大家都知道这个语句声明了一个变量p,其类型是指向整型的指针(pointer to int);如果在这个声明的前面加上一个typedef后,整个语义(semantics)又会是如何改变的呢?

    typedef  int    *p;

    我们先来看看C99标准中关于typedef是如何诠释的?C99标准中这样一小段精辟的描述:"In a declaration whose storage-class specifier is typedef, each declarator defines an identifier to be a typedef name that denotes the type specified for the identifier in the way described in xx"。

    参照这段描述,并拿typedef  int    *p作为例子来理解:在一个声明中,如果有存储类说明符typedef的修饰,标识符p将被定义为了一个typedef name,这个typedef name表示(denotes)一个类型,什么类型呢?就是int *p这个声明(declarator)中标识符(indentifier)p的类型(int*)。

    再比对一下两个声明:

    int    *p;

    typedef  int    *p;

    是不是有点"茅舍顿开"的感觉,int *p中, p是一个变量,其类型为pointer to int;在int *p前面增加一个typedef后,p变为一个typedef-name,这个typedef-name所表示的类型就是int *p声明式中p的类型(int*)。说句白话,typedef让p去除了普通变量的身份,摇身一变,变成了p的类型的一个typedef-name了。

    为了巩固上面的理解,我们再来看看"C语言参考手册(C: A Reference Manual)"中的说法:任何declarator(如typedef int   *p)中的indentifier(如p)定义为typedef-name, 其(指代p)表示的类型是declarator为正常变量声明(指代int  *p)的那个标识符(指代p)的类型(int*)。有些绕嘴,不过有例子支撑:

    [例1]

    typedef double MYDOUBLE;  

    分析:

    去掉typedef ,得到正常变量声明=> double MYDOUBLE;

    变量MYDOUBLE的类型为double;

    => "typedef double MYDOUBLE"中MYDOUBLE是类型double的一个typedef-name。

    MYDOUBLE    d; <=> d是一个double类型的变量

    [例2]

    typedef double *Dp;  

    分析:

    去掉typedef  ,得到正常变量声明=> double *Dp;

    变量Dp的类型为double*,即pointer to double;

    => "typedef double *Dp"中Dp是类型double*的一个typedef-name。

    Dp    dptr; <=> dptr是一个pointer to double的变量

    [例3]

    typedef int* Func(int);

    分析:

    去掉typedef  ,得到正常变量声明=> int* Func(int);

    变量Func的类型为一个函数标识符,该函数返回值类型为int*,参数类型为int;

    => "typedef int* Func(int)"中Func是函数类型(函数返回值类型为int*,参数类型为int)的一个typedef-name。

    Func    *fptr; <=> fptr是一个pointer to function with one int parameter, returning a pointer to int

    Func     f;   这样的声明意义就不大了。

    [例4]

    typedef int (*PFunc)(int);

    分析:

    去掉typedef  ,得到正常变量声明=> int (*PFunc)(int);

    变量PFunc的类型为一个函数指针,指向的返回值类型为int,参数类型为int的函数原型;

    => "typedef int (*PFunc)(int)"中PFunc是函数指针类型(该指针类型指向返回值类型为int,参数类型为int的函数)的一个typedef-name。

    PFunc     fptr; <=> fptr是一个pointer to function with one int parameter, returning int

    #include "iostream"

    using namespace std;

    int add(int a,int b){
    return (a+b);
    }

    typedef int (* func)(int ,int ) ;

    void main(){
    func f = add;
    int n = f(1,2);
    cout << n << endl;
    }

    [例5]

    typedef    int   A[5];

    分析:

    去掉typedef ,得到正常变量声明 => int   A[5];

    变量A的类型为一个含有5个元素的整型数组;

    => "typedef    int   A[5]"中A是含有5个元素的数组类型的一个typedef-name。

    A   a = {3, 4, 5, 7, 8};

    A   b = { 3, 4, 5, 7, 8, 9}; /* 会给出Warning: excess elements in array initializer */

    [例6]

    typedef    int   (*A)[5]; (注意与typedef    int*    A[5]; 区分)

    分析:

    去掉typedef ,得到正常变量声明 => int   (*A)[5];

    变量A的类型为pointer to an array with 5 int elements;

    => "typedef    int   (*A)[5]"中A是"pointer to an array with 5 int elements"的一个typedef-name。

    int   c[5] = {3, 4, 5, 7, 8};  

    A    a = &c;

    printf("%d ", (*a)[0]); /* output: 3 */

    如果这样赋值:

    int   c[6] = {3, 4, 5, 7, 8, 9};  

    A    a = &c; /* 会有Warning: initialization from incompatible pointer type */

    [例7]

    typedef struct _Foo_t Foo_t;

    分析:

    去掉typedef ,得到正常变量声明 => struct _Foo_t Foo_t;

    变量Foo_t的类型为struct _Foo_t;

    => "typedef struct _Foo_t Foo_t"中Foo_t是"struct _Foo_t"的一个typedef-name。

    [例8]

    typedef   struct { ... // }   Foo_t;

    分析:

    去掉typedef ,得到正常变量声明 => struct { ... // }   Foo_t;

    变量Foo_t的类型为struct { ... // } ;

    => "typedef   struct { ... // }   Foo_t "中Foo_t是"struct { ... // }"的一个typedef-name。这里struct {...//}是一个无"标志名称(tag name)"的结构体声明。

    参考资料:

    1、"ISOIEC-98991999(E)--Programming Languages--C"之Page 123;

    2、C语言参考手册(中文版) 之 Page 119

    http://www.cnblogs.com/lkkandsyf/archive/2015/04/27/4461389.html

    C 中typedef 函数指针的使用

    类型定义的语法可以归结为一句话:只要在变量定义前面加上typedef,就成了类型定义。这儿的原本应该是变量的东西,就成为了类型。

    int integer;     //整型变量
    int *pointer;   //整型指针变量
    int array [5]; //整型数组变量
    int *p_array [5]; //整型指针的数组的变量
    int (*array_pointer) [5];//整型数组的指针的变量
    int function (int param);//函数定义,也可将函数名看作函数的变量
    int *function (int param);//仍然是函数,但返回值是整型指针
    int (*function) (int param);//现在就是指向函数的指针了

    若要定义相应类型,即为类型来起名字,就是下面的形式:
    typedef int integer_t;                      //整型类型
    typedef int *pointer_t;     //整型指针类型
    typedef int array_t [5]; //整型数组类型
    typedef int *p_array_t [5];    //整型指针的数组的类型
    typedef int (*array_pointer_t) [5]; //整型数组的指针的类型
    typedef int function_t (int param);     //函数类型
    typedef int *function_t (int param);    //函数类型
    typedef int (*function_t) (int param); //指向函数的指针的类型
    注意:上面的函数类型在C中可能会出错,因为C中并没有函数类型,它的函数变量会自动退化成函数指针;在C++中好像是可以的。在这里主要说明的是形式上的相似性.

    typedef的一般形式为:
    typedef   类型     定义名;
    在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。
    其实,在C语言中声明变量的时候,有个存储类型指示符(storage-class-specifier),它包括我们熟悉的extern、static、auto、register。在不指定存储类型指示符的时候,编译器会根据约定自动取缺省值。另外,存储类型指示符的位置也是任意的(但要求在变量名和指针*之前),也就是说以下几行代码是等价的:
    static const int i;
    const static int i;
    int const static i;
    const int static i;
    根据C语言规范,在进行句法分析的时候,typedef和存储类型指示符是等价的!所以,我们把上述使用static的地方替换为typedef:
    typedef const int i;
    const typedef int i;
    int const typedef i;
    const int typedef i;
    上述代码的语义是:将i定义为一个类型名,其等价的类型为const int。以后如果我们有i   a代码,就等价于const int a。对于有指针的地方也是一样的,比如:
    int const typedef *t;那么代码t   p。就相当于int const *p。
    另外,typedef不能和static等存储类型指示符同时使用,因为每个变量只能有一种存储类型,所以代码:typedef static int i;是非法的。

    typedef有两种用法:
    一、一般形式,定义已有类型的别名
      typedef   类型    定义名;
    二、创建一个新的类型
         typedef   返回值类型   新类型名(参数列表);

    1)typedef int NUM[10];//声明整型数组类型

        NUM n;//定义n为整型数组变量,其中n[0]--n[9]可用

    2)typedef char* STRING;//声明STRING为字符指针类型

        STRING p,s[10];//p为字符指针变量,s为指针数组

    3)typedef int (*POINTER)();//声明POINTER为指向函数的指针类型,该函数返回整型值,没有参数

        POINTER P1,P2;//p1,p2为POINTER类型的指针变量

    说明:

          1)用typedef可以声明各种类型名,但不能用来定义变量,用typedef可以声明数组类型、字符串类型、使用比较方便。

    例如:定义数组,原来是用:int a[10],b[10],c[10],d[10];由于都是一维数组,大小也相同,可以先将此数组类型声明为一个名字:

    typedef int ARR[10];

    然后用ARR去定义数组变量:

    ARR a,b,c,d;//ARR为数组类型,它包含10个元素。因此a,b,c,d都被定义为一维数组,含10个元素。可以看到,用typedef可以将 数组类型 和 数组变量 分离开来,利用数组类型可以定义多个数组变量。同样可以定义字符串类型、指针类型等。

          2)用typedef只是对已经存在的类型增加一个类型名,而没有创造新的类型。

          3)typedef与#define有相似之处,但事实上二者是不同的,#define是在 预编译 时处理,它只能做简单的字符串替换,而typedef是在 编译时 处理的。它并不是做简单的字符串替换,而是采用如同 定义变量 的方法那样来 声明 一个类型。

    例如:typedef int COUNT;和#define COUNT int的作用都是用COUNT代表int,单事实上它们二者是不同的。

    两个陷阱:

    陷阱一: 
    记住,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、mutable、static、register等一样),虽然它并不真正影响对象的存储特性,如:
    typedef static int INT2; //不可行
    编译将失败,会提示“指定了一个以上的存储类”。

    1.typedef  函数指针的使用方法

    (1)typedef 首先是用来定义新的类型,i.e typedef struct {.....}mystruct; 在以后引用时,就可以用 mystruct 来定义自己的结构体,mystruct structname1,mystruct structname2.

    (2)typedef 常用的地方,就在定义函数指针,行为和宏定义类似,用实际类型替换同义字,但是有区别: typedef 在编译时被解释,因此让编译器来应付超越预处理器能力的文本替换

    案例一: 
    通常讲,typedef要比#define要好,特别是在有指针的场合。请看例子: 
    typedef char *pStr1; 
    #define pStr2 char *; 
    pStr1 s1, s2; 
    pStr2 s3, s4; 
    在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char,不是我们所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一个类型起新名字。 
    案例二: 
    下面的代码中编译器会报一个错误,你知道是哪个语句错了吗? 
    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和const long x本质上没有区别,都是对变量进行只读限制,只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已。因此,const pStr p2的含义是:限定数据类型为char *的变量p2为只读,因此p2++错误。

    用法一:

     typedef  返回类型(*新类型)(参数表)

     typedef int ( * MYFUNCTION )( int,int ); 这种用法一般是在定义函数指针 MYFUNCTION 是一个函数指针类型 有两个整型的参数,返回一个整型。

    在对于这样的形式,去掉typedef和别名 就剩下了的是原变量的类型 如:int (*)(int ,int); 在函数指针中,抽象得看待函数,函数名其实就是一个地址,函数名指向该函数的代码在内存的首地址。

    用法二: 复杂函数声明类型

     下面是三个变量的声明  用typedef 如何来做???

    >1 int *(*a[5])(void *,void *);

    >2 void (*b[5])(void (*)());

    >3 float (*)()(*pa)[10];

    分析如下:

    >1 int *(*a[5])(void *,void *);

    //pFUN是自己建立的类型别名 typedef int *(* pFUN)(void  *,void *); //等价于int *(*a[5])(void *,void *); 

    pFUN a[5];  a是一个数组,包含五个元素,这些元素都是函数指针,该函数指针所指的函数的返回值是int的指针 输入参数有两个都是void *.

    >2 void (*b[5])( void (*)() );

    // first 为蓝色的 声明一个新的类型 typedef void (*pFUNParam)( );

    //整体声明一个新类型  typedef void (*pFUN)(FUNParam); 

    //使用定义的新类型声明对象 等价于void (*b[5])( void (*)() ); 

    pFUN b[5]; b 是一个含有5个元素的数组,每个元素都是一个函数指针,该函数指针所指的函数的返回值是void.输入参数是另一个函数指针,这个函数指针没有参数,返回值为空。在这里套用了连续的函数指针。本身就是一个函数指针,而且参数也是一个函数指针。

    >3 float (*)()(*pa)[10];

    //first 为上面的蓝色表达式声明一个新类型 typedef float (*pFUN)(); 

    //整体声明一个新类型typedef pFUN (* pFunParam)[10];

    //使用定义的新类型来声明对象 等价与float (*)()(*pa)[10];

    pa 是一个指针,指针指向一个含有10个元素的数组,数组的元素是函数指针,函数指针所指的函数没有输入参数,返回值为float.

    **********************************************

    使用typedef简化复杂的变量声明
    1)、定义一个有10个指针的数组,该指针指向一个函数,该函数有一个整形参数,并返回一个整型?
    第一种方法:int (*a[10])(int);
    第二种方法:typedef int (*pfunc)(int);
                 pfunc a[10];
    2)、定义一个有10个指针的数组,该指针指向一个函数,该函数有一个函数指针(不带参数,返回值为空)参数,并返回空。
    第一种方法:void (*a[10])(void (*)(void));
    第二种方法:typedef void (*pfuncParam)(void);
                   typedef void (*pfunc)(pfuncParam);
    pfunc a[10];
    3)、一个指向有10个函数指针(不带参数,返回值为double)数组的指针
    第一种方法:double (*)(void) (*p)[10];
    第二种方法:typedef double (*pfunc)(void);
                 typedef pfunc (*pfuncParam)[10];
                 pfuncParam p;

    从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完。举例:
    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 (*)[]数组指针 

    **********************************************

    finally 

    typedef 使用最多的地方是创建易于记忆的类型名,用它来归档程序员的意图。类型出现在所声明的变量名字中,位于 ''typedef'' 关键字右边。例如:
    typedef int size;此声明定义了一个 int 的同义字,名字为 size。注意 typedef 并不创建新的类型。它仅仅为现有类型添加一个同义字。你可以在任何需要 int 的上下文中使用 size:
    void measure(size * psz); size array[4];size len = file.getlength();std::vector <size> vs; typedef 还可以掩饰符合类型,如指针和数组。例如,你不用象下面这样重复定义有 81 个字符元素的数组:
    char line[81];char text[81];定义一个 typedef,每当要用到相同类型和大小的数组时,可以这样:
    typedef char Line[81]; Line text, secondline;getline(text);同样,可以象下面这样隐藏指针语法:
    typedef char * pstr;int mystrcmp(pstr, pstr);这里将带我们到达第一个 typedef 陷阱。标准函数 strcmp()有两个‘const char *'类型的参数。因此,它可能会误导人们象下面这样声明 mystrcmp():
    int mystrcmp(const pstr, const pstr); 这是错误的,按照顺序,‘const pstr'被解释为‘char * const'(一个指向 char 的常量指针),而不是‘const char *'(指向常量 char 的指针)。这个问题很容易解决:
    typedef const char * cpstr; int mystrcmp(cpstr, cpstr); // 现在是正确的记住:不管什么时候,只要为指针声明 typedef,那么都要在最终的 typedef 名称中加一个 const,以使得该指针本身是常量,而不是对象。 
    代码简化
    上面讨论的 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()函数的头文件 <csinal>,一个有同样接口的函数。 
    typedef 和存储类关键字(storage class specifier)
    这种说法是不是有点令人惊讶,typedef 就像 auto,extern,mutable,static,和 register 一样,是一个存储类关键字。这并是说 typedef 会真正影响对象的存储特性;它只是说在语句构成上,typedef 声明看起来象 static,extern 等类型的变量声明。下面将带到第二个陷阱:
    typedef register int FAST_COUNTER; // 错误编译通不过。问题出在你不能在声明中有多个存储类关键字。因为符号 typedef 已经占据了存储类关键字的位置,在 typedef 声明中不能用 register(或任何其它存储类关键字)。 
    促进跨平台开发
    typedef 有另外一个重要的用途,那就是定义机器无关的类型,例如,你可以定义一个叫 REAL 的浮点类型,在目标机器上它可以i获得最高的精度:
    typedef long double REAL; 在不支持 long double 的机器上,该 typedef 看起来会是下面这样:
    typedef double REAL; 并且,在连 double 都不支持的机器上,该 typedef 看起来会是这样: 、
    typedef float REAL; 你不用对源代码做任何修改,便可以在每一种平台上编译这个使用 REAL 类型的应用程序。唯一要改的是 typedef 本身。在大多数情况下,甚至这个微小的变动完全都可以通过奇妙的条件编译来自动实现。不是吗? 标准库广泛地使用 typedef 来创建这样的平台无关类型:size_t,ptrdiff 和 fpos_t 就是其中的例子。此外,象 std::string 和 std::ofstream 这样的 typedef 还隐藏了长长的,难以理解的模板特化语法,例如:basic_string<char, char_traits<char>,allocator<char>> 和 basic_ofstream<char, char_traits<char>>。

  • 相关阅读:
    AtCoder Regular Contest 093
    AtCoder Regular Contest 094
    G. Gangsters in Central City
    HGOI 20190711 题解
    HGOI20190710 题解
    HGOI 20190709 题解
    HGOI 20190708 题解
    HGOI20190707 题解
    HGOI20190706 题解
    HGOI 20190705 题解
  • 原文地址:https://www.cnblogs.com/moon-lights/p/7079233.html
Copyright © 2011-2022 走看看