数组与指针
arr、&arr、&arr[0]、*P 的区别
-
首先,他们指向的都是数组首元素的地址。单纯的打印他们的值,都是相同的。例如以下代码段:
// 假定以下伪代码在内存中的起始地址是从0x0开始的 int main() { int arr[3] = {1,2,3}; int* p = arr; printf("%p", arr); // 输出地址为0x0 printf("%p", &arr[0]); // 输出地址为0x0 printf("%p", p); // 输出地址为0x0 printf("%p", &arr); // 输出地址为0x0 return 0; }
-
但是,他们的数据类型是不同的。
arr
:表示数组类型,或者说是数组本身&arr[0]
:是一个指针类型(匿名指针),该指针指向arr数组的首地址,上文代码段中的&arr[0]
和int* p
可以认为除了匿名和非匿名的区别,其余的特性都是等价的int* p
:是一个指针类型,该指针指向arr数组的首地址&arr
:是一个数组类型的指针(匿名指针),该指针指向一个int类型的数组,等价于匿名的int (*arr)[];
虽然他们指向的地址相同,但是当对不同的数据类型做操作时,他们的差异就会体现出来,例如以下代码段:
// 假定以下伪代码在内存中的起始地址是从0x0开始的 int main() { int arr[3] = {1,2,3}; int* p = arr; printf("%p", arr + 1); // 错误代码,编译会失败! 数组本身是不能地址偏移的 printf("%p", p+ 1); // 输出地址为0x4,因为是一个int类型的指针,每次+1偏移4字节 printf("%p", &arr[0] + 1); // 输出地址为0x4,等价于p,但是是匿名的 printf("%p", p + 1); // 输出地址为0xC,因为是数组类型的指针,所以偏移了一整个数组的地址 return 0; }
常见的数组与指针结合
int arr[];
表示一个数组,数组中的元素都是int类型
int (*arr)[];
表示数组类型的指针 ,这个指针指向一个int类型的数组
int* arr[]={};
表示数组中的元素都是指向int类型的指针
int* (*arr)[];
表示数组类型的指针,这个指针指向的数组中的元素都是指针类型,且数组中的元素指向的都是int类型
逻辑关系梳理
不要死记硬背奥,核心原因,就是因为按照运算符的优先级,( ) > [ ] > * > 数据类型。
int arr[]
:首先,因为运算符的优先级原因,变量名arr先与[ ]结合,表明这是一个数组。其次arr[]
再与数据类型int相结合,表明数组中的元素都是int类型。int (*arr)[]
:首先()先生效,导致*arr先结合,表明这个变量是一个指针。其次,(*arr)
再与[]相结合,表明这个指针是指向数组的。最后,(*arr)[]
再与int相结合,表明这个指针指向的数据中的元素都是int类型。int *arr[]
:首先[]先生效,导致arr[]先结合,表明这个变量是一个数组。其次,arr[]
再与*相结合,表明这个数组数组中的元素都是指针。最后,*arr[]
再与int相结合,表明这个数组中的元素都是指向int类型的指针。int* (*arr)[]
:首先()先生效,导致*arr先结合,表明这个变量是一个指针。其次,(*arr)
再与[]相结合,表明这个指针是指向数组的,最后,(*arr)[]
再与最前面的*相结合,表示这个指针指向的数组中的元素都是指针类型。最最后,* (*arr)[]
再与int相结合,表示这个指针指向的数组中的元素都是int类型的指针类型。
数组作为形参
一维函数形参接收数组的几种方式
void test1(int arr[], int arrSize){}
void test2(int arr[4], int arrSize){}
void test3(int* p, int arrSize){}
int main()
{
int arr[4] = {1,2,3,4};
int arrSize = sizeof(arr);
test1(arr, arrSize);
test2(arr, arrSize);
test3(arr, arrSize);
return 0;
}
说明:
- 上述代码段中,形参
int arr[]
,int arr[4]
,int* p
都可以用来接收一维数组。- 如果被调函数预期想要接收一个数组,建议使用
int arr[]
,便于提高代码可读性。 int arr[4]
一般不推荐,如果被调函数对接收的数组有预期大小,才建议使用。int *p
这种声明方式更通用,因为它不限制指针所指向的数据类型,只是指向具体的内存地址。指针的本质是存储内存地址的变量,而内存地址是通用的,不区分数据类型。
- 如果被调函数预期想要接收一个数组,建议使用
void test1(int arr[][3], int arrSize){}
void test2(int (*arr)[], int arrSize){}
void test3(int** p, int arrSize){}
int main()
{
int arr[][3] = {{1,2,3},{4,5,6},{7,8,9}};
int arrSize = sizeof(arr);
test1(arr, arrSize);
test2(arr, arrSize);
test3(arr, arrSize);
return 0;
}
说明:
- 上述代码段中,形参
int arr[][3]
,int (*arr)[]
,int** p
都可以用来接收二维数组。