指针?其实也没那么难(超详解,建议收藏)

指针是什么?

指针是什么?
指针理解的两个要点:
1.指针是内存中一个最小单元的编号,也就是地址
为了更好的管理和使用内存单元 更快更准的定位到每个单元的内容,我们会把内存划分为一个个小的单元 一个内存单元是一个字节, 把每个内存单元都编号

在这里插入图片描述

2.平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量

在这里插入图片描述

指针变量

#include <stdio.h>
int main()
{
    
    int a = 10;//在内存中开辟一块空间
    int* pa = &a;//这里我们对变量a,使用&操作符(取地址操作符)取出它的地址
                     //把a的地址存放在pa变量中,使用这个变量就是使用这个地址,这里的pa就是指针变量
    *pa = 20;		//*解引用操作 *pa就是通过pa里面的地址,找到a,通过pa来修改a
    printf("%d\n", a);

    return 0;
} 

pa 指针首先是一个变量,它本身也占据一块内存,这块内存里存放的就是a的首地址。
当解引用的时候,就会从这个首地址连续划出4个byte,然后按照int类型的编码方式解释
在这里插入图片描述

为什么要有地址呢?

为什么要有地址呢?
可以快速的找到内存单元 这个跟生活中小区的楼房单元门还有室是一样的
假如一天胡图图让你去他家玩,他只告诉你他在番斗大街番斗花园住 ,你肯定是找不到他,如果他又在告诉你番斗花园2号楼1001室,你可以很快的去找到他,这就是地址的意义

内存单元的编号如何产生?

如果当前机器生成32位虚拟地址空间
32位地址 32根地址线中每个存储单元的字长是32位 所以4个字节就可以存放一个地址
如果当前机器生成64位虚拟地址空间
64位地址 64根地址线中每个存储单元的字长是64位 所哟8个字节就可以存放一个地址

32位的CPU的虚拟地址范围为:
00000000000000000000000000000000

0111111111111111111111111111111111111
10000000000000000000000000000000

1111111111111111111111111111111111111
即:0x00000000 ~ 0xFFFFFFFF,
这里就有2的32次方个地址。

即最大虚拟内存为2^32 Bytes = 4GB。

地址线是用来传输地址信息用的。举个简单的例子:cpu在内存或硬盘里面寻找一个数据时,先通过地址线找到地址,然后再通过数据线将数据取出来。
如果有32根.就可以访问2的32次方的空间,也就是4GB。也就是说,地址线一次确定一个存储单元,地址线上值可能取的所有组合确定了存储单元的个数。所以,存储单元的个数=2^32地址线的条数。我们常说的存储容量就是指存储单元的个数。存储容量=4GB
这里是因为存储单元与字节存在一个一对一的关系,一个存储单元占一个字节。
为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址。

指针变量的大小

类型大小都是4字节-如果当前平台是个32位平台,编译出32位的程序,里面不管什么类型的地址都是四个字节,因为地址的大小都是一样的 char,short…类型变量的地址也是个地址

int main()
{	
	printf("%d\n", sizeof(char*));//4或8
	printf("%d\n", sizeof(short*));//4或8
	printf("%d\n", sizeof(int*));//4或8
	printf("%d\n", sizeof(long*));//4或8
	printf("%d\n", sizeof(long long*));//4或8
	printf("%d\n", sizeof(float*));//4或8
	printf("%d\n", sizeof(double*));//4或8
	return 0;
}

在32位的机器上,32个0或者1组成的地址线,用4个字节的空间来存储,所以一个指针变量的大小是4个字节
在64位的机器上,如果有64个地址线,一个指针变量的大小是8个字节,才能存放一个地址

总结:
32位机器或平台上指针大小是4个字节
64位机器或平台上指针大小是8个字节

指针和指针类型

我们都知道,变量有不同的类型,整型,浮点型等。
那指针有没有类型?
有类型.
指针类型有非常重要的意义!

int num = 10; 
p = &num;

要将&num(num的地址)保存到p中,我们知道p就是一个指针变量,那它的类型是怎样的呢?
我们给指针变量相应的类型。

char  *pc = NULL;
int   *pi = NULL;
short *ps = NULL;
long  *pl = NULL;
float *pf = NULL;
double *pd = NULL;

这里可以看到,指针的定义方式是: type + * 。
其实:
char* 类型的指针是为了存放 char 类型变量的地址。
short* 类型的指针是为了存放 short 类型变量的地址。
int* 类型的指针是为了存放 int 类型变量的地址

下面让我们看一个例子说明指针类型的重要性

int main()
{
	int a = 0x11223344;
	int* pa = &a;
	*pa = 0;

	return 0;
}

当我们取地址a放到pa中
可以看到a的地址和pa指针变量相同,说明a的地址已经存放到pa中了
在这里插入图片描述
当pa是整型指针时,对pa进行解引用 找到了a赋值为0,a被修改了四个字节的数据在这里插入图片描述
那我们来看看pa为 char类型指针

int main()
{
	int a = 0x11223344;
	char *pa = &a;
	*pa = 0;

	return 0;
}

可以看到a的地址和ac指针变量相同,说明a的地址已经存放到pa中了
在这里插入图片描述
当我们pa是char类型指针时,对pa进行解引用只修改了一个字节的数据
在这里插入图片描述

这里只有指针类型不同,说明了指针类型决定了,在被解引用时访问权限有多大 整型指针解引用访问四个字节 字符指针解引用访问一个字节
指针类型其实是一种看待空间的方法 例如 创建一个整型变量,向后访问认为都是四字节整型,所以我们以后使用指针类型时想要以什么方式访问数据,就用什么指针

指针±整数

指针加减数字表示的意义是指指针再数组中位置的移动,对于整个整数部分而言,它代表的是一个元素,对于不同的数据类型,其数组的元素占用的字节是不一样的
指针+1,不是再指针地址的基础之上加一个地址,而是在这个指针地址的基础上加1个元素占用的字节数

在这里插入图片描述
例如:
在这里插入图片描述
为什么?
就是因为指针类型的差异 整型指针 使用一次访问四个字节 字符指针 使用一次访问一个字节

指针类型决定了我们看待问题的视角
char *p 对p解引用 可以向后访问一个字节 p+1向后访问一个字节
在这里插入图片描述

指针的解引用

指针类型发生变化 操作范围也发生变化 指针类型决定了 指针在被解引用的时候访问的权限
整型指针解引用访问四个字节
字符指针解引用访问一个字节

下面我们用个例子来理解一下:

假设你希望访问这40个字节的时候,是以字符为单位访问的

int main()
{
	int arr[10] = { 0 };
	//假设你希望访问这40个字节的时候,是以字符为单位访问的
	char* p = (char*)arr;//char*进行强制类型转换
	int i = 0;
	for (i = 0; i < 40; i++)
	{
		*p = 'x';
		p++;
	}
	return 0;
}

在这里插入图片描述

/假设你希望访问这40个字节的时候,是以整型为单位访问的

int main()
{
	int arr[10] = { 0 };
	//假设你希望访问这40个字节的时候,是以整型为单位访问的
	int* p = arr;	
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*p = 0x11223344;
		p++;
	}
	return 0;
}

在这里插入图片描述

总结: 指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。

野指针

概念:野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)

野指针的成因

1.指针未初始化

局部变量指针未初始化,默认为随机值,指向的空间不属于当前程序,但是能找到一块空间
这里的p是野指针 ,指向的空间是未知的,如果不知道给p赋值为什么,可以传入空指针

int main()
{
	int* p;                            

	*p = 20;
	return 0;
}

2.指针越界访问

当指针指向的范围超出数组arr的范围时,造成越界访问,所以p就是野指针

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int i = 0;
	int* p = arr;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *p);
		p++;//当指针指向的范围超出数组arr的范围时,造成越界访问,所以p就是野指针
	}
	return 0;
}

**3.指针指向的空间释放

主函数按顺序进行,进入test函数内部a创建,出函数a销毁,a销毁内存还给操作系统 不属于该程序内容了,如果再使用就造成野指针问题

int* test()
{
    int a = 10;
    printf("%d\n",a);
    return &a;
}
int main()
{
    int *p = test();
    *p = 20;
    printf("%d\n",p);
    return 0;
}

在这里插入图片描述

这是编译器会提示你,a不属于当前程序,因为已经被释放了

在这里插入图片描述

如何规避野指针

1.指针的初始化
2.小心指针越界
3.指针指向空间释放及时放置NULL 让我们不使用指针的时候,及时放置空指针
4.避免返回局部变量的地址
5.指针使用之前检查有效性 指针不为空 才能去使用它

指针运算

指针的关系运算

简单点来说就是指针的比较大小
标准规定: 允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较
但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
在这里插入图片描述
允许vp与向后越界的p1(代表values[4]后一个元素的地址)进行比较
但不建议vp与向前越界的p2(代表values[0]前一个元素的地址)进行比较

指针-指针

前提:指针-指针是,两个指针必须指向同一块空间

char ch [5] = 0;
int arr [6] = 0;

&arr[4]-&ch[3];//err  错误的

指针与指针相减,表示两个指针指向的内存位置之间相隔多少个元素(注意是元素,并不是字节数),准确的来说时指针减指针的绝对值得到的是指针和指针之间元素的个数

在这里插入图片描述

在这里插入图片描述
我们也可以用指针-指针来实现strlen函数

int my_strlen(char* str)
{
    char* start = str;
    while(*str)//如果str不为0,就++,str为0时停止循环找到'\0';
    {
        str++;    
    }
    return str-start;
}
int main()
{
    char arr[] = "abcdef";
    int len = my_strlen(arr);
    printf("%d\n",len);
    return 0;
}

指针和数组

数组是通过指针访问的

#inclue <stdio.h>
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    printf("%p\n",arr);//数组名
    printf("%p\n",&arr[0]);//数组首元素地址
    return 0;
}

在这里插入图片描述

可以说明数组名和数组首元素的地址一样 那我们来测一测arr和arr[0]的大小是否一样?咦,这里输出40,为什么是整个数组的大小呢?
在这里插入图片描述
数组名就是数组首元素的地址
但是有两个例外:
1.sizeof(数组名),数组名不是数组首元素的地址,数组名表示整个数组,计算的是整个数组的大小
2.&arr(取地址数组名),数组名不是数组首元素的地址,数组名表示整个数组,取出的是整个元素的地址

数组是可以通过指针来访问的,我们可以通过p来把arr数组所有元素的找到
可以说明,数组在内存中是连续存放的,当给一个数组的首元素地址,通过指针的加减整数运算,向后一个整型一个整型的进行解引用访问

在这里插入图片描述

数组和指针一回事吗?
数组是一块连续的空间,这里面可以放很多元素
指针是存放地址的变量
但是可以通过指针来访问数组
p+i 得到的就是arr[i]的地址
在这里插入图片描述

int main()
{
	int arr[10] = { 0 };
	printf("%p\n", arr);//数组名
	printf("%p\n", arr+1);

	printf("%p\n", &arr[0]);//对首元素取地址
	printf("%p\n", &arr[0]+1);

	printf("%p\n", &arr);//对数组名取地址
	printf("%p\n", &arr+1);

	return 0;
}

首元素加1跳过一个元素
数组+1跳过一个数组
在这里插入图片描述

二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?

int main()
{
	int a = 10;
	int* pa = &a;//pa是指针变量(一级指针)
	int* * ppa = &pa;//ppa是二级指针
	return 0;
}

上面这个例子就是二级指针,我们如何理解二级指针的?
在这里插入图片描述

说明pa是指针
int说明pa指向的对象是int类型
说明ppa是指针
int
说明pa指向的对象是int
类型的

如何通过ppa找到a?
*ppa可以找到pa
*pa可以找到a 所以**ppa可以找到a变量

指针数组

指针数组是指针还是数组?
答案:是数组。是存放指针的数组
数组有整型数组,字符数组

int arr1[5];//整型数组 存放整型的数组
char ch[5];//字符数组 存放字符的数组 int*
arr2[5]//指针数组 存放指针的数组

在这里插入图片描述
那么类比一下,不难想到指针数组的储存方式
在这里插入图片描述

举个简单的例子这就是指针数组:

int main()
{
	int a = 11;
	int b = 12;
	int c = 13;
	int d = 14;
	int e = 15;
	int* arr[5] = { &a,&b,&c,&d,&e };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d\n", *(arr[i]));
	}

	return 0;
}

我们可以使用指针数组简单来实现个二维数组

int main()
{
	int arr[] = { 1,2,3,4 };
	int arr1[] = { 2,3,4,5 };
	int arr2[] = { 3,4,5,6 };
	int arr3[] = { 4,5,6,7 };
	int* pa[4] = { arr,arr1,arr2,arr3 };
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			printf("%d ", *(pa[i] + j));
		}
		printf("\n");
	}
	return 0;
}

在这里插入图片描述

个人水平不足 如果代码中有错误,可以多多在评论区指出,一定会及时修改!
谢谢大家看到这里 觉得有收获的话可以三连一下 一起加油!

在这里插入图片描述

  • 32
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值