[转载]教你理解复杂的C/C++声明

如何理解c和c++的复杂类型声明[定义].pdf 立即下载
介绍

曾经碰到过让你迷惑不解、类似于int * (* (*fp1) (int) ) [10];这样的变量声明吗?本文将由易到难,一步一步教会你如何理解这种复杂的C/C++声明:我们将从每天都能碰到的较简单的声明入手,然后逐步加入const修饰符和typedef,还有函数指针,最后介绍一个能够让你准确地理解任何C/C++声明的“右左法则”。需要强调一下的是,复杂的C/C++声明并不是好的编程风格;我这里仅仅是教你如何去理解这些声明。注意:为了保证能够在同一行上显示代码和相关注释,本文最好在至少1024x768分辨率的显示器上阅读。



基础

让我们从一个非常简单的例子开始,如下:

int n;

这个应该被理解为“declare n as an int”(n是一个int型的变量)。

接下去来看一下指针变量,如下:

int *p;

这个应该被理解为“declare p as an int *”(p是一个int *型的变量),或者说p是一个指向一个int型变量的指针。我想在这里展开讨论一下:我觉得在声明一个指针(或引用)类型的变量时,最好将*(或&)写在紧靠变量之前,而不是紧跟基本类型之后。这样可以避免一些理解上的误区,比如:

int* p,q;

第一眼看去,好像是p和q都是int*类型的,但事实上,只有p是一个指针,而q是一个最简单的int型变量。

我们还是继续我们前面的话题,再来看一个指针的指针的例子:

char **argv;

理论上,对于指针的级数没有限制,你可以定义一个浮点类型变量的指针的指针的指针的指针...

再来看如下的声明:

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

这里,p被声明为一个指向一个4元素(int类型)数组的指针,而q被声明为一个包含5个元素(int类型的指针)的数组。

另外,我们还可以在同一个声明中混合实用*和&,如下:

int **p1; // p1 is a pointer to a pointer to an int.
int *&p2; // p2 is a reference to a pointer to an int.
int &*p3; // ERROR: Pointer to a reference is illegal.
int &&p4; // ERROR: Reference to a reference is illegal.

注:p1是一个int类型的指针的指针;p2是一个int类型的指针的引用;p3是一个int类型引用的指针(不合法!);p4是一个int类型引用的引用(不合法!)。



const修饰符

当你想阻止一个变量被改变,可能会用到const关键字。在你给一个变量加上const修饰符的同时,通常需要对它进行初始化,因为以后的任何时候你将没有机会再去改变它。例如:

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

上述两个变量n和m其实是同一种类型的--都是const int(整形恒量)。因为C++标准规定,const关键字放在类型或变量名之前等价的。我个人更喜欢第一种声明方式,因为它更突出了const修饰符的作用。

当const与指针一起使用时,容易让人感到迷惑。例如,我们来看一下下面的p和q的声明:

const int *p;
int const *q;

他们当中哪一个代表const int类型的指针(const直接修饰int),哪一个代表int类型的const指针(const直接修饰指针)?实际上,p和q都被声明为const int类型的指针。而int类型的const指针应该这样声明:

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

这里,p和q都是指向const int类型的指针,也就是说,你在以后的程序里不能改变*p的值。而r是一个const指针,它在声明的时候被初始化指向变量n(即r=&n;)之后,r的值将不再允许被改变(但*r的值可以改变)。

组合上述两种const修饰的情况,我们来声明一个指向const int类型的const指针,如下:

const int * const p=&n // n has been declared as 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

注:p1是指向char类型的指针的指针;p2是指向const char类型的指针的指针;p3是指向char类型的const指针;p4是指向const char类型的const指针;p5是指向char类型的指针的const指针;p6是指向const char类型的指针的const指针;p7是指向char类型const指针的const指针;p8是指向const char类型的const指针的const指针。



typedef的妙用

typedef给你一种方式来克服“*只适合于变量而不适合于类型”的弊端。你可以如下使用typedef:

typedef char * PCHAR;
PCHAR p,q;

这里的p和q都被声明为指针。(如果不使用typedef,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经常用在一个结构声明之前,如下。这样,当创建结构变量的时候,允许你不使用关键字struct(在C中,创建结构变量时要求使用struct关键字,如struct tagPOINT a;而在C++中,struct可以忽略,如tagPOINT b)。

typedef struct tagPOINT
{
int x;
int y;
}POINT;

POINT p; /* Valid C code */



函数指针

函数指针可能是最容易引起理解上的困惑的声明。函数指针在DOS时代写TSR程序时用得最多;在Win32和X-Windows时代,他们被用在需要回调函数的场合。当然,还有其它很多地方需要用到函数指针:虚函数表,STL中的一些模板,Win NT/2K/XP系统服务等。让我们来看一个函数指针的简单例子:

int (*p)(char);

这里p被声明为一个函数指针,这个函数带一个char类型的参数,并且有一个int类型的返回值。另外,带有两个float类型参数、返回值是char类型的指针的指针的函数指针可以声明如下:

char ** (*p)(float, float);

那么,带两个char类型的const指针参数、无返回值的函数指针又该如何声明呢?参考如下:

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



“右左法则”[重要!!!]

The right-left rule: Start reading the declaration from the innermost parentheses, go right, and then go left. When you encounter parentheses, the direction should be reversed. Once everything in the parentheses has been parsed, jump out of it. Continue till the whole declaration has been parsed.

这是一个简单的法则,但能让你准确理解所有的声明。这个法则运用如下:从最内部的括号开始阅读声明,向右看,然后向左看。当你碰到一个括号时就调转阅读的方向。括号内的所有内容都分析完毕就跳出括号的范围。这样继续,直到整个声明都被分析完毕。

对上述“右左法则”做一个小小的修正:当你第一次开始阅读声明的时候,你必须从变量名开始,而不是从最内部的括号。

下面结合例子来演示一下“右左法则”的使用。

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

阅读步骤:
1. 从变量名开始 -------------------------------------------- fp1
2. 往右看,什么也没有,碰到了),因此往左看,碰到一个* ------ 一个指针
3. 跳出括号,碰到了(int) ----------------------------------- 一个带一个int参数的函数
4. 向左看,发现一个* --------------------------------------- (函数)返回一个指针
5. 跳出括号,向右看,碰到[10] ------------------------------ 一个10元素的数组
6. 向左看,发现一个* --------------------------------------- 指针
7. 向左看,发现int ----------------------------------------- int类型


总结:fp1被声明成为一个函数的指针,该函数返回指向指针数组的指针.


再来看一个例子:

int *( *( *arr[5])())();

阅读步骤:
1. 从变量名开始 -------------------------------------------- arr
2. 往右看,发现是一个数组 ---------------------------------- 一个5元素的数组
3. 向左看,发现一个* --------------------------------------- 指针
4. 跳出括号,向右看,发现() -------------------------------- 不带参数的函数
5. 向左看,碰到* ------------------------------------------- (函数)返回一个指针
6. 跳出括号,向右发现() ------------------------------------ 不带参数的函数
7. 向左,发现* --------------------------------------------- (函数)返回一个指针
8. 继续向左,发现int --------------------------------------- int类型

总结:??


还有更多的例子:

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.
2025反爬终极方案:Python破解Cloudflare v2行为指纹,设备伪装+轨迹模拟拦截率压至1% Cloudflare v2的反爬逻辑越来越接近“人类行为识别”,单纯的技术破解已失效,核心竞争力变成“模拟真实用户的能力”。本文的终极方案本质是“全方位复刻人类行为”——从设备指纹到操作轨迹,从网络特征到交互模式,让爬虫成为“无法被区分的真实用户”。这套方案的优势是“通用性强”,不仅适用于Cloudflare v2,也能破解大多数高反爬网站(如小红书、知乎、跨境电商)的行为检测;且代码可直接复用,新手只需替换目标URL和数据提取逻辑,就能快速落地。 阅读详情

相关推荐

Vivado SDK中LWIP库版本冲突与RTL8211FDI兼容性实战解析

本文深入解析Vivado 2019.1 SDK中LWIP 1.0库与RTL8211FDI千兆PHY芯片的版本冲突问题,并提供实战解决方案。通过手动移植高版本LWIP库、整合第三方驱动及优化BSP配置,有效解决了以太网通信兼容性问题,确保千兆性能稳定运行。

ios99的博客 272

C++ 概述

C++ 概述

SpriCoder的博客 519

C#与AutoCAD插件开发实战:从零构建你的第一个自定义命令

本文为AutoCAD插件开发新手提供了一份完整的C#实战指南。从搭建Visual Studio开发环境、添加AutoCAD .NET API引用,到编写并注册第一个“Hello AutoCAD”自定义命令,再到实现一个交互式绘制十字线的实用命令,详细讲解了核心代码结构与调试技巧。文章旨在帮助工程师和设计师利用C#自动化重复绘图任务,显著提升工作效率。

weixin_29294597的博客 434

C++右值引用浅析

一直想试着把自己理解和学习到的右值引用相关的技术细节整理并分享出来,希望能够对感兴趣的朋友提供帮助。 右值引用是C++11标准中新增的一个特性。右值引用允许程序员可以忽略逻辑上不需要的拷贝;而且还可以用来支持实现完美转发的函数。它们都是实现更高效、更健壮的库。 move语义 先不展开具体右值引用定义。先说说move语义。右值引用是用来支持move语义的。move语义是指将一个同类型...

dashishao9295的博客 217

阿里巴巴-AI Agent工程师面试题精选:10道高频考题+答案解析(附PDF)

阿里巴巴AIAgent技术面试要点摘要(149字) 阿里巴巴在AIAgent领域布局完整,面试重点考察系统架构、幻觉治理、成本优化等核心能力。候选人需掌握分层架构设计、多Agent协作死锁处理、Token成本优化策略等关键技术。面试强调工程落地能力,包括工具调用容错、记忆系统设计、安全防护等实践要点。评估体系需覆盖效果、效率、成本多维度指标。未来趋势关注垂直领域专业化、多模态融合等方向。建议准备实际项目案例,突出技术深度与业务价值的结合,展现系统思维和工程能力。

ZHHHHH15的博客 615

【VMware】win 10:VMware 15 虚拟机安装 win 7 系统

目录 一、准备虚拟机 二、win7 ghost ISO镜像文件下载 三、VMware新建虚拟机 四、VMware安装win7 五、解决vmware虚拟机屏幕没有适应窗口全屏问题 一、准备虚拟机 查看我的另一博文:【VMware】VMware Workstation Pro15 虚拟机永久激活破解安装教程 二、win7 ghost ISO镜像文件下载 百度网盘下载地址:点击前往下载...

顾三殇 —— 博客空间(软件测试) 11万+

教你理解复杂的C/C++声明

  教你理解复杂的C/C++声明陆其明 译 原文:http://www.codeproject.com/cpp/complex_declarations.asp作者:Vikram A Punathambekar 介绍 曾经碰到过让你迷惑不解、类似于int * (* (*fp1) (int) ) [10];这样的变量声明吗?本文将由易到难,一步一步教会你如何理解这种复杂的C/C++声明:我们将从每天

一辈子的孤单 936

【8】mctp & pldm

异步会check seq,保证不丢包,虽然发一条req,不用等,但是不能发下一条req,等上条response回来后才能发下一条。TO(tag owner)=1是request,TO=0是response,但是异步通信response由0改为1。如下还有kcs,i3c,serial等。

持续更新ing... 1080

C++速成(第五期)

本期我们学while~

Ender_momo的博客 524

【WRF-Urban】使用 CGLC-MODIS-LCZ_100m 数据集运行 WRF 时的城市参数化问题

【WRF-Urban】使用 CGLC-MODIS-LCZ_100m 数据集运行 WRF 时的城市参数化问题

WW、forever的博客 709

【即插即用模块】注意力篇 | ECCV 2024 | DPB:高低频动态分离+双提示融合,PSNR直冲40+!

本文提出了一种基于频率提示的图像恢复方法FPro,通过动态滤波器分离输入特征的高低频分量,并采用双提示块(DPB)进行频域专属提示生成与调制。DPB包含低频提示调制器(LPM)和高频提示调制器(HPM),分别处理不同频段信号,通过生成提示组件并融合解码器特征来优化恢复效果。实验表明,该方法在U型结构中表现优异,尤其适用于分割、底层视觉和多模态任务。模块通过动态无监督频率分离、频域专属提示生成和适配性融合机制,有效解决了传统方法对频域信息关注不足的问题,显著提升了图像恢复质量。

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 147

【C++】C++中的枚举enum类型使用详解

枚举(enumeration)和类相似,能够定义一种新的数据类型,不同的是,枚举是将一组整形常量组织在一起,所以和类的使用方法有一些类似之处。1.不限定作用域的枚举enum colorRED,GREEN,BLUE。

qq_35902025的博客 9万+

使用MATLAB和Simulink来设计并仿真一个智能家居多传感器数据融合系统

智能家居多传感器数据融合系统旨在通过集成来自多种不同类型的传感器的数据,以提供更精确和可靠的环境信息或用户状态监测。例如,将温度、湿度、光照强度、运动检测等多种传感器的数据结合起来,可以更好地了解家庭内部的状态,并做出相应的调整或响应。在这个教程中,我们将使用MATLAB和Simulink来设计并仿真一个智能家居多传感器数据融合系统。

amy_mhd的博客 189

如何阅读C/C++中复杂声明(转载)

原创: 上善若水  码农有道  2017-10-09 我们经常遇到一些复杂的指针类型说明,很多初学者可能感觉有点蒙,即使是非常有经验的C/C++程序员,也会对那些比较复杂些的声明感到头疼。例如,下面这个是一个包含指针的数组,还是一个指向数组的指针呢 int *a[10]; 好了,这个并不是特别复杂的声明,你可能快速而又准确的说出你的答案:一个含有10个指针的数组。 那么,来一个更具挑战性的例子,...

wWX336815的博客 442

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

1.深入理解C/C++中头文件(.h)与源文件(.c/.cpp)以及我们为什么需要.h头文件

1.深入理解C/C++中头文件(.h)与源文件(.c/.cpp)以及我们为什么需要.h头文件 本篇文章灵感来自于:https://www.cnblogs.com/laojie4321/archive/2012/03/30/2425015.html 本文章中所有表情包均来自互联网,如有侵权,劳请联系。 另外,转载请标明出处哦,语雀:https://www.yuque.com/yifeideshijie 上面的文章很清楚地解析了在整个项目编译过程中的过程,头文件(通常的.h文件)和源文...

qq_43746863的博客 8843

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

教你理解复杂的C-C++声明(转)

教你理解复杂的C-C++声明(转) 教你理解复杂的C-C++声明(转) - arely - 博客园教你理解复杂的C-C++声明(转) 教你理解复杂的C-C++声明本篇文章为转载,但原文链接...

111

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

c/c++复杂声明的理解

c/c++复杂声明的理解 一直被c/c++的复杂声明搞得头晕。特地找了一些资料学习了一下,总结如下: 理解复杂声明可用的“右左法则”:从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方...

aneqqix79573的博客 169

[转载2]复杂的变量的声明

陆其明 译 原文: http://www.codeproject.com/cpp/complex_declarations.asp 作者:Vikram A Punathambekar 介绍 曾经碰到过让你迷惑不解、类似于int * (* (*fp1) (int) ) [10];这样的变量声明吗?本文将由易到难,一步一步教会你如何理解这种复杂的C/C++声明:我们将从每天都能碰到的较简单

sky911911的专栏 790

C++ 理解复杂声明

C++ 理解复杂声明 本文转载于https://www.cnblogs.com/BinarySong/p/12913504.html C和C++中的复杂声明往往难以理解,这里为大家总结出理解它们的系统化方法。 前置知识 1.声明 C++标准对声明的定义是:声明规定名字将如何被解释。一个声明只解释一个名字。这个名字是声明的“主语”,是描述的中心,其余的名字都并非重点。例如: int fun(int a); 这个声明包含两个名字fun和a。然而只有fun是“主语”。后面的部分会阐述如何确定这唯一的“主语”。

qq_36402529的博客 261

如何理解c和c++的复杂类型声明(转载)

声明:此文章是我转载的 曾经碰到过让你迷惑不解、类似于int * (* (*fp1) (int) ) [10];这样的变量声明吗?本文将由易到难,一步一步教会你如何理解这种复杂的C/C++声明。   我们将从每天都能碰到的较简单的声明入手,然后逐步加入const修饰符和typedef,还有函数指针,最后介绍一个能够让你准确地理解任何C/C++声明的“右左法则”。   需要强调一下的

zhoujie676215665的专栏 940

如何理解c和c ++的复杂类型声明- -(转载)

曾经碰到过让你迷惑不解、类似于int * (* (*fp1) (int) ) [10];这样的变量声明吗?本文将由易到难,一步一步教会你如何理解这种复杂的C/C++声明。   我们将从每天都能碰到的较简单的声明入手,然后逐步加入const修饰符和typedef,还有函数指针,最后介绍一个能够让你准确地理解任何C/C++声明的“右左法则”。   需要强调一下的是,复杂的C/C++声明并不是好的编程风

E.N.O 989

[转载]教你理解复杂的C-C++声明

教你理解复杂的C-C++声明 原文: http://www.codeproject.com/cpp/complex

计算机视觉&机器视觉 909

(转载)如何理解c和c++ 的复杂类型声明

曾经碰到过让你迷惑不解、类似于int * (* (*fp1) (int) ) [10];这样的变量声明吗?本文将由易到难,一步一步教会你如何理解这种复杂的C/C++声明。   我们将从每天都能碰到的较简单的声明入手,然后逐步加入const修饰符和typedef,还有函数指针,最后介绍一个能够让你准确地理解任何C/C++声明的“右左法则”。   需要强调一下的是,复杂的C/C++声明

井中月的专栏 460
上一篇: (转载)C/C++头文件一览
下一篇: [转载]深入探讨C++中的引用
csdboy
博客等级 码龄25年 0粉丝 19原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值