前言
各位师傅好,我是qmx_07,今天来讲解指针的知识点,之后的内容会持续更新!
内存和地址
想要学习指针,我们就要先谈谈内存和地址 是怎么一回事
• 比如 我们在校园生活中,肚子饿了 有时候会不会 点个外卖,解决我们的饮食问题
• 但是 外卖小哥取餐之后,来到 宿舍公寓,这里的房间都长得差不多,不能挨个进门喊吧,那是不是效率就很差
• 只要 我们提供一个房间号,是不是就轻松很多,快速定位了呢
• 这个就是 地址的思想 (下面我们换算到计算器里)
(这边只是举个例子哦,正常情况下 外卖小哥 是送到校园门口,进不来的,只是引申概念)
在我们的计算机中,CPU想要处理数据,需要到内存里面读取,再存放回去,那我们是如何进行管理的呢?
• 内存会划分成一个个内存单元,每个内存单元会存储1个字节--存放着8个比特位
• 换算成现实生活中就是: 内存单元编号(门牌号),所以可以方便找到内存空间,在c语言中把这个单元编号又叫做指针
• 内存单元编号 == 地址 == 指针
理解了地址和内存的关系后,我们来看看 在c语言中创建变量的过程
如何理解编址
• 为什么要编址? 因为内存中 地址很多,需要进行管理
• 而 计算机中的编址是在硬件层面中完成的
• 必须理解,计算机内是有很多的硬件单 元,⽽硬件单元是要互相协同⼯作的。所谓的协 同,⾄少相互之间要能够进⾏数据传递。
• CPU和内存之间也是有⼤量的数据交互的,所 以,两者必须也⽤线连起来
我们需要关注一条线,地址总线
• 32位机器有32根地址总线, 每根线只有两态,表⽰0,1【电脉冲有⽆】,那么 ⼀根线,就能 •表⽰2种含义,2根线就能表⽰4种含 义,依次类推。32根地址线,就能表⽰2^32种含 义,每⼀种含义都代表⼀个地址
• 地址信息被下达给内存,在内存上,就可以找到 该地址对应的数据,将数据在通过数据总线传⼊ CPU内寄存器
• 而 32位操作系统 因此 能够管理 2^32=4,294,967,300 / 1024 / 1024 /1024 = 4G内存
64位操作系统,大家可以自行推导一下
• 32位平台下地址是32个bit位,指针变量⼤⼩是4个字节
• 64位平台下地址是64个bit位,指针变量⼤⼩是8个字节
指针和指针类型
指针变量和解引用操作符
我们通过取地址操作符(&)拿到的地址是⼀个数值,这个就是指针变量
再通过解引用操作符,获取数据(*)
画图表示的话:
因为pa 这个指针,里面存储着 a的地址,所以能根据地址,找到a,继而拿到值
指针变量类型的意义
我们先来看一下 这个例子:
为什么这里 都是将指针里面的内容改为0,结果不一样呢?
结论:指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)
char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节
指针 +- 整数
我们观察一下 不同指针变量+- 整数 会有怎样的变化?
char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节
结论:指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)
指针运算
指针+- 整数-for循环遍历数组
前面已经演示了 +-整数的效果,所以这边 是 怎么使用for循环,来遍历数组
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* parr = &arr[0];
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++)
{
printf("%d ", *(parr + i));
}
return 0;
}
思路:获取数组的长度,再根据for循环,拿到第一个数组的地址,再依次增长4个单位长度,获取全部值
指针-指针
我们来用指针 模拟实现strlen函数,strlen: 获取字符串的长度
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int my_strlen(char* s)
{
char* p = s;
while (*p != '\0')
{
p++;
}
return p - s;
}
int main()
{
char arr[] = "hello world";
int sz = my_strlen(arr);
printf("字符串的长度是:%d", sz);
return 0;
}
思路:保存原始指针地址的位置,再解引用地址 一直增加到\0终止位置,两者相减,得到字符串距离
野指针
概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
指针未初始化
#include <stdio.h>
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
指针越界访问
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* parr = &arr[0];
int sz = sizeof(arr) / sizeof(arr[0]);
for(int i = 0; i < 11;i++)
{
printf("%d ", *(parr+i));
}
return 0;
}
指针指向的空间已释放
#include <stdio.h>
int* test()
{
int n = 100;
return &n;
}
int main()
{
int*p = test();
printf("%d\n", *p);
return 0;
}
函数体里的变量 只在使用时有效,之后会进行销毁的操作,使用权限转移到操作系统,再去访问会出现问题
指针的有效性
int*p=NULL:
*p=10;
对空指针进行访问,也会出现问题
如何规避野指针
- 指针初始化
- 小心指针越界
- 指针指向空间释放即使置NULL
- 避免返回局部变量的地址
- 指针使用之前检查有效性
const修饰指针
• const作用:用来保护变量 不被修改
• const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。 但是指针变量本⾝的内容可变。
• const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指 向的内容,可以通过指针改变
总结
介绍了 怎么使用指针,指针运算、以及const符的作用。指针还有一部分内容 下节继续分享