深入理解指针(中)

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);//指针数组

  • 14
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值