一维数组中,arr、&arr[0]、&arr、*arr、arr[0]、*&arr

1. 一维数组 

一维数组:int arr[] = {1, 2, 3, 4, 5};
arr的值是数组第一个元素的地址,arr是一个常量,对吗?

在 C++ 中,一维数组的数组名(如,arr),在表达式中,会被视为,一个指向第一个元素的指针。因此,一维度数组的数组名(如,arr)的值,是,数组第一个元素的地址。

1.1 详细说明
(1)一维数组的数组名的性质

在大多数上下文中( 例如,一维数组名作为函数参数、一维数组名被赋值给另一个变量,对一维数组名的解引用,等),一维数组名会被视为,一个指向第一个元素的指针;一维数组名的值,就是,数组第一个元素所在存储空间的首地址!

(2)常量的性质

一维数组名本身是一个常量!也就是,一维数组名的值不可更改!给一维数组名进行赋值,是错误的!

例如,下面代码是非法的:

int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

// arr = nullptr; // 错误!一维数组名是一个常量,其值不可被修改!不可以给一维数组名进行赋值!
(3)访问元素

可以通过元素的索引,来访问数组的元素!如,arr[0]arr[1]等;实际上,这些访问操作,也是通过指针实现的!

1.2 示例代码
#include <iostream>

int main()
{
    int arr[] = {1, 2, 3, 4, 5, 6};
    
    std::cout << "Address of first element: " << &arr[0] << std::endl; // 数组第一个元素的地址
    std::cout << "Value of arr (Address of first element): " << arr << std::endl; // arr的值,即数组第一个元素的地址

    // arr = nullptr; // 导致编译错误

    return 0;
}

输出结果:

Address of first element: 006FFC7C
Value of arr (Address of first element): 006FFC7C
1.3 总结
  • 一维数组名的值,是,数组第一个元素所在存储空间的首地址;
  • 一维数组名是一个常量!其值不能被更改,不能对其进行赋值!
  • 通过,数组名[元素索引],来访问数组的元素!但,实际上,这些访问操作,也是通过指针实现的!


2. 几个关键量之间的区别

一维数组:int arr[] = {1, 2, 3, 4, 5};
arr、*arr、&arr、arr[0]、&arr[0]、*&arr之间的区别?

2.1 arr
  • 含义:arr是数组的名字;
  • 数据类型:int[5],表示一个拥有5个int型元素的数组;
  • 值:在大多数上下文中,arr会被视为,一个指向数组第一个元素的指针,因此,其值等于,&arr[0]但是,它本身并不是一个指针,而是一个数组类型的值
  • 特性:是一个常量!不能被重新赋值,也就是,不能对其进行赋值!如,arr=nullptr; 是非法的!
  • 注意:数组的首地址( 或者说,起始地址 ),就等于,数组的第一个元素所在存储空间的首地址!也就是,数组的地址,等于,数组第一个元素的地址!

2.2 &arr
  • 含义:&arr,表示,指向整个数组的一个指针;
  • 数据类型:int (*)[5],表示,一个指向拥有5个int型元素的数组的指针;&arr,就是,一个指针!是一个指向数组的指针
  • 值:&arr的值,是数组的首地址( 或者说,起始地址 )!也等于,数组的第一个元素所在存储空间的首地址!&arr的值,等于,arr;也等于,&arr[0];
  • 特性:指向整个数组,可以用来代表整个数组

2.3 &arr[0]
  • 含义:&arr[0],表示,数组第一个元素的地址,也就是,数组第一个元素所在存储空间的首地址;
  • 数据类型:int*,表示一个指向整数的指针;
  • 值:数组第一个元素的地址,也就是,数组第一个元素所在存储空间的首地址;&arr[0]的值,等于,arr
  • 特性:可以用来获取,数组第一个元素的地址,也就是,数组第一个元素所在存储空间的首地址;

2.4 *arr
  • 含义:*arr,表示,对arr的解引用获取数组的第一个元素
  • 数据类型:int型,表示一个整数;
  • 值:*arr,等于,arr[0];也就是,*arr,等于,数组第一个元素的值;
  • 特性:可以通过*arr,访问数组的第一个元素!

2.5 arr[0]
  • 含义:arr[0],表示,数组的第一个元素;
  • 数据类型:int型,表示一个整数;
  • 值:数组第一个元素的值;arr[0],等于,*arr;
  • 特性:可以直接访问和修改该元素的值;

2.6 *&arr
  • 含义:*&arr,表示,对&arr的解引用获取的是,&arr所指向的整个数组
  • 数据类型:int[5],表示一个拥有5个int型元素的数组;
  • 值:&arr所指向的整个数组;*&arr,等于,arr;
  • 特性:可以直接访问和修改,&arr所指向数组的元素的值;
#include <iostream>
#include <typeinfo>

int main() {
	int arr[] = { 1, 2, 3, 4, 5 };

	std::cout << arr << std::endl; // 00000071552FFB58
	std::cout << &arr << std::endl; // 00000071552FFB58
	std::cout << &arr[0] << std::endl; // 00000071552FFB58

	std::cout << arr[0] << std::endl; // 1
	std::cout << *arr << std::endl; // 1

	std::cout << *&arr << std::endl; // 00000071552FFB58 ---> &arr所指向数组的地址
	std::cout << (*&arr)[0] << std::endl; // 1 ---> &arr所指向数组的第一个元素


	std::cout << sizeof(arr) << std::endl; // 20 (5个int,每个4字节)
	std::cout << sizeof(&arr) << std::endl; // 8 (在64位系统上,指针的大小)
	std::cout << sizeof(&arr[0]) << std::endl; // 8

	std::cout << sizeof(arr[0]) << std::endl; // 4
	std::cout << sizeof(*arr) << std::endl; // 4

	std::cout << sizeof(*&arr) << std::endl; // 20 ---> &arr所指向数组的大小, 所以占据的存储空间的大小
	std::cout << sizeof((*&arr)[0]) << std::endl; // 4 ---> &arr所指向数组的第一个元素所占据的存储空间的大小


	std::cout << typeid(arr).name() << std::endl; // int [5]
	std::cout << typeid(&arr).name() << std::endl; // int (* __ptr64)[5]
	std::cout << typeid(&arr[0]).name() << std::endl; // int * __ptr64

	std::cout << typeid(arr[0]).name() << std::endl; // int
	std::cout << typeid(*arr).name() << std::endl; // int

	std::cout << typeid(*&arr).name() << std::endl; // int [5] ---> &arr所指向数组的数据类型
	std::cout << typeid((*&arr)[0]).name() << std::endl; // int ---> &arr所指向数组的第一个元素的数据类型

	return 0;
}
表达式数据类型含义
arrint[5]一个拥有5个int型元素的数组的数组名数组的起始地址,也等于,数组第一个元素的地址
&arrint (*)[5]指向一个拥有5个int型元素的数组的一个指针数组的起始地址,也等于,数组第一个元素的地址
&arr[0]int*数组第一个元素的地址数组第一个元素的地址,也等于,数组的起始地址
*arrint对数组名解引用,获取数组第一个元素数组的第一个元素的值
arr[0]int数组第一个元素数组的第一个元素的值
*&arrint[5]&arr所指向的数组&arr所指向的数组的地址


3. arr 与 &arr之间的区别

一维数组:int arr[] = {1, 2, 3, 4, 5};
arr、&arr之间的区别?

3.1 arr
  • 数据类型:int[5],即,数组类型;arr,是一个,数据类型为数组类型的值!该数组拥有5个int型元素!
  • 含义:arr是数组的名字;在表达式中使用时,arr会被隐式转换为,一个指向数组第一个元素的指针;但是,arr本身并不是一个指针,其数据类型为数组类型,是一个数组类型的值!
  • 值:等于数组的起始地址/首地址,即,数组第一个元素的地址;
3.2 &arr
  • 数据类型:int (*)[5],即,一个指针;只不过,该指针是一个,指向拥有5个int型元素的数组的,指针!
  • 含义:&arr,指向整个数组!可以用来代表整个数组!
  • 值:等于数组的起始地址/首地址,即,数组第一个元素的地址;
#include <iostream>
#include <typeinfo>

int main() {
	int arr[] = { 1, 2, 3, 4, 5 };

	std::cout << arr << std::endl; // 00000077D6EFF828
	std::cout << &arr << std::endl; // 00000077D6EFF828

	std::cout << sizeof(arr) << std::endl; // 20 (5个int,每个4字节)
	std::cout << sizeof(&arr) << std::endl; // 8 (在64位系统上,指针的大小)

	std::cout << typeid(arr).name() << std::endl; // int [5]
	std::cout << typeid(&arr).name() << std::endl; // int (* __ptr64)[5]

	return 0;
}
3.3 为什么sizeof(arr)的返回值是,整个数组的元素个数乘以4个字节?而sizeof(&arr)的返回值是,一个指针的大小
3.3.1 对于sizeof(arr)
(1)数组的定义
int arr[] = {1, 2, 3, 4, 5};

可见,arr是一个拥有5个int型元素的数组;

(2)sizeof运算符
  • 当使用sizeof(arr)时,编译器能够知道,arr的数据类型是 int[5],这意味着,arr是一个包含 5 个int型元素的数组。
  • 因此,sizeof(arr)会计算整个数组的大小。
(3)计算过程
  • 每个 int型数值,通常占用 4 个字节的存储空间(具体取决于编译器和平台);
  • 因此,sizeof(arr)计算过程为:sizeof(arr) = 5 × sizeof(int) = 5 × 4 = 20 Bytes
(4)示例代码
#include <iostream>

int main() {
	int arr[] = { 1, 2, 3, 4, 5 };
	std::cout << "Size of arr: " << sizeof(arr) << " bytes" << std::endl; // 20 bytes
	std::cout << "Number of elements: " << sizeof(arr) / sizeof(arr[0]) << std::endl; // 5

	return 0;
}
(5)注意事项

数组名的值是一个地址!所以,在将数组名传递给一个函数时,传递的方式要么是值传递(传递的是地址),要么是引用传递

如果是值传递方式,数组名会被转换为一个指向数组第一元素的指针数据类型发生了变换!此时,在调用的函数内部,再用sizeof函数来检查数组名所占据的存储空间大小时,会发现,结果变为,一个指针值所占据的存储空间的大小;

如果是引用传递,只是给该数组另起一个名字!此时,在调用的函数内部,再用sizeof函数来检查数组名所占据的存储空间大小时,会发现,结果不变!

#include <iostream>
#include <typeinfo>

void func1(int arr1[]) // 值传递方式
{
	std::cout << arr1 << std::endl; // 00000056BC5BF638
	std::cout << sizeof(arr1) << std::endl; // 8
	std::cout << typeid(arr1).name() << std::endl; // int * __ptr64
    
    // arr1的值发生变化,arr的值不会发生变化
}

void func2(int (&arr2)[5]) // 引用传递方式
{
	std::cout << arr2 << std::endl; // 00000056BC5BF638
	std::cout << sizeof(arr2) << std::endl; // 20
	std::cout << typeid(arr2).name() << std::endl; // int [5]

    // arr2的值发生变化,arr的值也会发生变化
}

int main() {
	int arr[] = { 1, 2, 3, 4, 5 };

	std::cout << arr << std::endl; // 00000056BC5BF638
	std::cout << &arr << std::endl; // 00000056BC5BF638

	std::cout << sizeof(arr) << std::endl; // 20 (5个int,每个4字节)
	std::cout << sizeof(&arr) << std::endl; // 8 (在64位系统上,指针的大小)

	std::cout << typeid(arr).name() << std::endl; // int [5]
	std::cout << typeid(&arr).name() << std::endl; // int (* __ptr64)[5]

	func1(arr);

	func2(arr);

	return 0;
}
3.3.2 对于sizeof(&arr)
(1)数组的定义
int arr[] = {1, 2, 3, 4, 5};

可见,arr是一个拥有5个int型元素的数组,而&arr是一个指针,指向一个拥有5个int型元素的数组!

(2)sizeof运算符
  • 当使用sizeof(&arr)时,编译器能够知道,&arr的数据类型是 int (*)[5],这意味着,&arr是一个指针,指向一个拥有5个int型元素的数组!
  • 因此,sizeof(&arr)计算的是一个指针所占据的存储空间的大小!
  • 在大多数现代系统中,指针所占据的存储空间的大小通常是 4个字节(在 32 位系统上)或 8个字节(在 64 位系统上)。
(3)示例代码
#include <iostream>
#include <typeinfo>

int main() {
	int arr[] = { 1, 2, 3, 4, 5 };

	std::cout << "Size of arr: " << sizeof(arr) << " bytes" << std::endl; // 20 bytes
	std::cout << "Size of &arr: " << sizeof(&arr) << " bytes" << std::endl; // 8 bytes  ( 4 bytes )

	std::cout << typeid(arr).name() << std::endl; // int [5]
	std::cout << typeid(&arr).name() << std::endl; // int (* __ptr64)[5]  ( int (*)[5] )

	return 0;
}
3.4 arr与&arr的值是一样的,但是两者的数据类型不一样,代表的对象不一样?

对!

arr 和 &arr 的值是相同的,都等于,数组的起始地址/首地址,即,数组第一个元素的地址( 所在存储空间的首地址 );但它们的数据类型和代表的对象确实不同!

表达式数据类型含义所代表的对象
arrint[5]一个拥有5个int型元素的数组的数组名代表的是数组的第一个元素数组的起始地址,也等于,数组第一个元素的地址
&arrint (*)[5]指向一个拥有5个int型元素的数组的一个指针代表的是整个数组,可以用于传递整个数组到某个函数数组的起始地址,也等于,数组第一个元素的地址
3.5 如何使用 arr 和 &arr 访问和修改数组元素?
(1)使用arr
  • 含义:arr是数组的名字;在表达式中使用时,arr会被隐式转换为,一个指向数组第一个元素的指针;但是,arr本身并不是一个指针,其数据类型为数组类型,是一个数组类型的值!
  • 值:等于数组的起始地址/首地址,即,数组第一个元素的地址;

使用arr访问数组元素时,可以通过元素索引,来访问数组元素!

arr[元素索引]

如下面代码所示:

#include <iostream>

int main() 
{
    int arr[] = {1, 2, 3, 4, 5};


    // 访问元素
    std::cout << "Original array elements:" << std::endl;
    for (int i = 0; i < 5; ++i) {
        std::cout << arr[i] << " "; // 通过元素索引,访问元素
    }
    std::cout << std::endl;


    // 修改元素
    arr[2] = 10; // 通过元素索引,修改第三个元素为 10
    std::cout << "After modification:" << std::endl;
    for (int i = 0; i < 5; ++i) {
        std::cout << arr[i] << " "; // 通过元素索引,访问元素
    }
    std::cout << std::endl;

    return 0;
}
(2)使用&arr
  • 数据类型:int (*)[5],即,是一个指针;只不过,该指针是一个,指向拥有5个int型元素的数组的,指针!
  • 含义:&arr,指向整个数组!可以用来代表整个数组!
  • 值:等于数组的起始地址/首地址,即,数组第一个元素的地址;

使用&arr访问数组元素时,可以通过解引用,以及元素索引,来访问数组元素!

(*&arr)[元素索引]

注意:

(1)*&arr,获取,&arr所指向的数组 —— arr,数据类型为int[5];

(2)再通过元素索引,获取,&arr所指向的数组的元素;

#include <iostream>

int main() {
    int arr[] = {1, 2, 3, 4, 5};


    // 访问元素
    std::cout << "Original array elements using &arr:" << std::endl;
    for (int i = 0; i < 5; ++i) {
        std::cout << (*&arr)[i] << " "; // 通过解引用,访问元素
    }
    std::cout << std::endl;


    // 修改元素
    (*&arr)[1] = 20; // 通过解引用,修改第二个元素为 20
    std::cout << "After modification using &arr:" << std::endl;
    for (int i = 0; i < 5; ++i) {
        std::cout << (*&arr)[i] << " "; // 通过解引用,访问元素
    }
    std::cout << std::endl;

    return 0;
}

(*&arr)[元素索引] ------> arr[元素索引]

(3)总结
  • 使用 arr:
    • 可以直接使用元素下标(元素索引),访问和修改元素,如 arr[i]
    • 更加直观和简单。
  • 使用 &arr:
    • 需要先进行解引用,然后使用元素下标(元素索引),才能访问元素,如 (*&arr)[i]
    • 适合,在需要传递整个数组时使用。

在大多数情况下,使用 arr 更为简洁和常见,&arr 的用法,通常在特定的需要传递整个数组的场景下。

3.6 arr+1,与(&arr)+1,这两个1的含义不同

int arr[] = {1, 2, 3, 4, 5};
arr+1,与(&arr)+1,这两个1的含义不同?

对!

(1)arr+1

arr的含义:arr是一维数组的名字,在表达式中使用时,arr会被隐式转换为,一个指向数组第一个元素的指针;但是,arr本身并不是一个指针,其数据类型为数组类型 —— int[5]。

因此,在表达式 "arr+1"中,arr被隐式转换为指向数组第一个元素的指针,则arr+1是,指向数组第二个元素的指针,其值等于数组第二个元素的地址( 即,该元素所在存储空间的首地址 )!所以,"arr+1"中的"1",指的是,一个数组元素所占据的存储空间的大小!

(2)(&arr)+1

&arr的含义:&arr是一个指针,该指针指向的是,一个拥有5个int型元素的数组!其数据类型为:int (*)[5]

"(&arr)+1"是,将指针&arr移动,其所指向的数组所占据的存储空间大小,也就是,跳过其所指向的数组;所以,"(&arr)+1"的值( 地址值 ),与&arr的值( 地址值 ),相差20个字节( 该值是,&arr所指向的数组所占据的存储空间的大小 )。

因此,"(&arr)+1"中的"1",指的是,&arr所指向的数组所占据的存储空间的大小( 即,元素个数 × 一个元素所占据的存储空间 )!

如下面所示代码:

#include <iostream>
#include <typeinfo>

int main() {
	int arr[] = { 1, 2, 3, 4, 5 };

	std::cout << arr << std::endl; // 00000005E1CFF7F8
	std::cout << &arr[0] << std::endl; // 00000005E1CFF7F8
	std::cout << *arr << std::endl; // 1
	std::cout << arr[0] << std::endl; // 1

	std::cout << sizeof(arr) << std::endl; // 20 ---> 数组类型的数值, 所占存储空间的大小 = 数组元素个数 × 单个元素所占据的存储空间大小
	std::cout << typeid(arr).name() << std::endl; // int [5]
	std::cout << sizeof(arr[0]) << std::endl; // 4 ---> 一个数组元素占据4个字节( 32位 )的存储空间
	std::cout << typeid(arr[0]).name() << std::endl; // int ---> 数组元素为int型元素


	std::cout << arr + 1 << std::endl; // 00000005E1CFF7FC
	std::cout << &arr[1] << std::endl; // 00000005E1CFF7FC
	std::cout << *(arr + 1) << std::endl; // 2
	std::cout << arr[1] << std::endl; // 2

	std::cout << sizeof(arr+1) << std::endl; // 8 ---> 一个指针所占据的存储空间的大小( 在64位系统上, 占据8个字节存储空间; 在32位系统上, 占据4个字节的存储空间 )
	std::cout << typeid(arr+1).name() << std::endl; // int * __ptr64 ---> 指针类型


	auto diff = (arr + 1) - arr; // 00000005E1CFF7FC - 00000005E1CFF7F8 = 4 ---> 十进制:4个字节 ---> 一个int型数组元素
	std::cout << diff << std::endl; // 1 ---> 两个指针之间相差一个元素
	std::cout << sizeof(diff) << std::endl; // 8 ---> 一个指针所占据的存储空间的大小( 在64位系统上, 占据8个字节存储空间; 在32位系统上, 占据4个字节的存储空间 )
	std::cout << typeid(diff).name() << std::endl; // __int64 ---> 一个占据64个bit位的整型数值


	// -*-*-*--*-*-*--*-*-*--*-*-*--*-*-*--*-*-*--*-*-*--*-*-*--*-*-*--*-*-*-


	std::cout << &arr << std::endl; // 00000005E1CFF7F8
	std::cout << sizeof(&arr) << std::endl; // 8 ---> 一个指针所占据的存储空间的大小( 在64位系统上, 占据8个字节存储空间; 在32位系统上, 占据4个字节的存储空间 )
	std::cout << typeid(&arr).name() << std::endl; // int (* __ptr64)[5]

	std::cout << *(&arr) << std::endl; // 00000005E1CFF7F8
	std::cout << sizeof(*(&arr)) << std::endl; // 20
	std::cout << typeid(*(&arr)).name() << std::endl; // int [5]

	std::cout << *(&arr)[0] << std::endl; // 1
	std::cout << sizeof(*(&arr)[0]) << std::endl; // 4
	std::cout << typeid(*(&arr)[0]).name() << std::endl; // int


	std::cout << (&arr) + 1 << std::endl; // 00000005E1CFF80C
	std::cout << sizeof((&arr) + 1) << std::endl; // 8 ---> 一个指针所占据的存储空间的大小( 在64位系统上, 占据8个字节存储空间; 在32位系统上, 占据4个字节的存储空间 )
	std::cout << typeid((&arr) + 1).name() << std::endl; // int (* __ptr64)[5]

	std::cout << *((&arr) + 1) << std::endl; // 00000005E1CFF80C
	std::cout << sizeof(*((&arr) + 1)) << std::endl; // 20
	std::cout << typeid(*((&arr) + 1)).name() << std::endl; // int [5]

	std::cout << *((&arr) + 1)[0] << std::endl; // -858993460
	std::cout << sizeof(*((&arr) + 1)[0]) << std::endl; // 4
	std::cout << typeid(*((&arr) + 1)[0]).name() << std::endl; // int


	auto diff2 = ((&arr) + 1) - &arr; // 00000005E1CFF80C - 00000005E1CFF7F8 = 14 ---> 十进制:20个字节 ---> 5个int型数组元素
	std::cout << diff2 << std::endl; // 1 ---> 两个指针之间相差 一整个数组
	std::cout << sizeof(diff2) << std::endl; // 8 ---> 一个指针所占据的存储空间的大小( 在64位系统上, 占据8个字节存储空间; 在32位系统上, 占据4个字节的存储空间 )
	std::cout << typeid(diff2).name() << std::endl; // __int64 ---> 一个占据64个bit位的整型数值

	return 0;
}

补充:

(1)https://blog.csdn.net/dabaooooq/article/details/134319470

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值