【C语言】趣说指针(上篇)初识指针

发布于:2022-12-29 ⋅ 阅读:(259) ⋅ 点赞:(0)

目录

前言

一.什么是指针

二.什么是指针变量

 三.二级指针

四.指针类型的意义

1.指针的访问权限

2指针的运算

1.指针类型也决定了我们指针的运算

2.如果指针减指针会发生什么

五.指针变量的大小

六.野指针

1.什么是野指针

2.如何规避野指针

1.首先注意指针的初始化

2.避免指针越界

3.指针指向的空间释放时及时的将其置为NULL空指针

4.避免返回局部变量的地址


 

前言

在本篇中我们讲述指针的基本概念与用法,知识点中涉及的堆区,栈区,静态区的概念我们在本篇中暂时忽略,后面会写一篇文章简要的讲述一下相关的内容。

一.什么是指针

我么知道数据是在内存中存放的,当我们想要访问内存中的某一特定区域时就需要用到了指针,例如我创建了一个局部变量int a=4而我如果想直接操作这个数字所在的内存空间将其更改。而指针又是如何实现的呢?

我们知道内存中是存在地址这个概念的,什么是地址呢,地址呢就是地址。 055bfde5af96422fb5a71b3e46bd0d00.png

当然这里并不是说一些废话,而是地址我们可以简单的理解为字面意思。假设内存是一个正在建设的小镇,这个小镇里面按一个字节的大小整齐的划分成了一个一个的区域,我们给这些区域按顺序编上了号,那么这些编号就是对应区域的地址。而地址就是指针

e5c84a8e3b0f40b3bcf4c0266c1d98b7.png

当我们需要一片空间来盖房子或者 干其他事就需要向内存申请一片空间。

9f5e60825fad4438bfd7aeefe08f2087.png

既然有了指针我们又该怎么去用呢,这个时候我们便需要一个指针变量去存储我们的指针。

二.什么是指针变量

int main()
{
 int a=8;
 int* p=&a;
 return 0;
}

这个p就是我们用来存放指针a的指针变量。

当我们想要修改a的时候只要使用我们的解引用操作符*对p进行解引用就可以直接操作其中的内容了

int main()
{
 int a=8;
 int* p=&a;
 *p=2;
 return 0;
}

此时我们的a中存放的数值就改成了2。

简单的来说就是我们假设a的地址是0x175864BF,我们在内存中开辟的这片内存空间就是一个宝箱

而地址就是这个宝箱的编号

c2de0a1483a54b71ba8d14910b86630f.png

(宝箱是我自己随手画的,很丑,还请见谅)

 其中指针p里边就是存放的就是这个编号,通过这个编号我们就找到了这个宝箱。

当我们在p前面加上*写成*p就可以找到这个宝箱,拿走或者放入东西了。

 三.二级指针

我们既然提到了指针变量本身也是存放的一组数据(地址),那么指针变量它本身也是有地址的。

那么这个地址又能不能存放到一个指针变量之中呢,当然是可以的。

那么这个存放指针变量的的指针也就是二级指针。

以上面那个宝箱为例,既然宝箱有编号,那么我们就可以把这张编号写在纸上,将这张纸放入另一个宝箱,那么存放这张纸的宝箱肯定也有自己的编号(地址),这个存放宝箱编号的宝箱的编号(地址)就是二级指针。

当然这里只是为了更形象的说明才举了这样一个例子,实际存储中并没有纸这个介质,数据是直接存在内存空间中的。

int main()
{
 int a=8;
 int* b=&a;
 int* c=&b;
 **c=2;
 printf("%d",a);
 return 0;
}

0e31edd7cde3408f918634d08f0debe7.png

 当我们解引用一次的时候,找到c宝箱的位置里边放的是一个地址,也就是另一个宝箱的编号。在次解引用找到名为a的宝箱,将里边放的2换成了8。

当然这些描述对于比较熟悉C语言的人来说可能会感到相当糟糕,因为实际上指针也就是地址实际上是一片空间的首位(或者说低地址处)的地址,根据指针的类型来决定它的访问权限的,这时候就要提到我们的指针类型的意义了。

四.指针类型的意义

1.指针的访问权限

我们已经知道了一个int开辟的是一个4个字节的空间,而我们每一个字节都是有自己的地址的,那么当我们使用一个int*的指针变量时它其中又是怎么存放的地址呢

9f5e60825fad4438bfd7aeefe08f2087.png

 假设我们如图中所示开辟了一片空间用来存放数据a,如果此时我们给了一个p的指针变量去存放a的地址,那么p中存放的就是4个字节中3这个位置的地址。

而类型就决定了我们会从3开始往后访问几个字节,如int*我们会从3的最左端开始往后访问4个字节,而使用char*的话只会从左端开始访问一个字节也就是3这个字节。

2指针的运算

1.指针类型也决定了我们指针的运算

以下面这段代码为例

int main()
{
	char ch = 5;
	int a = 5;
	int* pa = &a;
	char* pc = &ch;
	printf("%p\n", pa);
	printf("%p\n", pa+1);
	printf("%p\n", pc);
	printf("%p\n", pc+1);
}

7280703c0b55402cac9d0ddf07372dbb.png

 我们可以看到pa+1跳过了一个int的大小4个字节,而pa+1跳过了一个char 类型的大小1个字节。

2.如果指针减指针会发生什么

首先指针减指针的话,两个指针必须指向的是一片连续的空间例如arr[6],里面arr[0]到arr[6]就是一片连续的空间

关于指针减指针我们可以通过下面一段代码进行分析

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

24fbbd826d7d4956a1b3ac972a725bde.png

 

五.指针变量的大小

 既然p是存放我们地址的,那么p本身也就是一组数据,那么自然也就会在内存中开辟一片空间。

而这片空间的大小在32位平台和64位平台环境下是有所差别的

这是因为32位系统的最大寻址空间是2的32次方=4294967296(bit)= 4(GB)左右,64位系统的最大寻址空间为2的64次方方。所以对指针的大小也就有所改变,在32位x86环境下指针变量的大小是4个字节(32个比特位),而在x64环境下就是8个字节。

六.野指针

1.什么是野指针

int main()
{
 int *p;
 return 0;
}

在实际编写代码中如果意外使用了野指针可能会导致难以发现的bug。

2.如何规避野指针

1.首先注意指针的初始化

避免出现未完全初始化的指针

2.避免指针越界

int main()
{
  int arr[5]={0,1,2,3,4};
  int* p=arr;
  *(arr+5)=1;
  return 0;
}

 注意此时就出现了指针越界。

3.指针指向的空间释放时及时的将其置为NULL空指针

#include<stdlib.h>
int main()
{
 int* p=malloc(40);
 free(p);
 p=NULL;
}

 malloc是动态内存分配,当我们给他一个40时他就会开辟一块40字节的空间给我们,返回值是指向这片空间开头的指针,当然我们在这里就不详细讲解malloc了;

free的作用释放内存,当我们free(p)时这个块的内存空间就释放了,此时指针就没有了明确指向,所以我们使用p=NULL;将其置为一个空指针避免其成为野指针。

4.避免返回局部变量的地址

int* fun()
{
 int a=8;
 return &a;
}
int main()
{
 int* p=fun();
}

像这种里面的指针p也是一个野指针, 我们知道a是一个局部变量,当离开它的作用域之后,它的生命周期也就结束了,此时指向a的空间已经释放。

这些大概就是指针的部分基本概念与注意事项了,下一篇文章再带大家更多的认识一下指针和它们能做什么。

 

 


网站公告

今日签到

点亮在社区的每一天
去签到