复杂的C语言标志

曾经碰到过让你迷惑不解、类似于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.

C语言实现冒泡排序优化算法详解与实战 在C语言中,可以使用整型变量模拟布尔逻辑(通常以0表示false,非0表示true该标志位应在每轮外层循环开始前被重置为0,确保其状态不被上一轮影响。具体实现如下:int flag;// 定义标志位变量i < n - 1;// 每轮开始前初始化为0,表示尚未发生交换j++) {// 发生交换flag = 1;// 设置标志位为1,表示发生了交换上述代码片段展示了标志位的基本使用流程。变量flag。 阅读详情

相关推荐

如何理解c和c++的复杂类型声明[定义].pdf

如何理解c和c++的复杂类型声明[定义].pdf

C++ 概述

C++ 概述

SpriCoder的博客 519

DW1000芯片实战:如何用UWB技术打造10厘米精度的室内定位系统(附Arduino代码)

本文是一份基于DW1000芯片和UWB技术的实战指南,详细介绍了如何从零构建一个精度可达10厘米的室内定位系统。内容涵盖硬件选型与连接、软件环境配置、核心的双向测距算法实现,并深入探讨了信号干扰抑制、功耗优化等实战挑战,为物联网开发者提供了完整的Arduino代码和部署思路。

root9的博客 497

C++右值引用浅析

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

dashishao9295的博客 217

Elsevier期刊LaTeX双栏模板(cas-dc)实战:从Overleaf配置到投稿全流程

本文详细介绍了Elsevier期刊LaTeX双栏模板(cas-dc)在Overleaf平台上的配置与使用全流程,包括模板结构解析、标题与作者信息规范、摘要与关键词特殊格式、正文写作技巧、参考文献管理以及投稿前检查清单。特别针对Elsevier期刊投稿需求,提供了从模板安装到最终投稿的实用指南,帮助研究者高效完成论文排版工作。

weixin_28455801的博客 1323

一文说清Arduino如何实现舵机平滑转动(机器人场景)

深入讲解如何利用arduino控制舵机转动,实现机器人场景中的平稳运动。通过代码优化与脉冲宽度调节,让舵机动作更精准流畅,适用于各类自动化项目。

weixin_35706255的博客 899

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

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

一辈子的孤单 936

【超详细】安装了anaconda后,Ubuntu18+COLMAP配置疯狂踩坑踩至魔怔的记录

本博客收集了博主在Ubuntu18系统中编译COLMAP所遇到的困难,提供了解决方法,也收集了目前网上大多数的坑,除此之外,也为Ubuntu18系统下COLMAP的安装提供了一种新的路径。

Stay_Foo_lish的博客 3509

C++速成(第五期)

本期我们学while~

Ender_momo的博客 524

【RT-Thread】从零到一:RT-Thread Studio工程创建与程序下载全流程实战

本文详细介绍了RT-Thread Studio开发环境的安装与使用,从工程创建到程序下载的全流程实战指南。通过具体案例演示了如何在STM32平台上快速开发RT-Thread应用,包括时钟配置、内存优化、构建调试等关键技巧,帮助开发者高效完成嵌入式系统开发。

weixin_30336061的博客 541

2-6-1-1 QNX编程入门之进程和线程(一)

文章围绕进程和线程展开介绍。先是将进程类比为房子,线程类比为居住者,阐述其基本概念,如进程占用内存,线程可共享内存。接着讲解同步相关内容,介绍了互斥,线程通过互斥锁来控制对特定资源的访问,像使用浴室一样。还提到优先级,线程按优先级和等待时长来决定获取资源顺序。此外,说明了信号量,以厨房为例,区分计数为 1 和大于 1 的信号情况,且指出信号量可当互斥锁用,但互斥锁是特殊用途信号量,二者存在区别,最后提及后续会介绍其他同步机制。

星原飞火的博客 1323

C++入门——Day4_循环和关系表达式

C++入门——Day4_循环和关系表达式

leikang111的博客 853

用python画带有正负值的条形图

常见的条形图只能表现数据的大小,却不能表现值的正负关系。但是其实仅仅使用`pandas`和`matplotlib`两个包就可以实现y轴负轴的添加以及负值的显示,并且可以为正负值分别设置不同的颜色,让条形图看上去更加清晰,易于我们分析

weixin_44853840的博客 5382

C语言sb代码,复杂的C语言标志

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

weixin_34634720的博客 15万+

LeRobot X-VLA:首个适用于任意机器人、任意任务的软提示机器人基础模型

X-VLA是一种基于软提示的视觉-语言-动作模型,通过可学习嵌入向量编码不同机器人配置,实现跨平台统一控制。该框架采用两阶段训练:先在29万次多平台任务中学习通用策略,再通过微调少量参数(仅1%)适配新机器人。实验表明,0.9B参数的X-VLA在6个仿真和3个真实平台上表现优异,如LIBERO任务达93%成功率,布料折叠任务100%完成。其核心创新是将硬件差异视为"任务",用软提示引导Transformer处理异构数据,为机器人基础模型提供可扩展方案。

2506_90492529的博客 1241

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

冒泡排序--C语言

​ 冒泡排序(Bubble Sort)是一种简单的排序算法,通过重复地遍历待排序的列表,比较相邻元素并交换它们的位置来完成排序。

2301_79273939的博客 2506

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

C语言冒泡排序优化实战(仅需4步显著降低时间复杂度)

掌握C语言实现冒泡排序优化版本,4步显著降低时间复杂度。适用于数据量较小且需稳定排序的场景,通过标志位判断和提前终止机制提升效率。代码简洁易懂,兼顾可读性与性能,值得收藏。

SimSolve的博客 625

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

《C和指针》笔记15: C语言跳出外循环的方式

《C和指针》笔记15: C语言跳出外循环的方式

subtitle_的博客 559

数组中标志位的使用

数组标志量的运用 在我们编写程序的过程中,会利用很多的表达式来对我们的数据进行不同程度的限定。虽然表达式的书写具有一定的规律性,但这么多的表达式,一定会使得算法的运行效率得不到保证。下面,通过对数组标志量的使用来记录数据是否有重复的现象,避免了许多复杂逻辑表达式的使用。 题目: 输入N个整数及一个K值(整数)。打印K值第一次出现是第几个数,如果不出现打印-1。 思路分析: (1)涉及到多...

yo_u_niverse的博客 1014

筛选N以内的素数(C语言描述)

#include<stdio.h> int main() { int i,j,n,flag; scanf("%d",&n); for(i=2;i<=n;i++) { flag=1; for(j=2;j*j<=i;j++) if(i%j==0){ flag=1; break; } if(flag==1) printf("%d\n",i); ...

weixin_45768308的博客 732

语言必学算法:冒泡排序超详细讲解,从原理到优化一次吃透

学 C 语言,绕不开的第一个算法就是冒泡排序。它简单、经典、面试常问,也是所有新手建立 “算法思维” 的第一道门。今天我把冒泡排序从原理→代码→优化→场景一次性讲透

yangda1111的专栏 339

冒泡排序算法的 C 语言实现与经验分享

例如,对于一个有 n 个元素的数组,第一轮需要比较 n - 1 次,第二轮需要比较 n - 2 次,以此类推,总的比较次数为 (n - 1)+(n - 2)+...+1=\frac{n(n - 1)}{2},这是一个关于 n 的二次函数,所以时间复杂度为 O(n^2)。当一轮遍历结束后,如果标志位仍然是 0,说明数组已经有序,就可以提前结束排序过程,避免不必要的遍历。- 在最好情况下,数组已经是有序的,那么只需要进行 n - 1 次比较,不需要进行交换操作,时间复杂度为 O(n)。

2402_89640902的博客 908

C语言中的冒泡排序

从第二行开始,每行的结果都是上一行被标记的两个数字进行大小比较之后的顺序,两个数中较大数字居右,较小数字居左,如此往复9次便可以找出这10个数字中的最大值并使其居于最右侧,接下来进行第二次冒泡排序找出剩下8个数字中的最大值,第三次找出剩下7个数字中的最大值......直到第九次比较出所剩最后两个数字的大小,便完成了完整的排序过程,得到该组数据的升序排序。如对数据{9,0,1,2,3,4,5,6,7,8}进行升序排序,只需要进行一次冒泡排序即可,但是上述代码需要进行九次,加大了计算机的运算量。

YiiiiiiiiYiii的博客 695

C语言的printf输出格式控制

***C语言的printf输出格式控制&nbsp;*** printf大家都耳熟能详,但是能真正将其用法弄透的估计很少见。&nbsp;转一篇,改天整理。&nbsp; 1.转换说明符&nbsp; %a(%A)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;浮点数、十六进制数字和p-(P-)记数法(C99)&nbsp; %c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp

SupreEvi的博客 468

从零开始完成冒泡排序(0基础)——C语言版

冒泡排序面向C语言小白

日常分享C/C++相关的知识 2535

冒泡排序太慢?,教你用C语言实现工业级优化版本并大幅提升效率

提升排序效率?本文详解C语言实现冒泡排序优化版本,通过减少无效比较和提前终止机制,在小规模或近有序数据场景显著提速。代码简洁易懂,兼顾可读性与性能,值得收藏。

DebugVibe的博客 977
上一篇: 一个IP建多个Web站点
下一篇: 暴笑口误2007酷热版别笑晕过去了
普通网友
博客等级 码龄21年 1粉丝 6原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值