【C语言】指针(一)

目录

一、内存

1.1 ❥ 理解内存和地址的关系

1.2 ❥ 编址

二、指针变量

2.1 ❥ 取地址操作符(&)

2.2 ❥ 指针变量和解引用操作符(*)

2.3 ❥ 指针变量的大小

三、指针类型的意义

3.1 ❥ 指针的解引用

3.2 ❥ 指针+-整数

3.3 ❥ 易错点

3.4 ❥ void*指针

四、野指针

4.1 ❥ 野指针的成因

4.2 ❥ 野指针可能导致的问题

4.3 ❥ 如何避免野指针的出现

五、指针运算

5.1 ❥ 指针 +- 整数

5.2 ❥ 指针 - 指针

5.3 ❥指针的关系运算(比大小)

六、二级指针


一、内存

内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中完成的。

所以为了有效的使用内存,就把内存分成一个个小的内存单元,每个内存单元的大小是1个字节(1 byte)

为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址。

1.1 ❥ 理解内存和地址的关系

我们可以举个例子:当我们回宿舍的时候,我们是如何找到自己所在的宿舍呢?

当然是根据门牌号来确定我们所在的宿舍。有了门牌号,就能提高效率,快速的找到房间。

一楼:101,102,103...
二楼:201,202,203...

这里的一个学生宿舍相当于一个内存单元,每个内存单元的大小取1个字节,1个字节里放8个比特位,就好比同学住的八人间,每个人是1个比特位。

每个内存单元都有一个编号,这个编号就相当于宿舍的门牌号,有了这个内存单元的编号,cpu就可以快速找到一个内存空间。

这里的门牌号,也就是内存单元的编号,也叫作地址。c语言中给地址起了个新名字,叫指针。

所以:内存单元的编号 == 地址 == 指针

1.2 ❥ 编址

CPU访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置,因为内存中字节很多,所以需要给内存进行编址。

计算机中的编址,并不是把每个字节的地址记录下来,而是通过硬件设计完成的。

计算机内是有很多的硬件单元,而硬件单元是要互相协同工作的。所谓的协同,至少相互之间要能够进行数据传递。

但是硬件与硬件之间是互相独立的,要想达到通信效果,那就得用线连接起来。
而CPU和内存之间也是有大量的数据交互的,所以,两者必须也用线连起来。其中,有一组线叫
地址总线。

32位机器有32根地址总线,每根线只有两态,表示0或者1(电脉冲有无),那么一根线,就能表示2种含义,2根线就能表示4种含义,依次类推。32根地址线,就能表示2^32种含义,每一种含义都代表一个地址。地址信息被下达给内存,在内存上,就可以找到该地址对应的数据,将数据在通过数据总线传入CPU内寄存器。



二、指针变量

2.1 ❥ 取地址操作符(&)

在c语言中,创建变量其实就是向内存申请空间:

上述的代码就是创建了整型变量a,内存申请了4个字节,用于存放整数10,其中每个字节都有地址,上图中4个字节的地址分别是:

0x010FF9E4

0x010FF9E5

0x010FF9E6

0x010FF9E7

这里我们利用取地址操作符(&)来获取a的地址。

#include <stdio.h>
int main()
{
	int a = 10;

	printf("%p\n", &a);//打印的是首字节的地址,即:0x010FF9E4
	return 0;
}

2.2 ❥ 指针变量和解引用操作符(*)

❤ 指针变量

那我们通过取地址操作符(&)拿到的地址是一个数值,比如:0x010FF9E4,这个数值有时候也是需要存储起来,方便后期再使用。那么我们把这样的地址值存放在指针变量。

int* p = &a;//将a的地址存放在指针变量p中
  1. p:指针变量,用来存放地址
  2. 存储地址的意义:p有机会找到a
  3. int:说明p指向的对象是int类型的。
  4. *:放在这里只是说明p是个指针变量(没有其它含义)
  5. int*:说明指针变量p的类型是int*

解引用操作符(*)

c语言中,我们可以通过解引用操作符来找到我们指针(地址)所指向的对象。

代码如下:

这里*p的意思是:通过p中存放的地址,找到p所指向的对象,*p就是p所指向的对象a

当然,也可以对*p(即变量a)进行修改,代码如下:

2.3 ❥ 指针变量的大小

  • 不管什么类型的指针(地址),都是在创建指针变量,指针变量是用来存放地址的。
  • 指针变量的大小取决于一个地址存放需要多大的空间(即指针变量的大小取决于地址的大小)。
  • 32位机器上的地址:32bit位 - 4byte ,所以指针变量的大小是4个字节
  • 64位机器上的地址:64bit位 - 8byte ,所以指针变量的大小是8个字节
  • 指针变量的大小和类型是无关的,只要指针类型的变量,在相同的平台下,大小都是相同的。
  • 在32OS下,int*,char*,double*...都是4个字节。

总结:指针大小与数据类型无关,它与系统位数有关。

三、指针类型的意义

指针变量的大小和类型无关,只要是指针变量,在同一平台下,大小都是一样的,为什么还要有各种指针类型呢?

其实指针类型是有它存在的意义的,我们来看下指针类型存在哪些意义吧。

3.1 ❥ 指针的解引用

我们首先来看下面两段代码:对他们进行调试:

代码1

代码2

调试我们可以看到:代码1会将n的4个字节全部改为0,但是代码2只是将n的第一个字节改为0。

结论:指针的类型决定了,指针在被解引用时能有多大权限(一次能操作几个字节)。

例如:char*的指针解引用就只能访问1个字节,而int*的指针解引用能访问4个字节。

3.2 ❥ 指针+-整数

我们还是先来看一段代码:

#include <stdio.h>
int main()
{
	int a = 10;
	char* pa = (char*)&a;
	int* pc = &a;

	printf("%p\n", &a);

	printf("%p\n", pa);
	printf("%p\n", pa + 1);

	printf("%p\n", pc);
	printf("%p\n", pc + 1);
	
	return 0;
}

运行结果可以发现:char* 类型的指针变量+1跳过1个字节;int* 类型的指针变量+1跳过了4个字节。

结论:指针的类型决定了指针在+-操作的时候,能够跳过几个字节。(决定了指针的步长)

3.3 ❥ 易错点

我们看如下代码:

#include <stdio.h>
int main()
{
	int a = 0;

	int* pa = &a; 
	float* pf = &a; 		

	return 0;

发现:

pa解引用访问4个字节,pa+1跳过4个字节。

pf解引用访问4个字节,pf+1也是跳过4个字节。

那么int*和float*是不是就可以通用呢?

答案是:不能。

原因:因为两者的存储方式完全不同。

3.4 ❥ void*指针

void* —— 泛型指针,没有具体类型的指针。

这种类型的指针可以用来接收任意类型的地址。

如下面代码所示:

#include <stdio.h>
int main()
{
	int a = 10;

	void* pa = &a;
	void* pc = &a;

	return 0;
}

但是也有局限性:void*类型的指针不能直接解引用和进行指针的 + - 整数的运算,因为不知道访问(多大空间)多少个字节。

如下图所示:

void*类型的指针的用处:

一般void*类型的指针是使用在函数参数的部分,用来接收不同类型数据的地址,这样涉及可以实现泛型编程的效果。使得一个函数来处理多种类型的数据。

四、野指针

  • 当一个指针没有被正确初始化或者被错误释放后,它可能仍然保留着之前所指向的内存地址。在这种情况下,这个指针就成了野指针。
  • 它包含一个无效的内存地址。当野指针被解引用或者试图在其指向的内存上进行读取或写入操作时,就会导致未定义行为。

4.1 ❥ 野指针的成因

 

❤  指针没有初始化

一个指针变量被声明但没有初始化时,其中的值将是不确定的,它可能指向任意的内存地址,包括重要的内存位置。

❤ 指针越界访问

❤ 指针指向的空间释放

当某个内存块已经被释放并返回给系统,但对应的指针仍然保留着指向之前的内存地址,那么这个指针就是野指针。

分为以下几个方面:

  1. 一个指针指向已经释放的内存区域(例如通过free函数释放的内存)。
  2. 一个指针指向已经超出其作用域的静态变量,这可能会访问到无效的内存。
  3. 一个指针指向栈帧上的局部变量,并且该变量的生命周期已经结束(例如函数调用返回后),这时使用该指针会访问到无效的内存。

例如下面代码:

#include <stdio.h>
int* test()
{
	int n = 100;//局部变量n进栈创建,出栈销毁
	return &n;
}

int main()
{
	int* p = test();

	printf("%d\n", *p);
	return 0;
}
  • 空间销毁的意思是:内存里这块空间还存在,只是不属于我当前程序了。
  • 这块空间是我当前程序所拥有的,我就可以使用它,这个函数出去之后,这块空间就还给操作系统了,我当前程序没有这块空间使用权限了。但是内存里的这块空间还存在。

4.2 ❥ 野指针可能导致的问题

  1. 未定义行为:使用野指针进行解引用或间接访问可能会导致未定义行为。这包括程序崩溃,数据损坏或产生不可预测的结果。
  2. 安全漏洞:如果野指针指向重要的内存区域,可能会导致安全漏洞。攻击者可能利用野指针来修改或获取敏感数据,从而对程序的安全性产生影响。
  3. 内存泄漏:如果野指针指向已经释放的内存,可能导致内存泄漏。释放的内存应该被正确管理,否则会占用系统资源并影响程序性能。

4.3 ❥ 如何避免野指针的出现

  • ❤ 指针初始化
  • ❤ 小心指针越界
  • ❤ 指针指向的空间释放要及时置空(NULL),或者不想使用的时候赋值空指针(NULL),或者一个指针不想初始化的时候可赋值空指针(NULL)
  • ❤ 避免返回局部变量的地址
  • ❤ 谨慎使用动态内存分配和释放,确保指在针使用之前检查其有效性

补充:NULL是C语言中定义的一个标识符常量,值是0,0也是地址。该地址是属于系统内核的,用户程序是不能使用的,读写该地址会报错。

五、指针运算

5.1 ❥ 指针 +- 整数

因为数组在内存中是连续存放的,只要知道第一个元素的地址,就能找到后面的所有元素。

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

	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);

	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i)); //p+i 这里就是指针+整数
	}

	return 0;
}

5.2 ❥ 指针 - 指针

指针 - 指针的绝对值得到的是指针和指针之间的元素个数

注意:

  1. 不是所有的指针都能相减,指向同一块空间的两个指针才能相减,这样才有意义。
  2. 指针(地址)+ 指针毫无意义。

5.3 ❥指针的关系运算(比大小)

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

代码1

代码2

实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行。

六、二级指针

二级指针:用来存放指针变量的地址的指针(变量)

如下图所示:

对于二级指针的运算有:
 *ppa 通过对ppa中的地址进行解引用,这样找到的是 pa , *ppa 其实访问的就是pa 

int b = 20;
*ppa = &b; //等价于 pa = &b;

**ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,找到的是 a 

**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/620850.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL探索之旅】JDBC (Java连接MySQL数据库)

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 |《MySQL探索之旅》 |《Web世界探险家》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更…

供应商管理库存用什么软件

库存的管理是一个非常重要的话题&#xff0c;不管是企业还是个人&#xff0c;在日常工作中多多少少都会涉及到库存的管理&#xff0c;那么作为供应商&#xff0c;也是需要对库存进行管理的&#xff0c;因为需要对分销商进行出货&#xff0c;所以库存的有效管理非常重要。 供应商…

【数据结构】堆(Heap)

文章目录 一、堆的概念及结构二、堆的实现1.向上调整算法2.向下调整算法3.堆的创建4.堆的插入5.堆的删除6.堆的其他操作 三、堆的应用1.堆排序2.Top-K问题 一、堆的概念及结构 堆(Heap)是一种特殊的非线性结构。堆中的元素是按完全二叉树的顺序存储方式存储在数组 中。满足任意…

【送书福利第七期】你好!Java(文末送书)

文章目录 编辑推荐内容简介作者简介目录前言/序言 编辑推荐 适读人群 &#xff1a;程序员;相关院校师生 本书以轻松幽默的语言&#xff0c;从零开始介绍Java语言。书名来源于编程语言中最经典的Hello World程序&#xff0c;寓意带读者从入门到精通。 书中每章都设有总结与扩展…

开源软件托管平台gogs操作注意事项

文章目录 一、基本说明二、gogs私有化部署三、设置仓库git链接自动生成参数四、关闭新用户注册入口 私有化部署gogs托管平台&#xff0c;即把gogs安装在我们自己的电脑或者云服务器上。 一、基本说明 系统环境&#xff1a;ubuntu 20.4docker安装 二、gogs私有化部署 前期准…

深入学习心理学才知道以前高估了自己的交易能力

人的大脑总是能带来无限的惊喜。你能同时完成很多种工作而没有觉察你自己正在做这些事情。比如你一边开车一边打电话讨论着很严肃的事&#xff0c;边吃饭边用眼睛关心着后排坐着的孩子。除了这些外部的动作&#xff0c;你的内脏机能也在马不停蹄的工作着。这一切并不需要我们付…

【Shell】正则表达式的操作实例

正则表达式是一个描述一组字符串的模式 是由普通字符和元字符组成的字符集&#xff0c;而这个字符集匹配&#xff08;或指定&#xff09;一个模式。 正则表达式的操作实例 &#xff08;一&#xff09;概述1.定义2.作用3.类型 &#xff08;二&#xff09;字符串匹配实例&#xf…

掌握 Spring Boot 观察者模式:打造松耦合事件驱动应用程序

掌握 Spring Boot 观察者模式&#xff1a;打造松耦合事件驱动应用程序 观察者模式是一种常用的设计模式&#xff0c;用于解决事件驱动编程中的问题。该模式定义了一对多的依赖关系&#xff0c;其中一个对象&#xff08;主题&#xff09;向一组依赖它的对象&#xff08;观察者&a…

【phpstorm】根据等号对键值对进行自动对齐

对于强迫症的我&#xff0c;看到这样的代码&#xff0c;总想给它整理一番。 可以通过 ide 配置&#xff0c;手动格式化代码&#xff0c;整理成这样&#xff1a; ide 版本&#xff1a; PhpStorm 2023.1.3 配置路径&#xff1a; Setting -> Editor -> Code Style -> PH…

智能酒精壁炉与酒店前台的氛围搭配

智能酒精壁炉与酒店前台的氛围搭配可以为前台区域增添舒适、现代和独特的氛围&#xff0c;以下是一些建议&#xff1a; 欢迎区域装饰&#xff1a; 将智能酒精壁炉作为前台欢迎区域的装饰物&#xff0c;放置在客人抵达的显眼位置。选择现代设计的壁炉款式&#xff0c;如壁挂式…

SpringBoot整合SpringScurity权限控制(菜单权限,按钮权限)以及加上SSH实现安全传输

文章目录 项目地址&#xff1a; 一、md5 与 先进的哈希算法的区别1.1. 安全性问题1.2. 设计目的1.3. 功能特性1.4. 适用性1.5. 总结 二、数据传输安全和数据加密实现&#xff1a;2.1 生成证书&#xff1a;2.2、在springboot中进行集成2.2.1 配置证书&#xff1a;2.2.2. 强制使用…

Pytorch 与 Tensorflow:深度学习的主要区别(1)

引言 目前&#xff0c;Python 深度学习领域已经涌现出多个由科技界巨头如 Google、Facebook 和 Uber 等公司公开发布的框架&#xff0c;这些框架旨在帮助开发者构建先进的计算架构。对于刚接触这一领域的你来说&#xff0c;深度学习是计算机科学中的一个分支&#xff0c;它通过…

springboot306基于Java的民宿管理系统(源码+包运行+配套LW+技术指导)

项目描述 临近学期结束&#xff0c;开始毕业设计制作&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉的困难吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。今天给大家介绍一篇基于Java的民宿管理…

初识指针(5)<C语言>

前言 在前几篇文章中&#xff0c;已经介绍了指针一些基本概念、用途和一些不同类型的指针&#xff0c;下文将介绍某些指针类型的运用。本文主要介绍函数指针数组、转移表&#xff08;函数指针的用途&#xff09;、回调函数、qsort使用举例等。 函数指针数组 函数指针数组即每个…

24年做抖音小店,你还停留在数据?别人都已经开始注重利润了

大家好&#xff0c;我是电商笨笨熊 一件事情持续做&#xff0c;一个项目持续深耕&#xff0c;意义到底是什么&#xff1f; 这句话我常常说&#xff0c;但很多人似乎走偏了实际意义&#xff1b; 尤其对于新手来说&#xff0c;做抖音小店总是向往某某老玩家多么牛的数据&#…

如何把握人力RPO的蓝海机遇?实战策略分享!

随着企业间竞争的日益激烈&#xff0c;人力资源管理的重要性愈发凸显。在众多人力资源管理策略中&#xff0c;招聘流程外包(RPO)作为一种新兴的服务模式&#xff0c;逐渐受到业界的关注。那么&#xff0c;人力RPO是否是蓝海项目?我们又该如何实施RPO呢? 一、人力RPO&#xff…

C++干货--引用

前言&#xff1a; C的引用&#xff0c;是学习C的重点之一&#xff0c;它与指针的作用有重叠的部分&#xff0c;但是它绝不是完全取代指针(后面我们也会简单的分析)。 引用的概念&#xff1a; 引用 不是新定义一个变量 &#xff0c;而 是给已存在变量取了一个别名 &#xf…

经典文献阅读之--D-Map(无需射线投射的高分辨率激光雷达传感器的占据栅格地图)

0. 简介 占用地图是机器人系统中推理环境未知和已知区域的基本组成部分。《Occupancy Grid Mapping without Ray-Casting for High-resolution LiDAR Sensors》介绍了一种高分辨率LiDAR传感器的高效占用地图框架&#xff0c;称为D-Map。该框架引入了三个主要创新来解决占用地图…

附录2 创建flask镜像

目录 1 python镜像 2 安装flask 3 把项目文件扔进去 3.1 创建git仓库 3.2 上传文件 3.3 获取git链接 3.4 在容器中git clone 4 启动flask服务 5 将容器保存为镜像 6 映射端口运行镜像 7 遇到的问题 8 Dockerfile创建镜像 1 python镜像 首先找一下fla…

Android 老年模式功能 放大字体

1 配置属性 <attr name"text_size_16" format"dimension"/><attr name"text_size_18" format"dimension"/><attr name"text_size_14" format"dimension"/><attr name"text_size_12&quo…