如何解释复杂的 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. 推荐阅读

欢迎关注我的公众号:

如何解释复杂的 C/C++ 声明

Original: https://www.cnblogs.com/CodeWorkerLiMing/p/14880149.html
Author: wdliming
Title: 如何解释复杂的 C/C++ 声明

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/536479/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • C++ *和&

    概述 在c++中,当申明变量int *p 的时,表示p是一个储存地址的变量;比如int _p=0,表示p指向地址为00000000的地址单元。当申明指针p之后,再用_p表示p指向的…

    C++ 2023年5月29日
    051
  • C#调用C++的dll两种方法(托管与非托管)

    C#与C++交互,总体来说可以有两种方法: 利用PInvoke实现直接调用 非托管C+ 利用C++/CLI作为代理中间层 一、非托管C++ 由于C#编写的是托管代码,编译生成微软中…

    C++ 2023年5月29日
    041
  • Google C++ 单元测试 GTest

    from : http://www.cnblogs.com/jycboy/p/6057677.html 一、设置一个新的测试项目 在用google test写测试项目之前,需要先编…

    C++ 2023年5月29日
    029
  • 【C++】第1章 在VS2015中用C++编写控制台应用程序

    分类:C++、VS2015 创建日期:2016-06-12 看到不少人至今还在用VC 6.0开发工具学习C++,其实VC 6.0开发工具早就被淘汰了。这里仅介绍学习C++时推荐使用…

    C++ 2023年5月29日
    062
  • 收藏的博客 — Qt/C++学习

    Qt Creator环境: 使用Qt Creator作为Linux IDE,代替Vim:实现两台Linux电脑远程部署和调试(一台电脑有桌面系统,一台电脑无桌面系统) 使用Qt C…

    C++ 2023年5月29日
    041
  • C++/服务器开发4天实战训练营

    第一天: 1.四种不同的方式来实现add函数 //面向过程 int add1(int a, int b) { return a + b; } //面向对象 class ADD{ p…

    C++ 2023年5月29日
    036
  • EclipseC++学习笔记-5 隐藏cmd窗口

    每次启动eclipse后,会有一个cmd窗口,很影响整洁解决办法示例:powershell.exe -WindowStyle Hidden -c wsl — /root…

    C++ 2023年5月29日
    062
  • C++11 正则表达式——基础知识介绍

    C++11开始支持正则表达式,使得处理文本更加简洁方便。C++11 支持六种正则表达式语法:ECMAScript, basic(POSIX Basic Regular Expres…

    C++ 2023年5月29日
    031
  • UNITY 手游(安卓)如何使用C/C++代码

    解决方案:将C/C++代码编译成so供C#代码调用。 SO生成工具:android studio,简称AS 一,so 生成方法: 1,菜单:File->New->New…

    C++ 2023年5月29日
    063
  • 您的第一个C++Builder程序(Hello, world!)

    最近有些老旧的项目是C++Builder开发的,虽然和Delphi的IDE的界面和操作十分相似,但是还是找本《C++ Builder 5 Developer’s Gui…

    C++ 2023年5月29日
    054
  • Cannot find a C++ compiler that supports both C++11 and the specified C++ flags.

    Linux 安装 cmake 时候出现的问题,解决方法: yum install gcc-c++ Original: https://www.cnblogs.com/hunttow…

    C++ 2023年5月29日
    043
  • C++菜鸟经验:如何有效地避免各种不期而遇的Bug

    本文展示了笔者在编写 C++程序中遇到的问题和解决方案。文中附有大量有用的代码,这些代码往往都可以不加修改的添加进你自己的函数包中。你可能不能在其他的书上找到这些写法,因为这些都是…

    C++ 2023年5月29日
    038
  • C++C#联合调试

    https://blog.csdn.net/BYH371256/article/details/79815097 Original: https://www.cnblogs.com…

    C++ 2023年5月29日
    055
  • C++ Memory Order

    为什么需要Memory Order 原子操作(简单语句,C++也不保证是原子操作) 指令执行顺序(编译器可能优化代码使代码顺序发生变化,CPU可能会调整指令执行顺序) CPU可见性…

    C++ 2023年5月29日
    055
  • C++面试题1

    1,LeetCode给出一个 32 位的有符号整数,将这个整数中每位上的数字进行反转; 2,怎么判断一个变量是指针; Original: https://www.cnblogs.c…

    C++ 2023年5月29日
    051
  • C++常用的设计模式

    单例模式: 单例模式:确保一个类只有一个实例,并且这个实例化向整个系统提供 (例如只有一台打印机,可以有多个打印任务队列,但是只能有一个正在打印)。单例模式又分为(饿汉模式,懒汉模…

    C++ 2023年5月29日
    041
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球