C/C++ 下标运算符subscript、后缀表达式、正负下标

提问:如果我们要获得a数组的第二个元素,请问一下那种方式可以达到要求。

int a[] = {
    
     1,2,3,4,5,6 };
cout << a[1] << endl;
cout << *(a+1) << endl;
cout << 1[a] << endl;
cout << (1)[a] << endl;

答案是都可以

首先,ar[i]和*(ar+1)这两个表达式都是等价的,这里就不再多说了,而1[a](1)[a] 这是个什么东西,好像以前没见过啊。

对于数组而言, 要访问一个数组元素无非是 数组名[下标] ⇒ base[offset] // 基地址+偏移 ,因此[ ] 运算符与数组在一起时我们把它叫做下标运算符。

用于识别数组元素的数字被称为下标(subscript)、索引(indice)或
偏移量(offset)。下标必须是整数,而且要从0开始计数。数组的元素被依
次储存在内存中相邻的位置。

然而下标运算符还有另外的用法似乎一直被我们忽略了,下面将演示它的另外两种用法 前缀式负下标

下标运算符

参考:https://docs.microsoft.com/zh-cn/cpp/cpp/subscript-operator?view=msvc-170

下标运算符也可以称之为后缀表达式,因为它在使用时是一个数组名加一个后缀的形式。

postfix-expression [ expression ]

一般来说,后缀表达式 表示的值是一个指针值(如数组标识符),而 expression 是一个整数值 (包括) 枚举类型。 但是,从语法上来说,只需要一个表达式是指针类型,另一个表达式是整型

因此,我们可以用这样 下标[数组名] ⇒ offset[base] // 偏移基+地址 来表示数组中的某个数。而可以这样做的原因在MicroSoft Document给出了解释

下标表达式的结果 e1[e2] 是由指定的:
*((e2) + (e1))
.
表达式生成的地址不是来自地址e1的e2字节。 相反,地址将进行缩放以生成数组 e2中的下一个对象。

这里我们利用*(ar+1) 与 ar[1] 来理解就会清晰很多,前者都是在a的地址基础上偏移一个 单位 的位置上取数据 ,后者也是同样的道理。

而对于如何理解下标运算符,MicroSoft Document也给出了示例:

double aDbl[2]; 和的地址 aDb[0]aDb[1] 类型的对象的大小相隔8个字节 —double 。 这种根据对象类型进行缩放的操作是由 c + + 语言自动完成的,并且在 加法运算符 中定义,其中讨论了指针类型的操作数的加法和减法。

下标表达式还可以有多个下标,如下所示:

表达式2[2] [expression3]

下标表达式从左至右关联。 首先计算最左侧的下标表达式 expression1[expression2]。 通过添加 expression1 和 expression2 得到的地址构成一个指针表达式;然后 expression3 将添加到此指针表达式,从而构成一个新的指针表达式,依此类推,直到添加最后一个下标表达式。 在 * 计算最后一个下标表达式后 () 的间接寻址运算符(除非最终指针值用于处理数组类型)。

这里讲的下标表达式可以支持多个下标,其实就是我们的多维数组的使用方式。例如 nums[i][j], array[i][j][k] 等。

正下标和负下标

数组的第一个元素是元素 0。 C + + 数组的范围是从 array[0] 到 array[size -1]。 但是,C++ 支持正负下标。 负下标必须在数组边界内;否则结果不可预知。 以下代码显示了正数组和负数组下标:

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

int main() {
    
    
	int intArray[1024];
	/* 初始化,intArray:{0,1,2,3,4...1023 } */ 
	iota(intArray, intArray + _countof(intArray), 0);

	cout << intArray[512] << endl;   // 512

	cout << 257[intArray] << endl;   // 257

	int* midArray = &intArray[512];  // pointer to the middle of the array

	cout << midArray[-256] << endl;  // 256

	cout << intArray[-256] << endl;  // unpredictable, may crash
}

这里midArray 指向了intArray中间的位置,因此midArray[-256]其实使用了 减法运算符(指针减法),即——以midArray 地址为基准向左偏移256个单位。

关于减法运算符:指针减法
如果两个操作数都是指针,则减法运算的结果就是两个操作数之差(在数组元素中)。 减法表达式生成在标准包含文件 ptrdiff_t < stddef.h >
.
其中一个操作数可以是整型,条件是该操作数是第二操作数。 减法的结果的类型与原始指针的类型相同。 减法的值是指向第 () 数组元素的指针,其中 n 是原始指针指向的元素 ,i是第二个操作数的整数值。

而 intArray[-256] 的结果之所以不可预测是因为,intArray地址是数组起始位置,如果向左寻址则超出数组界限之外,而在那部分的内存是我们程序未申请的部分,如果强行访问可能会造成未知的后果。

再举个例子:我们使用负下标输出数组nums的所有元素即其地址

	int nums[] = {
    
     1,2,3,4,5,6,7,8,9 };
	const int len = _countof(nums);	// nums元素个数

	int* tail = nums + len;	// 指向nums尾部
	cout << static_cast<void*>(nums) << " ——address of nums \n";
	cout << static_cast<void*>(tail) << " ——address of tail \n";

	for (int i = 1; i <= len; ++i)
	{
    
    
		cout << static_cast<void*>(tail - i) 
			<< " offfset:" << i << " value="
			<< tail[-i] << "\n";
	}
	cout << endl; // 9 8 7 6 5 4 3 2 1

运行结果:
在这里插入图片描述
从运行结果可以看出,tail每次的偏移量左移一个单位,地址值减少4(int类型所占字节大小),而随着9个元素偏移量全部算在内时,它的地址就等于初始时nums的地址。

猜你喜欢

转载自blog.csdn.net/weixin_43919932/article/details/123267704
今日推荐