1.数组名的理解
概念:数组名就是首元素的地址
但是有两种情况数组名不是首元素的地址:
使用指针访问数组的内容:
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int* p = &arr[0];
&arr[0]拿到了数组的第一个元素的地址,但其实数组名本身就是首元素地址,可以测试一下:
#include<stdio.h>
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,10};
printf("&arr[0] = %p\n",&arr[0]);
printf("arr = %p\n",arr);
return 0;
}
输出结果:
通过上述的代码 验证了arr,也就是数组名就是首元素(第一个元素)的地址。
那下面的代码怎么去理解呢?
#include<stdio.h>
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,10};
peintf("%d\n",sizeof(arr));
return 0;
}
输出结果为:
如果arr确定为首元素地址的话在x86的环境下输出的应该是4,但是为什么是40呢?
原因如下:
虽然数组名代表的是首元素的地址,单数有两种特殊情况:
sizeof(数组名) 计算的是整个数组的大小,这里的数组名代表的是整个数组;
&数组名 代表的是整个数组,取出的是整个数组的地址;
举例就明白了:
#include<stdio.h>
int main()
{
int arr[] = {1,2,3,4};
printf("%p\n",arr);
printf("%p\n",arr+1);
printf("%p\n",&arr);
printf("%p\n",&arr+1);
return 0;
}
输出结果:
(注意内存大小都是按16进制来进行运算的,16进制就是满16进一)
第一个内存地址代表的是元素的起始地址,也是首元素的地址;
第二个 内存地址代表的是首元素+1的地址,就是第二个元素的地址,int类型在x86环境下是4个字节,第二个元素的地址就是首元素地址+4;
第三个内存地址代表的是整个数组的地址,整个数组的地址就是元素的首地址;
第四个内存地址代表的是整个数组+1的地址,整个数组大小是16byte,整个数组+1的地址正好比元素起始地址多了16byte
2.数组指针的使用
概念:数组指针就是指向数组的指针;
#include<stdio.h>
int main()
{
//下面代码的含义:
int arr[5]; //数组
int(*parr2)[10]; //数组指针 - 指向数组的指针
//拆分:*parr2是数组名数组指针类型,里面10元素的都是整数类型
int(*parr3[10])[5]; //parr3是存储数组指针的数组
//拆分:
// arr1: 1 2 3 4 5
//(*parr3[10])[5] = {&arr1,&arr2,&arr3}: arr2: 2 3 4 5 6
// arr3: 3 4 5 6 7
//*parr3[10] 就是整型指针数组
//[5] 每个整型指针数组里面都有5个元素
}
使用指针访问数组:
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr; //arr和p是等价的,所以arr[0]也可以写成p[0]
//输入
for(int i = 0; i < sz; i++)
{
scanf("%d", p + i);
}
//输出
for(int n = 0; n < sz; n++)
{
printf("%d ", *(p + n));
}
return 0;
}
结果:
3. 一维数组传参的本质
概念:一维数组实际上是指向数组首元素的指针,在函数中传递数组参数时,可以将数组名作为指针传递给函数
void test(int arr[])
{}
//分析:arr是一维数组,里面可以存放10个元素
void test(int arr[10])
{}
//分析:arr是一维数组,里面可以存放10个元素
void test(int *arr)
{}
//分析:因为arr是首元素的地址,指针指向的也是首元素的地址
void test2(int* arr[20])
{}
//分析:因为*arr2[20]是整型指针数组,所有元素都是一级指针类型
void test2(int** arr)
{}
//分析:因为*arr2[20]是整型指针数组,所有元素都是一级指针类型,但是二级指针是用来存放一级指针地址的
// arr2是数组首元素的地址,首元素是int*类型,所以是一级指针的地址
int main()
{
int arr[10] = { 0 };
int* arr2[20] = { 0 };
test(arr);
test2(arr2);
}
4.二维数组传参
概念:将二维数组的名称作为指向数组的指针传递,并在参数中声明数组的第二个维度。
void test(int arr[3][5])
{}
//分析:二维数组传参
void test(int arr[][])
{}
//分析:二维数组传参可以省略行但是不能省略列,所以是错的
// 多维数组传参也是只可以省略最前面的,后面的不可以省略
void test(int arr[][5])
{}
//分析:二维数组传参
void test(int* arr)
{}
//分析:二维数组的数组名是首元素的地址,指的是第一行的地址,第一行是一个一维数组
void test(int* arr[5])
{}
//分析:二维数组的数组名是首元素的地址,arr是数组名数组指针类型,里面5元素的都是整数类型
void test(int** arr)
{}
//分析:二级指针接收的是一级指针的地址,二维数组的数组名是首元素的地址,指的是第一行的地址,并不是3行的地址
int main()
{
int arr[3][5] = { 0 };
test(arr);
}
5.一级指针传参
概念:用一级指针接收数组名 (首元素地址),写个函数把一级指针变量传递过去。 打印出 arr 数组名当中的内容。 函数当中传参是一级指针,所以也被称之为一级指针的传参。
void print(int* p, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d\n", *(p + i));
}
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8};
int* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
//一级指针p,传给函数
print(p, sz);
return 0;
}
//如果是函数的参数部分是指针
//void print(int *p)
//{}
//int a = 10;
//int* prt = &a;
//int arr[10];
//
//print(&a);
//print(prt);
//print(arr);
6.二级指针传参
概念:指向指针的指针变量称为二级指针。
如果pp是一个二级指针,那么有如下属性:
①.二级指针的地址(&pp);
②.二级指针的地址保存的地址(pp);
③.二级指针的地址保存的地址,该地址里面保存的地址(*pp);
④.二级指针的地址保存的地址,该地址里面保存的地址里面的数据(**pp)。
void test(int** prt)
{
printf("%d\n", **prt);
}
int main()
{
int n = 10;
int* p = &n;
//一级指针存储的是变量的地址
int** pp = &p;
//二级指针存储的是一级指针的地址
test(pp);
//pp就是二级指针
test(&p);
//&p就是一级指针的地址
return 0;
}
//如果函数的形式参数是二级指针,调用函数的时候可以传的实参
//void test(int** p){}
//
//int* p1;
//int** p2;
//int* arr[10];
//
//test(&p1);
//test(p2);
//test(arr);//指针数组