首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

C++中指针和数组相关的运算符优先级

编程知识
2024年10月01日 19:10

概述

本文深入介绍了与指针和数组相关的运算符优先级,利用代码示例展示了当左结合和右结合运算符同时存在时的结合方式,同时也演示了如何使用()来强制人为指定结合顺序。

指针、数组相关的运算符优先级

下表展示了相关运算符的优先级,有4个级别,同级别内的运算符按照结合性依次调用。这4类也是所有运算符中优先级最高的4档,其它的运算符优先级都比它们低:

优先级 运算符 描述 结合性
1 :: 作用域解析 左结合
2 ()
[]
.
->
强制运算结合,函数形参列表
数组元素下标访问
类的成员访问
类指针的成员访问
右结合
3 (int)
*
&
强制转换
指针解引用
变量取地址
左结合
4 .*
->*
类的成员指针
类指针的成员指针
左结合

容易产生困惑的、需要仔细进行优先级判断的往往是一个左结合加一个右结合,例如:

  • *ptr[]
  • (int)a()
  • &class->data
  • obj->*fun()

请记住一个重要方法:当我们需要改变运算符的结合顺序(C++默认的优先级不是我们的意愿)时,可以通过添加()来人为强制指定优先顺序,因为()是除了::以外具有最高优先级的一类运算符。

简单例子:以[]和*为例探讨运算符结合规律

下面的p1, p2是数组,p3是指针:

int *p1[2];    // p1是一个数组,元素个数为2,每个元素为(int*)
int *(p2[2]);  // 等价于*p2[2],p2是一个数组
int (*p3)[2];  // p3是一个指针,指向一个int数组,这个int数组的元素个数必须是2!

因此只要记住两点即可:

  • **[]的优先级高于* **:即*p1[2]*(p1[2])等价。
  • 这个优先级同时适用于定义语句(*为指针定义符)执行语句(*为解引用符)中:
int *p1[2];    // 定义语句:先看[]:p1是一个数组,元素个数为2,每个元素为(int*)。等价于*(p1[2])
int (*p2)[2];  // 定义语句:先看*: p2是一个指针,指向一个int数组,这个int数组的元素个数必须是2!
cout << "*p1[0] = " << *p1[0] << endl;     // 执行语句:先看[]:先取第0个元素,再解引用。等价于*(p1[0])
cout << "(*p2)[0] = " << (*p2)[0] << endl; // 执行语句:先看*:先解引用,再取第0个元素

完整示例:

#include <iostream>
using namespace std;

int main(){	
	// []的优先级高于*,因此下面的p1是数组,p2是指针:
	int *p1[2];    // p1是一个数组,元素个数为2,每个元素为(int*)。等价于*(p1[2])
	int (*p2)[2];  // p2是一个指针,指向一个int数组,这个int数组的元素个数必须是2!
	int a = 1, b = 2;
	int c[2] = {4,5};
	p1[0] = &a;
	p1[1] = &b;
	p2 = &c;
	cout << "*p1[0] = " << *p1[0] << endl;
	cout << "*p1[1] = " << *p1[1] << endl;
	cout << "*(p1[0]) = " << *(p1[0]) << endl;  // 与上面两条等价
	cout << "*(p1[1]) = " << *(p1[1]) << endl;
	cout << "(*p2)[0] = " << (*p2)[0] << endl;  
	cout << "(*p2)[1] = " << (*p2)[1] << endl;
	
	return 0;
}

输出:

*p1[0] = 1
*p1[1] = 2
*(p1[0]) = 1
*(p1[1]) = 2
(*p2)[0] = 4
(*p2)[1] = 5

复杂例子:探讨当左结合和右结合运算符同时存在时如何界定优先级

下面的例子比较复杂,需要耐心仔细阅读和体会。如果这个例子能搞清楚,那么相信你对运算符优先级的理解将会上升一个档次。

这个例子研究了当左结合和右结合运算符同时存在时的结合顺序,同时也演示了可以使用()强制人为指定结合顺序:

#include <iostream>
#include <string>
using namespace std;

class Student{
public:
	Student(string name, int id):_name(name),_id(id){}
	void printInfo(){
		cout << "I am a student. My name is " << _name << ". My id is " << _id << endl;
	}
	void operator()(){
		printInfo();
	}
protected:
	string _name;
	int _id;
};

class Student2 : public Student{
public:
	Student2(string name, int id):Student(name, id){}
	void printInfo(){
		cout << "I am a super Student!!! " << endl;
	}
	void operator()(){
		cout << "I am Student2!!!" << endl;
	}
};

struct StudentWrapper{
	Student* _ps;
	StudentWrapper(Student* ps):_ps(ps){}
	Student* operator()(){return _ps;}
};

int main(){	
	// .和(), ->和()平级:从左向右
	cout << "-----------------1------------------" << endl;
	Student s1("Bob",101), s2("Jack", 102);
	Student *ps1 = new Student("Eric",103);
	s1.printInfo();
	s2.printInfo();
	ps1->printInfo();

	// .高于*:先结合.
	cout << "-----------------2------------------" << endl;
	// 下面这条语句报错:先调用.printInfo(),再*,因此会报错
	// *ps1.printInfo();  // error: request for member 'printInfo' in 'ps1'
	(*ps1).printInfo(); 
			
	// .和()高于*:先结合()和.(从右向左),最后结合*
	cout << "-----------------3------------------" << endl;
	StudentWrapper sw(ps1);
	// 下面这条语句报错:先结合sw(),再结合.printInfo(),最后结合*,因此会报错
	// *sw().printInfo(); // error: request for member 'printInfo' in 'sw.StudentWrapper::operator()()'
	(*sw()).printInfo(); // correct:先sw(),再*sw(),再(*sw()).printInfo()
	// 下面这条语句报错:先结合sw(),再结合(),最后结合*,因此会报错
	// *sw()(); // error: expression cannot be used as a function
	(*sw())(); // correct:先sw(),再*sw(),再(*sw())()

	// (int)和()/[]:先结合()和[],再强转
	cout << "-----------------4------------------" << endl;
	Student2 ss("Alice", 999), sss("Jason", 998), ssArray[2] = {ss, sss};
	ss();   // 调用Student2::operator()
	// 下面这条语句报错,因为会先结合ss(),再强制转换
	// (Student)ss(); // error: invalid use of void expression
	((Student)ss)(); // correct: 调用Student::operator()
	// 下面这条语句报错,因为会先结合ssArray[0], 再ssArray[0](),再强制转换
	// (Student)ssArray[0](); // error: invalid use of void expression
	((Student)ssArray[1])(); // correct:将ssArray[1]强制转换为Student类型后,调用其()方法

	// ()高于.*和->*:先结合()
	cout << "-----------------5------------------" << endl;
	void (Student::*fp)();
	fp = Student::printInfo;
	// s1.*fp();   // error: must use '.*' or '->*' to call pointer-to-member function in 'fp (...)'
	(s1.*fp)();
	(s2.*fp)();
	// ps1->*fp(); // error: must use '.*' or '->*' to call pointer-to-member function in 'fp (...)'
	(ps1->*fp)(); 
	
	// (int)高于.*和->*:先结合(int)
	cout << "-----------------6------------------" << endl;
	Student2 *ssp = &sss;   // Jason
	void (Student2::*fp2)();
	fp2 = Student2::printInfo;
	(ss.*fp2)();
	((Student)ss.*fp)();  // 先将ss强转为Student,然后调用Student::printInfo(),注意是.*fp而不是.*fp2
	((Student*)ssp->*fp)();  // 先将ssp强转为Student*,然后调用Student::printInfo(),注意是.*fp而不是.*fp2

	// *高于.*和->*:先结合*
	cout << "-----------------7------------------" << endl;
	(*ssp.*fp2)();   // 先*ssp,再.*fp2
	Student2 **sspp = &ssp;
	(*sspp->*fp2)(); // 先*sspp,再->fp2
	
	delete ps1;
	return 0;
}

输出:

-----------------1------------------
I am a student. My name is Bob. My id is 101
I am a student. My name is Jack. My id is 102
I am a student. My name is Eric. My id is 103
-----------------2------------------
I am a student. My name is Eric. My id is 103
-----------------3------------------
I am a student. My name is Eric. My id is 103
I am a student. My name is Eric. My id is 103
-----------------4------------------
I am Student2!!!
I am a student. My name is Alice. My id is 999
I am a student. My name is Jason. My id is 998
-----------------5------------------
I am a student. My name is Bob. My id is 101
I am a student. My name is Jack. My id is 102
I am a student. My name is Eric. My id is 103
-----------------6------------------
I am a super Student!!!
I am a student. My name is Alice. My id is 999
I am a student. My name is Jason. My id is 998
-----------------7------------------
I am a super Student!!!
I am a super Student!!!
From:https://www.cnblogs.com/phyjack/p/18443179
本文地址: http://shuzixingkong.net/article/2439
0评论
提交 加载更多评论
其他文章 将ASD光谱仪的.asd文件转为文本文件
本文介绍基于ViewSpec Pro软件,将ASD地物光谱仪获取到的.asd格式文件,批量转换为通用的.txt文本格式文件的方法~
将ASD光谱仪的.asd文件转为文本文件 将ASD光谱仪的.asd文件转为文本文件 将ASD光谱仪的.asd文件转为文本文件
manim边学边做--形状匹配
manim中有几个特殊的用于形状匹配的对象,它们的作用是标记和注释已有的对象,本身一般不单独使用。 形状匹配对象一共有4种: BackgroundRectangle:为已有的对象提供一个矩形的背景 Cross:用交叉线标记已有对象 SurroundingRectangle:用矩形框围住某个对象 Un
manim边学边做--形状匹配 manim边学边做--形状匹配 manim边学边做--形状匹配
【防忘笔记】测试过程与技术
测试人员应该想些什么 我自己是做后端的,对于模棱两可的需求和莫名其妙的测试case是深恶痛绝的,所以有时候我就会想测试人员应该会需要注意什么?以他们的角度,他们更在乎什么 最近有机会了解相关的知识,遂整理记录一下,以便之后在工作中更好的理解发生的各种事情 以客户为中心 这个真的很重要,以至于可以直接
【防忘笔记】测试过程与技术 【防忘笔记】测试过程与技术 【防忘笔记】测试过程与技术
论文解读《MASTERKEY: Automated Jailbreaking of Large Language Model Chatbots》
导言 ​ 在参加东南大学网络安全学院夏令营的契机下,我第一次接触大模型安全领域。L老师是网络安全领域的一位大牛,在和L老师交流期间,被告知需要准备一次paper presentation介绍四大会中感兴趣的一篇文章,我选择了汇报这篇来自NDSS2024的《MASTERKEY: Automated J
论文解读《MASTERKEY: Automated Jailbreaking of Large Language Model Chatbots》 论文解读《MASTERKEY: Automated Jailbreaking of Large Language Model Chatbots》 论文解读《MASTERKEY: Automated Jailbreaking of Large Language Model Chatbots》
Salesforce AI Specialist篇之 Einstein Trust Layer
本篇参考: https://trailhead.salesforce.com/content/learn/trails/drive-productivity-with-einstein-ai https://help.salesforce.com/s/articleView?id=sf.genera
Salesforce AI Specialist篇之 Einstein Trust Layer Salesforce AI Specialist篇之 Einstein Trust Layer Salesforce AI Specialist篇之 Einstein Trust Layer
【Wing】背后的插件们
wing 作为我们日常开发的命令行开发工具,项目开源以来,陆陆续续接入了多个插件,在这里集中分享给大家。
【Wing】背后的插件们 【Wing】背后的插件们 【Wing】背后的插件们
Nuxt.js 应用中的 app:rendered 钩子详解
title: Nuxt.js 应用中的 app:rendered 钩子详解 date: 2024/10/2 updated: 2024/10/2 author: cmdragon excerpt: 摘要:本文详细介绍了 Nuxt.js 应用程序中的 app:rendered 钩子,包括其定义、调用时
Nuxt.js 应用中的 app:rendered 钩子详解 Nuxt.js 应用中的 app:rendered 钩子详解
《痞子衡嵌入式半月刊》 第 108 期
痞子衡嵌入式半月刊: 第 108 期 这里分享嵌入式领域有用有趣的项目/工具以及一些热点新闻,农历年分二十四节气,希望在每个交节之日准时发布一期。 本期刊是开源项目(GitHub: JayHeng/pzh-mcu-bi-weekly),欢迎提交 issue,投稿或推荐你知道的嵌入式那些事儿。 上期回
《痞子衡嵌入式半月刊》 第 108 期 《痞子衡嵌入式半月刊》 第 108 期 《痞子衡嵌入式半月刊》 第 108 期