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;
}
表达式 | 数据类型 | 含义 | 值 |
arr | int[5] | 一个拥有5个int型元素的数组的数组名 | 数组的起始地址,也等于,数组第一个元素的地址 |
&arr | int (*)[5] | 指向一个拥有5个int型元素的数组的一个指针 | 数组的起始地址,也等于,数组第一个元素的地址 |
&arr[0] | int* | 数组第一个元素的地址 | 数组第一个元素的地址,也等于,数组的起始地址 |
*arr | int | 对数组名解引用,获取数组第一个元素 | 数组的第一个元素的值 |
arr[0] | int | 数组第一个元素 | 数组的第一个元素的值 |
*&arr | int[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 的值是相同的,都等于,数组的起始地址/首地址,即,数组第一个元素的地址( 所在存储空间的首地址 );但它们的数据类型和代表的对象确实不同!
表达式 | 数据类型 | 含义 | 所代表的对象 | 值 |
arr | int[5] | 一个拥有5个int型元素的数组的数组名 | 代表的是数组的第一个元素 | 数组的起始地址,也等于,数组第一个元素的地址 |
&arr | int (*)[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