zoukankan      html  css  js  c++  java
  • 如何解释复杂的 C/C++ 声明

    我想很多人曾经遇到过像 int * (*fp1) (int) [10] 这样的声明;或者你无法理解的类似的东西?本文将教您解释如此复杂的 C/C++声明,包括使用打字、const 和函数指头。

    你是否曾经遇到过类似。int * (* (*fp1) (int) ) [10];的语句而无法理解呢?这篇文章将教你解释C/C++宣言,先易后难,从简单的c语言声明、const修饰符,typedef修饰符、函数指针,最后到“左右法则”,本文的目的是帮助理解c语言的声明,并不推荐像文中的代码一样,工作中还是要遵守c编程规范。

    1. 基础

    让我从一个非常简单的例子开始。考虑声明:

    int n;
    

    声明 n 为 int 类型

    int *p;
    

    声明 p 为 int 类型的指针,作者原文建议写成

    int *p
    

    而不是

    int* p
    

    这个个人认为统一就好。

    还可以声明指针的指针

    char **argv;
    

    原则上,这种用法没有限制,这意味着你可以有一个指头指向指头到指头到指头,但是通常二级指针已经是比较难理解了。

    考虑以下声明:

    int RollNum[30][4];
    int (*p)[4]=RollNum;
    int *q[5];
    

    p声明为一个指针,该指针指向一个int类型的数组,该数组大小是4。如果执行p++;p的值增加4*sizeof(int)
    q声明为一个数组,数组的内容是保存指针的,什么指针?所有的指针都指向int类型的数据。

    2. const修饰符

    当您想要防止修改变量(这似乎是自相矛盾的)时,可以使用const关键字。在c语言声明const变量时,就需要初始化这个变量,否则在其他地方是不能赋值的。

    const int n=5;
    int const m=10;
    

    两个变量是同一类型 - 常整型。这是因为C++标准规定关键字可以放置在类型或可变名称之前。就我(原文作者)个人而言,我更喜欢使用前一种风格,因为它使修饰符更清晰。

    但是const和指针关联起来就相对比较难以理解了。

    const int *p;
    int const *q;
    

    其种哪一个是指向一个,哪个指向一个?实际上,它们都是指向int 类型的。
    p和q都指向const int,意味着均无法改变指向变量的数值。

    int * const r= &n; // n has been declared as an int
    

    这里r是一个const指针,初始化时指向了n,那么像下面的语句是非法的。

    r=&m;
    

    但是

    *r=4;
    

    是可以的。但是如果声明了下面的语句:

    const int * const p=&n; // n has been declared as const int
    

    英文:declare a const pointer to a const int,指针是const,变量也是const,都无法在运行中改变了。只能初始化进行赋值。

    下面罗列了一些声明,可以参考学习:

    char ** p1;                    //        pointer to       pointer to       char
    const char **p2;               //        pointer to       pointer to const char
    char * const * p3;             //        pointer to const pointer to       char
    const char * const * p4;       //        pointer to const pointer to const char
    char ** const p5;              //  const pointer to       pointer to       char
    const char ** const p6;        //  const pointer to       pointer to const char
    char * const * const p7;       //  const pointer to const pointer to       char
    const char * const * const p8; //  const pointer to const pointer to const char
    

    3. typedef的微妙之处

    直接看代码:

    typedef char * PCHAR;
    PCHAR p,q;
    

    p和q都定义为char * 类型。

    下面是一些typedef的使用方法及解释:

    typedef char * a;  // a is a pointer to a char
    
    typedef a b();     // b is a function that returns
                       // a pointer to a char
    
    typedef b *c;      // c is a pointer to a function
                       // that returns a pointer to a char
    
    typedef c d();     // d is a function returning
                       // a pointer to a function
                       // that returns a pointer to a char
    
    typedef d *e;      // e is a pointer to a function 
                       // returning  a pointer to a 
                       // function that returns a 
                       // pointer to a char
    
    e var[10];         // var is an array of 10 pointers to 
                       // functions returning pointers to 
                       // functions returning pointers to chars.
    
    

    typedef也常见与结构体类型定义中

    typedef struct tagPOINT
    {
        int x;
        int y;
    }POINT;
    
    POINT p; /*有效的 C 代码 */
    

    4. 函数指针

    相对来说,函数指针较为难理解了。在原始时代的dos,近代的win32,以及x-windows中,常用于回调函数。在c++的虚函数表、STL模版, 以及 Win NT/2K/XP 系统服务等等。
    函数指头的简单示例:

    int (*p)(char);
    void * (*a[5])(char * const, char * const);
    

    声明p为指向一个“参数为char,返回值为int”的函数指针.

    声明a为函数指针数组,数组中每个元素都指向一个

    含有两个char*const指针,返回值为void*的函数
    

    的函数指针

    5 左右法则

    这是一个简单的规则,允许解释任何声明。具体解释如下:

    从最内侧括号开始阅读声明,向右走,然后向左走。当遇到括号时,方向应相反。一旦括号中的所有内容都解析完,就跳出来。然后继续,直到整个声明被解析。

    左右规则的一个注意点:第一次开始阅读声明时,必须从标识符开始,而不是从最内在的括号开始。

    举例:

    int * (* (*fp1) (int) ) [10];
    

    按以下步骤开始庖丁解牛:

    从标识符开始 -------------------fp1
    右边是 ) ,左右向左看,找到了*---fp1是一个指针
    跳出()后遇到了(int) ------------指针指向了一个函数,函数中有一个int参数
    向左看,找到了 * ---------------该函数返回指针
    跳出(), 向右看,找到了 [10] ----返回的指针指向含有10个元素的数组
    向左看,找到了 * ---------------数组元素都是指针
    在向左看, 找到 int -------------每个数组元素指向int类型
    

    解剖完毕
    英文完整的解释:

    Start from the variable name -------------------------- fp1
    Nothing to right but ) so go left to find * -------------- is a pointer
    Jump out of parentheses and encounter (int) --------- to a function that takes an int as argument
    Go left, find * ---------------------------------------- and returns a pointer
    Jump put of parentheses, go right and hit [10] -------- to an array of 10
    Go left find * ----------------------------------------- pointers to
    Go left again, find int -------------------------------- ints.
    

    declare fp1 as pointer to function (int) returning pointer to array 10 of pointer to int

    再来练习一个:

    int *( *( *arr[5])())();
    
    从标识符开始 -----------------arr
    右边,找到[5] -----------------arr是一个数组,5个元素
    向左看, 找到 * ---------------每个元素都是指针
    跳出(), 向右看,找到了() ------每个指针指向函数,即数组元素为函数指针
    向左看, 找到 * --------------- 所指向的函数返回指针
    跳出(), 向右看,找到了() -------返回值指向函数
    向左看, 找到 *  ---------------函数返回指针
    继续向左看, 找到 int ----------指针指向 int类型数据.
    

    解剖完毕
    英文完整的解释:

    Start from the variable name --------------------- arr
    Go right, find array subscript --------------------- is an array of 5
    Go left, find * ----------------------------------- pointers
    Jump out of parentheses, go right to find () ------ to functions
    Go left, encounter * ----------------------------- that return pointers
    Jump out, go right, find () ----------------------- to functions
    Go left, find * ----------------------------------- that return pointers
    Continue left, find int ----------------------------- to ints.
    

    declare arr as array 5 of pointer to function returning pointer to function returning pointer to int

    罗列一些复杂的c声明以及解释供学习:

    float ( * ( *b()) [] )();              // b is a function that returns a 
                                           // pointer to an array of pointers
                                           // to functions returning floats.
    
    void * ( *c) ( char, int (*)());       // c is a pointer to a function that takes
                                           // two parameters:
                                           //     a char and a pointer to a
                                           //     function that takes no
                                           //     parameters and returns
                                           //     an int
                                           // and returns a pointer to void.
    
    void ** (*d) (int &, char **(*)(char *, char **));   // d is a pointer to a function that takes
                                           // two parameters:
                                           //     a reference to an int and a pointer
                                           //     to a function that takes two parameters:
                                           //        a pointer to a char and a pointer
                                           //        to a pointer to a char
                                           //     and returns a pointer to a pointer 
                                           //     to a char
                                           // and returns a pointer to a pointer to void
    
    float ( * ( * e[10]) (int &) ) [5];    // e is an array of 10 pointers to 
                                           // functions that take a single
                                           // reference to an int as an argument 
                                           // and return pointers to
                                           // an array of 5 floats.
    
    

    6. 推荐阅读

    欢迎关注我的公众号:

  • 相关阅读:
    IIS7.5应用程序池集成模式和经典模式的区别介绍(转)
    (转)Extjs4 展示图片与图片的更新
    webservices发布后远程无法调试的解决方法
    .Net通过OleDb方式读取Excel
    1000个JQuery插件(转载)
    2006年中科大计算机考研复试机试题
    2. 最好使用C++转型操作符
    16. 谨记8020法则
    英语阅读理解中表示作者态度的词汇汇总
    5. 对定制的“类型转换函数”保持警觉
  • 原文地址:https://www.cnblogs.com/CodeWorkerLiMing/p/14880149.html
Copyright © 2011-2022 走看看