IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【C语言基础之指针初阶】(1) -> 正文阅读

[C++知识库]【C语言基础之指针初阶】(1)

?hello,愿意点进来的小伙伴们,你们好呐!?
🐻🐻系列专栏:【C语言基础】
🐲🐲本篇内容:浅学指针基础
🐯🐯作者简介:一名大一即将升大二的三非编程小白

前言

指针是什么?
内存空间怎么管理呢?
切割成内存单元 – 1 byte(字节) – (最小单元)
每一个内存单元都有z自己独有的编号,这个编号就叫做地址(地址也称为指针)
指针其实就是地址,地址就是指针
指针就是内存单元的编号。

指针所占的内存大小

我们一般口中说的指针其实就是指针变量,指针变量本质上就是存放地址的变量,因此指针本质上也是一个地址,如 &a 是取出第一个字节的地址,该地址其实是一个数值。我们可以通过这个地址来找到一个内存单元。


int main() {
	int a = 10;//a 是整型变量,占4个字节的内存空间。
	int* pa = &a;
	//&a 是取出第一个字节的地址,该地址其实是一个数值。我们可以通过这个地址来找到一个内存单元。
	//pa 是一个指针变量用来存放地址的,
	//然后我们经常口头语的将 pa 说成指针。
	//本质上指针就是地址,
	//口语中说的指针,其实是指针变量,指针变量就是一个变量,指针变量就是用来存放地址的变量。(**存放在指针中的值都被当做地址处理)
	//总结:
	//指针变量存放地址的,地址是唯一标示一块地址空间
	//指针的大小在 32 位平台是 4 个字节,在 64 位平台是 8 个字节 (32 位 为32条地址线)
	return 0;
}

总结:
1.指针变量存放地址的,地址是唯一标示一块地址空间
2.指针的大小在 32 位平台是 4 个字节,在 64 位平台是 8 个字节 (32 位 为32条地址线)。例如:

在这里插入图片描述
该代码运行环境位 X86 (32位),所以该环境下的指针内存大小都为 4 个字节

指针和指针类型

指针也是有类型的,指针类型有什么意义呢?

int main() {
	int a = 0x11223344;
	/*int *p = &a;
	*p = 0;*/
	//也可以强制类型转换
	char* pc = (char*)&a;//int* 
	*pc = 0;
	//结论:
	//1.指针类型决定了指针在被解引用的时候访问了几个字节
	//2.如果是 int* 的指针,解引用的时候访问了 4 个字节
	//3.如果是 char* 的指针,解引用的时候访问了 1 个字节
	//推广到所有类型
	return 0;
}

> 结论:
1.指针类型决定了指针在被解引用的时候访问了几个字节
2.如果是 int
的指针,解引用的时候访问了 4 个字节
3.如果是 char
的指针,解引用的时候访问了 1 个字节
推广到所有类型**

例1:

在这里插入图片描述

pa 的指针类型为 int* – 该类型能访问 4 个字节的内存空间
pc 的指针类型为 char* – 该类型可以访问 1 个字节的内存空间
pa + 1 指在 pa 后面的另一个元素的地址。
在这里插入图片描述
pc + 1 指在 pc 后面的另一个元素的地址。
由上面的例子看出,pa + 1 跳过了 4 个字节, pc + 1跳过了 1 个字节。
可以得出:
由此可见,指针类型决定了指针 ± 1操作的时候跳过多少个字节
决定了指针的步长
因为指针类型 决定了解引用的时候在内存中访问了多少个字节。(访问权限)

然后一定要注意:
指针变量是 4 个字节
和指针变量的在内存中访问的内存空间不同(解引用)

不要将指针变量内存和指针变量的在内存中访问的内存空间搞混。
🐞🐞指针变量的编号是 4 个字节,但是他在内存中可以访问多少个字节的内存空间,要看指针类型🐞🐞

例2:

int main() {
	int a = 0;
	int* pi = &a;//pi 解引用访问四个字节, pi + 1 跳过 4 个字节
	float* pf = &a;//pf 解引用访问四个字节, pi + 1 跳过 4 个字节
	//int* 和 float* 是不是可以通用
	//不可以
	//两个指针变量内存中存放的值不同类型
	return 0;
}

虽然说,两个指针类型的访问内存空间大小相同,但是不可以将两个该指针类型的指针变量通用,因为它们在地址中存放的值是不同类型的。

野指针

🐞🐞野指针指的是一个指针指向的位置是未知的,不确定的。

例1:没有初始化

int main() {
	int* p;
	//p 没有初始化,就意味着没有明确的指向,
	//一个局部变量没有初始化,放的是随机值:0xcccccccc
	//
	*p = 10;
	//这时候 *p 是非法访问内存 , 这里的 p 就是野指针。
	//因为 0xcccccccc 是空间随机赋值的,并不是我们自己初始化的值
	return 0;
}

因为指针变量 p 没有初始化值,所以在内存中该指针指向一个随机值,这种情况下 *p 就是非法访问内存。
🐝🐝p 就称为野指针。

例2:数组下标越界

int main() {
	int arr[10] = { 0 };
	int* p = arr;//arr是首元素地址,p 相当于 arr[0];
	int i = 0;
	for (i = 0; i <= 10; i++){
		*p = i;
		//当指针指向的范围超出数组arr的范围时,p就是野指针
		p++;
	}
	return 0;
}

arr是首元素地址,p 相当于 arr[0] , 在for循环中,指针 p 已经指向arr数组外的一个地址,指向访问超过了数组的访问,这时候 p 就是野指针

例3:函数栈帧的销毁

函数栈帧的销毁的销毁属于比较难理解的一个原因,在这里我举个简单的例子:

int* test() {
	int a = 10;
	return &a;
}
int main() {
	int* p = test();
	//test 将 a 的地址返回过来给 p 
	// 但是 p 就是一个野指针
	//因为 a 是局部变量,当 p 接收到 a 的地址后
	//a 这个局部变量就会销毁 , 所以 p 访问不到 a 的地址
	//所以 p 就是野指针
	return 0;
}

🐛🐛test 将 a 的地址返回过来给 p ,
因为 a 是局部变量,当 p 接收到 a 的地址后,
a 这个局部变量就会销毁 , 所以 p 访问不到 a 的地址,
🐛🐛所以 p 就是野指针.

所以在知道了野指针的形成条件后,我们以后在写代码的过程中要注意一些事项,养成好习惯
1.

🐠🐠 我们要明确的初始化变量,
🐠🐠就算不知道初始化多少也要赋值为 NULL

	int a = 10;
	int* p = &a;
	int* p2 = NULL;//
	*p2 = 100;
	//然后因为 p2 是 NULL,所以我们无法访问到该地址
	//因此要做个约定
	//在访问该地址时先判断:
	
	if (p2 != NULL) { // 判断 p2 是否为空指针,不过对于全局变量栈帧的销毁没有有效的防范
		printf("%d\n", *p2);//结果为 10 ,是因为虽然 a 被销毁了,但是原本 a 的地址还是存放 10
		//但是不一定每一次都是 10
	}

野指针的防范

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

指针运算

减法

指针可以进行相减运算(指针 - 指针)

int main() {

	int arr[10] = { 0 };
	printf("%d\n", arr[9] - arr[0]);
	printf("%d\n", arr[0] - arr[9]);
	//指针减指针得到的绝对值是指针和指针之间元素的个数
	//不是所有指针都可以相减
	//要指向同一个空间的指针相减才有意义
    //指针相加无意义

	int arr2[5] = 0;
	char ch[2] = 0;

	printf("%d\n", arr2[4] - ch[1]);//无意义,会报错

	return 0;
}

指针减指针得到的绝对值是指针和指针之间元素的个数
不是所有指针都可以相减
要指向同一个空间的指针相减才有意义,如:printf("%d\n", arr2[4] - ch[1]);//无意义,会报错
指针相加也无意义

知道了指针可以相减后,我们可以用一种新的方法求字符串长度

int my_strlen(char* ch) {
	char* start = ch;
	while (*ch != '\0') {
		ch++;
	}
	return ch - start;//两个指针相减
}
int main() {
	int len = my_strlen("abcdef");
	printf("%d\n", len);
	return 0;
}

加法

指针的加法只能自增,如:


int main() {

	int arr[5];
	int* vp;
	for (vp = &arr[0]; vp < &arr[5];) {//指针关系判断 &arr[5] 不算指针越界,虽
	//然该地址不属于arr 但是内存中是有这个地址的
		*vp++ = 0;//指针加法
		//*vp = 0;
		//vp++;
	}
	return 0;
}

指针的自增在上文已经提过

指针关系运算

int main() {
	int arr[5];
	int* vp;
	for (vp = &arr[5]; vp > &arr[0];) {
		*--vp = 0;
	}
	for (vp = &arr[5 - 1]; vp >= &arr[0]; vp--) {
		*vp = 0;
	}
	//标准规定:
	//允许数组元素与指向数组最后一个元素的后面的指针相比,
	//但是不允许与指向数组第一个元素前面的指针相比
	return 0;
}

指针的大小比较 vp >= &arr[0],比较的是指针的大小(地址在内存中的先后顺序),
标准规定:
允许数组元素与指向数组最后一个元素的后面的指针相比,但是不允许与指向数组第一个元素前面的指针相比

指针和数组

数组是一组相同类型元素的集合
指针变量是一个变量,存放的是地址

int main() {
	int arr[10] = { 0 };
	//arr是首元素地址
	//&arr[0]
	int* p = arr;
	//通过指针来访问数组
	for (int i = 0; i < 10; i++) {
		printf("%d\n" , *(p + i));
	}
	//arr[i] -> *(arr + i) 在内存中是这样子访问的
	for (int i = 0; i < 10; i++) {
		printf("%p ----- %p\n", &arr[i] ,(p + i));
	}
	return 0;
}

p 指向 arr 的首元素,p + i 就可以访问arr数组里面的元素

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-08-19 18:45:50  更:2022-08-19 18:48:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/10 21:51:59-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码