指针的深度剖析(小白模式)

Posted 雨轩(小宇)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了指针的深度剖析(小白模式)相关的知识,希望对你有一定的参考价值。

初级指针

1. 指针是什么

指针是什么?

  • 在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。
  • 指针就是变量,用来存放地址的变量(存放内存单元的地址)
    在这里插入图片描述
    代码举例
#include <stdio.h>
int main()
{
 	int a = 10;//在内存中开辟一块空间
 	int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
   //将a的地址存放在p变量中,p就是一个之指针变量。
 	return 0; 
 }

我们知道了指针是用来存放内存单元的地址,但指针的大小我们还不知道,请接着看下面

一个小的单元到底是多大的字节那?

答案:一个字节

编址过程
对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的是产生一个电信号正电/负电(1或 者0)
那么32根地址线产生的地址就会是:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001

111111111 111111111 111111111 111111111
每个地址标识一个字节,这里就有(2^32Byte == 2^32/1024KB ==
232/1024/1024MB==232/1024/1024/1024GB == 4GB) 4G的空闲进行编址。
64位机器类推
所以在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,在64位上地址就得用8个字节的空间来存储。
所以在32位机器上,指针占4个字节,64位机器上,指针占8个字节。

2. 指针和指针类型

首先,C语言有多种不同的内置数据类型,当然也有相关指针的类型,指针类型如下:

char  *pc = NULL; //指向char型的指针
int   *pi = NULL; //指向整形的指针
short *ps = NULL; //指向short型的指针
long  *pl = NULL; //指向long型的指针
float *pf = NULL; //指向单精度浮点型的指针
double *pd = NULL;//指向双精度浮点型的指针

那指针类型存在的意义是什么?

指针的类型决定了指针向前或者向后走一步有多大(距离)。

看下面的例子:

#include <stdio.h>
//演示实例
int main()
{
	 int n = 10;
	 char *pc = (char*)&n; //这里截断,数据存储里面讲到了的
	 int *pi = &n;
	 
	 printf("%p\\n", &n);//n的地址
	 printf("%p\\n", pc);//n的一个字节的地址
	 printf("%p\\n", pc+1);//往下一个地址走
	 printf("%p\\n", pi);//n的地址
	 printf("%p\\n", pi+1);//n下一个内存空间的地址,跟n一样(4个字节)
	 return  0; 
}

指针的解引用

//演示实例
#include <stdio.h>
int main()
{
 int n = 0x11223344;
 char *pc = (char *)&n;
 int *pi = &n; 
 *pc = 0;   //重点在调试的过程中观察内存的变化。
 *pi = 0;   //重点在调试的过程中观察内存的变化。
 return 0; }

在这里插入图片描述
通过调试可以看到内存地址的一些变化。(根据类型不同内存地址会有变化)
总之:指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。

3. 野指针

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
野指针出现的原因:

  • 1.指针未初始化
#include <stdio.h>
int main()
{ 
	int *p;//局部变量指针未初始化,默认为随机值
    *p = 20;
	return 0; 
}
  • 2.指针越界访问
#include <stdio.h>
int main()
{
    int arr[10] = {0};
    int *p = arr;
    int i = 0;
    for(i=0; i<=11; i++)
   {
        //当指针指向的范围超出数组arr的范围时,p就是野指针(越界相当于未初始化)
        *(p++) = i;
   }
    return 0; 
}
  • 3.指针指向的空间释放
	char *Ptr = NULL;
	Ptr = (char *)malloc(100 * sizeof(char));
	if (NULL == Ptr) //检查指针的有效性
	{
	exit (1);
	}
	//如果不继续使用该指针,需要对char型Ptr指针进行的空间释放
	//加上free(Ptr)释放空间,最后置为NULL,避免形成野指针
	//如果没加,就会造成指针指向位置不明确,也就成了野指针。

如何避免野指针

  • 1.指针初始化
  • 2.小心指针越界访问
  • 3.指针指向空间释放即使置NULL
  • 4.指针使用之前检查有效性

4. 指针运算

指针±整数

	float values[5];
	float *vp=NULL;
	//指针+-整数;指针的关系运算
	for (vp = &values[0]; vp < &values[5];)
	{
	     *vp++ = 0; }
	//这里通过指针的+整数,将values数组的每个值都初始化为0了

指针-指针

int my_strlen(char *s) {
       char *p = s;
       while(*p != '\\0' )
              p++;
       return p-s; }//通过p指针找到s指针的末尾,然后p-s计算字符串s的长度

指针的关系运算

for(vp = &values[4]; vp >= &values[0];vp--)
{
    *vp = 0; }

标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

5. 指针和数组

数组名是数组首元素的地址

#include <stdio.h>
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    printf("%p\\n", arr);
    printf("%p\\n", &arr[0]);
    return 0; }

结果显示如下:
在这里插入图片描述
那么我们可以通过指针来访问数组中的元素

	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	int* p = arr;//通过指针指向数组首元素地址
	int len = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < len; i++)
	{
		printf("%d %p\\n", *(p + i),p + i);
	}

通过p指针输出结果:
在这里插入图片描述
地址显示:
在这里插入图片描述
这里的p+i,虽然i是增加的1,但内存地址中其实是跳的4个字节(也就是一个整形),而非这里的 i ,所以才能依次访问数组中的元素(注意区分!!!)
所以 p+i 其实计算的是数组 arr 下标为i的元素的地址

6. 二级指针

指针变量也是变量,也有地址,所以二级指针用来存放指针变量的地址。
在这里插入图片描述

	int a = 10;
	int* p = &a;//存放a的地址
	int** pa = &p;//存放指针变量p的地址
	*pa = &a;
	**pa = 20;
	//等价于*p = 30;
	//等价于a = 30;
	printf("%d %d %d", a, *p, **pa);
  • *pa通过对pa的地址进行解引用,这样找到的是p,*pa就是访问的p
  • **pa先通过*pa找到p,然后对p进行解引用操作:*p,找到的就是a
    在这里插入图片描述

指针的进阶

1. 字符指针

2. 数组指针

3. 指针数组

4. 数组传参和指针传参

5. 函数指针

6. 函数指针数组

7. 指向函数指针数组的指针

8. 回调函数

9. 指针和数组面试题的解析

以上是关于指针的深度剖析(小白模式)的主要内容,如果未能解决你的问题,请参考以下文章

深度剖析智能指针

C语言之深度剖析数据在内存中的存储

《C语言深度剖析》第四章 指针和数组 p2 C语言从入门到入土(进阶篇)

观察者模式深度剖析

深度剖析Istio共享代理新模式Ambient Mesh

深度剖析 Seata TCC 模式图解 + 源码分析