指针真的很容易——初阶指针详解

指针

目录

1. 指针是什么?

2.指针和指针类型

3.野指针

4.指针运算

5.指针和数组 

6. 二级指针

7.指针数组


1. 指针是什么?

指针理解的2个要点:

1. 指针是内存中一个最小单元的编号,也就是地址

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

1.怎么理解指针是内存中一个最小单元的编号,也就是地址

我们的买的电脑一般都是8G或者16G的内存

那这些内存空间是如何管理的呢?

计算机是把这些内存空间切割成内存单元 ——1个内存单元的大小为1byte

84220ec8cf774a17bab98235beff438a.png

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

d86cfcd3d5154e8fbdab69170396a818.png

 我们创建一个整形变量a,a在内存中如图所示占4个字节的内存空间,里面存放了10。现在我们用pa这个指针变量来存放a的地址。

下面是通过监视来证明pa里面存放的是a的地址。

77d8b45855204382845b34bf976a6c52.png

总结:指针变量里面存放的是地址,通过这个地址,就可以找到一个内存单元。 

3.计算机是如何编址的?

这里顺便说一下计算机是如何编址的(蛮重要的)

对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电 平(低电压)就是(1或者0);
那么32根地址线产生的地址就会是:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
...
11111111 11111111 11111111 11111111
这里就有2的32次方个地址。
每个地址标识一个字节,那我们就可以给 (2^32Byte == 2^32/1024KB ==
2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G的空闲进行编址。
同样,64位机器,就有64根地址线,有2的64次方个地址。
  在32 位的机器上,地址是32个0或者1组成二进制序列,每一个0或者1占一个比特(bit)的内存空间,一个字节=8个比特位,那 地址就得用 4个字节的空间来存储,所以
一个 指针变量的大小就应该是4个字节
如果在64 位机器上,如果有64个地址线,那 一个指针变量的大小是 8个字节,才能存放一个地
址。  
总结指针(指针变量)的大小在32位平台(X86)是4个字节,在64位平台(X64)是8个字节。

2.指针和指针类型

1.指针类型

我们都知道,变量有不同的类型,整形,浮点型等。那指针有没有类型呢?

有的,比如:

int a=0;

int* pa=&a;

这里的指针pa的类型是int* 它的意思是,pa这个指针存放的是int类型的变量的地址。

同样

double b=0;

double* pb=0;

那么pb这个指针它的类型就是double*,意思是pb存放的是double类型的变量的地址。

2.指针的解引用

4fb9ace5824844c0b6ed9a0b36ffeade.png

pa存放的是a的地址,在pa前面加上*号,就可以对pa进行解引用,通过解引用我们就可以通过pa里面存放的a的地址来找到a,并对a进行操作。所以*pa=0等价于a=0

 我们创建了一个整形变量a初始化为0x11223344,然后把a的地址给pa,最后对pa进行解引用,把0赋给a。

 b5bc6b4142254fa7bc8ab5eaaacde75e.png

首先通过调试我们可以看到到当程序走完int a =0x11223344时,a的地址存放的是44 33 22 11我们可以把它倒过来读,也就是我们的11 22 33 44

8b5f06d877b6418da5a1e3212d03fbda.png

可以看到当程序走完*pa=0时,a的值就变成了0,里面存放的都是0。

c544f4fef50343b2855053c0f182ec68.png我们现在把 pb的指针类型改成char*类型,然后和上面进行同样的操作,通过解引用把0赋值给a

6e9da85189104a3c814a2a94beaaddce.png

可以看到,虽然char*类型也能存放a的地址(因为指针存放的是地址,在32位平台上,大小都为4个字节) 。但是我们对pb进行解引用时,它只访问了一个字节。只是把44改成了0,所以它输出的结果不是0

 补充一下:一个16进制数比如0x11223344占4个字节,一个十六进制数字比如这里面的最后一个数字4,它翻译成4个2进制位,也就是4个bit位。那么2个16进制数字就是1个字节。所以16进制数字占4个字节。

为了方便大家理解,这里给大家翻译一下0x11223344

0x11223344

0001 0001 0010 0010 0011 0011 0100 0100

结论

指针类型决定了指针在被解引用时,访问几个字节

例:如果是int*类型的指针,解引用访问4个字节

如果是char*类型的指针,解引用访问1个字节

3.指针+-整数

b2ece1551083485faef660073f09bd59.png

 可以看到pa,和pb存放的都是a的地址,但是当它们+1时,它们的地址不相同。

pa+1,它的地址跳过了4个字节。pb+1,它的地址跳过了1个字节。

结论

指针的类型决定了,指针+-1操作时,跳过几个字节。

通俗点说:指针的类型决定了,指针的步长。

3.野指针

1.指针未初始化

b3cda5d101e74bc9991381ec2a40acba.png

我们这里没有给指针变量p初始化(就是没有给它一个明确的地址),在C语言中,如果不给指针初始化的话,它里面存放的是随机值。当我们对p进行解引用时,它是把生成的随机值当作地址,然后访问这个地址。这样做是非法的,这里的p就是野指针。

2.指针越界访问

当指针指向的范围超出数组的范围时,p就是野指针

4d352bc0c0e84f5d92caf5f4e9e21282.png

375c11ef2eca40329c6a6b82446b49c0.png

3. 指针指向的空间释放

04af5c24753d4e2b89f30b2de2e49cf2.png

我们这里的p也是野指针,因为a是一个局部变量,当a出代码块{}时,a就已经销毁了,我们用p存放a的地址(&a),p里面存放的a的地址已经不能使用了。

4.如何避免野指针

1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放即时置NULL
4. 避免返回局部变量的地址
5. 指针使用之前检查有效性

#include <stdio.h>
int main()
{
   int *p = NULL;
   int a = 10;
   p = &a;
   if(p != NULL)
  {
       *p = 20;
  }
   return 0;
}

4.指针运算

1.指针+-整数

现在有一个初始化为0的数组arr[10]=0,我们如何把它里面的每一个元素赋值为1?

没学数组之前大家应该都是这样写的

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	//sizeof(arr)计算的是整个数组的大小
	//sizeof(arr[0])计算的是数组首元素所占空间的大小
    //sz得到的就是数组有多少个元素
	for (i = 0; i < sz; i++)
	{
		arr[i] = 1;
	}
	return 0;
}

辣么学完指针后,我们还可以怎么写呢?

int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++)
	{
		*p = 1;//对p进行解引用,把p所指向的对象赋值为1
		p++;//这里的p++相当于p=p+1=arr+1
	}
	return 0;
}

还可以这样写:

int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++)
	{
		*(p + i) = 1;//这里其实就相当于把上个程序的*p=1和p++给合并了
		             //意思是先给p里面存储的地址+i,然后对它解引用。
	}
	return 0;
}

为了便于大家理解,给大家说下:当i=0时,*(p+i)相当于arr[0],当i=1时,*(p+i)相当于arr[1],当i=2时,*(p+i)相当于arr[2],以此类推……

2.指针-指针 

d8446458a86243b99936a7ad0d5427e2.png

 总结:指针-指针得到的是指针和指针之间的元素个数。

不是所有指针都能相减,指向同一块空间的2个指针才能相减。

指针-指针有什么作用呢?

这里给大家举个例子:

9cf489836dda4caba23235a559921d0b.png

我们在求字符串长度时,会用到strlen这个库函数,它可以帮我们求出字符串的长度。那我们可不可以自己设计一个函数来求字符串的长度呢?

fa9e8459b5434809b9cb3758c1529b03.png

学完指针-指针,我们还可以怎么写呢?

 b58efcd95e9546f08f4ac8aebf8e1fdf.png

5.指针和数组 

指针变量:是一个变量,里面存放的是地址

数组:一组相同元素的集合

那么它们之间有什么联系呢?

e6862780a61245e9a2068a599a230780.png

可以看到,p+i等价于&arr[i],*(p+i)等价于arr[i]、

我们可以用指针来访问数组:

#include <stdio.h>
int main()
{
 int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
 int *p = arr; //指针存放数组首元素的地址
 int sz = sizeof(arr) / sizeof(arr[0]);
 int i = 0;
 for (i = 0; i<sz; i++)
 {
 printf("%d ", *(p + i));
 }
 return 0;
 }

6. 二级指针

二级指针变量是用来存放一级指针变量的地址的

cfbed88944fc4c2c88d15c1466c7a5c7.png

 这里大家想到了什么?对,没错,就是它——洛必达法则,无限套娃【dog】

7.指针数组

指针数组:存放指针的数组

09c66fe9ab264edf81c1d4e71de4b078.png

我们也可以打印数组里面的内容

9a969d73dee446f9b1f10bee1e7455a1.png

 用指针数组模拟一个二维数组:

我们先写一个二维数组:

6af10a4d7c7347f581131dbf3a4e0a62.png

我们现在再用指针来模拟一个二维数组

 08973e3ef4624867ac01a77fb68c33e7.png

好啦,初阶指针的内容就给大家讲到这里啦,家人们如果觉得有帮助,还请来个一键三连【dog】 

评论 33
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值