C++中指针和数组相关的运算符优先级
概述
本文深入介绍了与指针和数组相关的运算符优先级,利用代码示例展示了当左结合和右结合运算符同时存在时的结合方式,同时也演示了如何使用()来强制人为指定结合顺序。
指针、数组相关的运算符优先级
下表展示了相关运算符的优先级,有4个级别,同级别内的运算符按照结合性依次调用。这4类也是所有运算符中优先级最高的4档,其它的运算符优先级都比它们低:
| 优先级 | 运算符 | 描述 | 结合性 |
|---|---|---|---|
| 1 | :: | 作用域解析 | 左结合 |
| 2 | () [] . -> |
强制运算结合,函数形参列表 数组元素下标访问 类的成员访问 类指针的成员访问 |
右结合 |
| 3 | (int) * & |
强制转换 指针解引用 变量取地址 |
左结合 |
| 4 | .* ->* |
类的成员指针 类指针的成员指针 |
左结合 |
容易产生困惑的、需要仔细进行优先级判断的往往是一个左结合加一个右结合,例如:
*ptr[](int)a()&class->dataobj->*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!!!
C++中指针和数组相关的运算符优先级的更多相关文章
- C语言中指针和数组
C语言数组与指针的那些事儿 在C语言中,要说到哪一部分最难搞,首当其冲就是指针,指针永远是个让人又爱又恨的东西,用好了可以事半功倍,用不好,就会有改不完的bug和通不完的宵.但是程序员一般都有一种迷之 ...
- 指针、数组与sizeof运算符
指针.数组与sizcof运算符 (1)sizeof是c语言的一个运算符(主要sizeof不是函数,虽然用法很像函数),sizeof的作用是用来返同()里面的变量或者数据类型占用的内存字节数. (2)s ...
- C语言中指针和数组的区别
看<C专家编程>一书,看到数组与指针并不相同一章,遂做了一段测试: 代码: #include <stdio.h> #include <stdlib.h> int m ...
- C语言中 指针和数组
C语言的数组表示一段连续的内存空间,用来存储多个特定类型的对象.与之相反,指针用来存储单个内存地址.数组和指针不是同一种结构因此不可以互相转换.而数组变量指向了数组的第一个元素的内存地址. 一个数组变 ...
- 上机实践 - - 一个例子了解C/C++中指针与数组的区别
本例子来自于<剑指Offer>(P37) 解答如下: size1:20 data1是一个数组,sizeof(data1)是求数组大小. 这个数组包含5个整数,每个整数4个字节,共20字节. ...
- 关于C++中的指针、数组
C++中指针和数组基本等价的原因在于指针算术和C++内部处理数组的方式:将整数变量加一后,其值将增加1:将指针变量加一后,增加的量等于其指向的数据类型的字节数: 指针中存储的是地址,地址在形式上和整数 ...
- 娓娓道来c指针 (3)指针和数组
(3)指针和数组 在c中指针和数组似乎有着千丝万缕的关系.事实上它们不是一回事:指针是指针,数组是数组.两者不同样. 说它们有关系,只是是由于常见这种代码: int main() { int arra ...
- 听翁恺老师mooc笔记(5)--指针与数组
如果我们通过函数的参数将一个数组传递到参数中去,那么在函数里接收到的是什么东西呢?我们知道如果传递一个普通变量,那么参数接收到的是值,如果传递一个指针变量,参数接收到的也是值,只不过这时的值是地址.那 ...
- C++指针和数组的区别(不能混用的情况)
通常情况下,C++中指针和数组是可以混用的,但是,在编写字符数组的全排列的时候,混用却出了问题,因此,今天特地mark一下,以备日后查找 这里整理的,不包括用new开辟的动态数组 1.数组一旦声明,我 ...
- C++指针与数组
对数组地址的理解,如 int c[2] = {2,3}; int(*cp)[2] = &c; cout << &c[0] << c << cp &l ...
随机推荐
- 解密prompt系列34. RLHF之训练另辟蹊径:循序渐进 & 青出于蓝
前几章我们讨论了RLHF的样本构建优化和训练策略优化,这一章我们讨论两种不同的RL训练方案,分别是基于过程训练,和使用弱Teacher来监督强Student 循序渐进:PRM & ORM So ...
- 我用Awesome-Graphs看论文:解读PowerGraph
PowerGraph论文:<PowerGraph: Distributed Graph-Parallel Computation on Natural Graphs> 上次通过文章< ...
- 【vue3】详解单向数据流,大家千万不用为了某某而某某了。
总览 Vue3 的单向数据流 尽信官网,不如那啥. vue的版本一直在不断更新,内部实现方式也是不断的优化,官网也在不断更新. 既然一切皆在不停地发展,那么我们呢?等着官网更新还是有自己的思考? 我觉 ...
- 工业AI制造:铝合金冲压、压铸工艺流程 —— 模具参数调整,以满足所需的规格和质量要求
压铸操作工艺流程作步骤: 模具安装 → 调试 →清理预热模具 → 喷刷涂料 → 合模 → 涂料准备 → 涂料配制 → 压铸 → 冷却与凝固 → 开模 → 顶出铸件 → 质量检验 → 成品 → 废品 → ...
- Python Pillow(PIL 第三方模块)和 cv2 (opencv第三方模块)对图片的 resize 操作 (缩放图片大小)
PIL 模块的 resize 操作: 1. 从文件中读取图片,然后 resize 大小: import matplotlib.pyplot as plt import numpy as np ...
- python版本的“共轭梯度法”算法代码
在看代码的过程中遇到了共轭梯度法这个概念,对这个算法的数学解释看过几遍,推导看过了,感觉懂了,然后过上一些日子就又忘记了,然后又看了一遍推导,然后过了一些日子也就又忘记了,最后想想这个算法的数学解释就 ...
- 模拟实现FutureTask
1.背景 面试官问,,假设让你编写一个FutureTask,你的思路是..... 2.代码 2.1.GuardedObject对象 package com.common; /** * @author ...
- springboot项目启动时禁止Redis、数据对象加载
1.背景 2.实现方式 启动类上添加需要排除的自动装配对象 @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, H ...
- BOM 相关知识总结
一:介绍BOM 1 1.什么是BOM? 2 DOM就是一套操作HTML标签的API(接口/方法/属性) 3 BOM就是一套操作浏览器的API(接口/方法/属性) 4 5 2.BOM中常见的对象 6 w ...
- .NET 轻量化定时任务调度 FreeScheduler
前言 在平时项目开发中,定时任务调度是一项重要的功能,广泛应用于后台作业.计划任务和自动化脚本等模块. FreeScheduler 是一款轻量级且功能强大的定时任务调度库,它支持临时的延时任务和重复循 ...