初阶指针详解✍

news2024/11/20 21:29:48

目录

  • 1.内存和地址
  • 2.指针变量的大小
  • 3.指针类型的意义
    • 意义1:指针访问权限的大小
    • 意义2:指针类型决定指针的步长
  • 4.野指针
    • 野指针成因
    • 如何规避野指针
  • 5.指针的运算
    • 指针加减整数
    • 指针减指针
    • 指针的比较运算
  • 6.指针与数组的关系
  • 7.二级指针

1.内存和地址

在这里插入图片描述

  • 内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的
  • 所以为了有效的使用内存,就把内存划分成一个个小块,这每一个小块被称为内存单元,每个内存单元的大小是1个字节
  • 为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址,地址在C语言中也被称为指针,所以以后就可以通过地址找到对应的内存单元

接下来创建一个变量aint a = 4int类型占四个字节,所以a在内存中占4个内存单元:
请添加图片描述
可以用&a进行取地址:&a,a是一个整形变量,占用4个字节,每个字节都有自己的地址,这里取出的地址是第一个字节的地址(较小的地址)

所以这里&a取出值的是0x0012ff44


2.指针变量的大小

通过一下代码就能得出指针变量的大小

#include <stdio.h>
int main()
{
  printf("%d\n", sizeof(char *));
  printf("%d\n", sizeof(short *));
  printf("%d\n", sizeof(int *));
  printf("%d\n", sizeof(double *));
  return 0;
}

结论:指针大小在32位平台是4个字节,64位平台是8个字节
以上结论是根据程序结果得到的,接下来我们也可以通过物理层面去得到指针变量大小:

对于32位机器,它有着32根地址线,每跟线在寻址的时候会产生高电压和低电压也就是1或者0,有着32根地址线,也就是地址有着32歌比特位,1字节等于8比特,所以32位机器中地址占4个字节
这里我们可以继续算下去:32根地址线,就有2的32次方个地址,2^32Byte == 2^32/1024KB ==2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB,所以在32位机器中,可以给4G的空间进行编址。

这里我们就明白:

  • 在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。
  • 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。

总结

  • 指针变量是用来存放地址的,地址是唯一标示一个内存单元的
  • 指针的大小在32位平台是4个字节,在64位平台是8个字节

3.指针类型的意义

指针有许多类型,如:int*,short*,char*,float*……
但是从前面得知指针的类型都是4或8字节大小,它们不会存在像如intchar不同类型有着不同占用空间的问题,既然指针类型大小一样,那么为什么不把指针设置成一个同一类型的指针并且可以接收不同的类型变量的地址呢?不同的指针类型都有什么意义呢?

意义1:指针访问权限的大小

首先看一下代码:

#include <stdio.h>

int main()
{
	int a = 0x11223344;
	int* pa = &a;
	*pa = 0;
	return 0;
}

调试后进入内存窗口,找到a的地址后可以看见在内存中a中的存储的值为:44332211,这样“倒着存放”的原因是在Visual studio这个IDE中采取小端存储,如不懂大小端存储,👉可点击跳转
在这里插入图片描述
然后继续运行下一行代码,会发现*pa = 0使内存中4个字节内容变为0
在这里插入图片描述

接着再看一个代码:

#include <stdio.h>

int main()
{
	int a = 0x11223344;
	char* pa = &a;
	*pa = 0;
	return 0;

这个代码与上面代码不同的一点是这次pa指针变量是char类型的,上一个代码中的类型为int类型的
还是调试后进入内存窗口,找到a的地址后可以看见在内存中a中的存储的值为:44332211
在这里插入图片描述

接着运行,会发现,内存中只有44变为了00,也就是只有一个字节的内容发生了改变
在这里插入图片描述

这两个代码比较得出结论:

  • 指针类型决定了,指针进行解引用操作的时候,一次性访问几个字节,访问权限的大小
  • int*类型的解引用会对4个字节内容进行操作
  • char*类型的解引用会对1个字节内容进行操作
  • float* 类型的解引用会对4个字节内容进行操作
  • double类型的解引用会对8个字节内容进行操作

所以,我们可以通过不同的指针变量按需求对不同字节大小内容进行操作


意义2:指针类型决定指针的步长

观看一下代码:

int main()
{
	int a = 0x11223344;
	int* pa = &a;
	char* pc = &a;

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

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

	return 0;
}

得出结果:
在这里插入图片描述
可以看出,前两个地址大小差4,后两个地址大小差2
在这里插入图片描述
pa的类型为int*pa+1的地址增加了4,pc的类型为char*pc+1的地址增加了1

得出结论

  • 指针类型决定指针的步长(指针+1到底跳过几个字节)
  • 字符指针+1,跳过1个字节
  • 整型指针+1,跳过4个字节指针初阶指针初阶

4.野指针

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

野指针成因

  1. 指针未初始化
#include <stdio.h>
int main()
{
	int *p;
	*p = 20;
	return 0;
}

这里的指针p在定义的时候没有初始化,默认为随机值,也就是指针指向位置不可知。

以随机值为地址,解引用时需要找到那个地址处,是很危险的

此程序在编译器里不会被编译成功,会警告:使用了未被初始化的内存p

  1. 指针越界访问
#include <stdio.h>
int main()
{
  int arr[10] = {0};
  int *p = arr;
  int i = 0;
  for(i=0; i<=11; i++)
 {
    *(p++) = i;
 }
  return 0;
}

arr数组内有10个元素,循环时,到下标为10的时候,指针指针指向的范围超出数组arr的范围时,p就是野指针

3.指针指向的空间释放

int* test()
{
	int a = 10;
	return &a;
}
int main()
{
	int* p = test();
	printf("%d\n", *p);
	return 0;
}

test函数中,返回int类型a的地址,在主函数中用p接受这个地址,但是当test运行完return语句后,在test函数中对于变量开辟的空间就会归还给系统,此时&a已经不存在了,所以p为野指针

如何规避野指针

  • 1.指针初始化
  • 2.小心指针越界
  • 3.指针指向空间释放,及时置NULL p = NULL
  • 4.避免返回局部变量的地址
  • 5.指针使用之前检查有效性:if(p!=NULL)

5.指针的运算

指针加减整数

指针加减整数在前面指针类型意义里提到,这里不多说

这里看一个代码:

int main()
{
	int values[5];
	int * vp;
	for(vp = &values[0]; vp < &values[5];)
	{
		*vp++ = 0;
	}
}

这个代码很简单,就是通过指针,逐步把数组中元素值赋值为0

这里有一点值得注意的:*vp++ = 0;++优先级比*高,又由于++是后置,先用再加加。

vp的初始值为&values[0],所以才会从values[0]开始赋值,接着++,再对下一个位置赋值。

若改为前置++,则会先加加,再赋值,就会倒置values[0]位置处未被赋值。


指针减指针

指针-指针,前提是:两个指针要指向同一块空间,得到的结果的绝对值是两个指针之间元素个数

int main()
{
	int arr[10] = { 0 };
	printf("%d", &arr[9] - &arr[0]);
	return 0;
}

输出结果:
在这里插入图片描述
在这里插入图片描述

这里我们可以利用指针减指针来模拟strlen函数

假设有一个字符串abcdefghi,在内存中实际存储的是a b c d e f g h i \0,所以只要找到首元素的地址和\0的地址,相减就能得到字符串的长度
在这里插入图片描述

代码如下:

int my_strlen(char* s)
{
	char* begin = s;
	while (*s!='\0')
	{
		s++;
	}
	return s - begin;
}

int main()
{
	char arr[] = "abcdefghi";
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

指针的比较运算

以数组为例,数组首元素地址是小于数组除首元素其他元素地址

但是标准规定:

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

数组尾部的地址不是最后一个元素的地址,而是下标为数组长度处的地址
在这里插入图片描述

如上图:可以用&arr[6]和指针vpp与数组中其他地址进行比较,但是不可以用指针vp进行比较

通俗来讲就是:可以用数组后边的地址比较,但是不可以用数组前面的地址比较。


6.指针与数组的关系

指针与数组的关系主要在这篇文章里👉数组与地址,数组名到底是什么?

我们观察以下代码

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	
	//用数组下标遍历数组元素
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}

	printf("\n");

	//利用指针遍历数组元素
	int* p = arr;
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
}

因为数组名就是数组元素首地址,所以在这个程序里arrp是完全相同的。
同时这里可以发现用数组下标遍历数组和利用指针遍历数组都可以遍历,所以arr[i]*(p+i)是等价的

也就是有一个指针pp[i] <=> *(p+i)

并且我们还可以推理:因为加法有交换律,所以*(p+i)*(i+p)肯定是是等价的,通过*(i+p)又可以推出i[p]
所以p[i]是可以写成i[p]的,只是因为i[p]这种写法太过于逆天,所以不常见。


7.二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?
这就是 二级指针

int main()
{
	int a = 10;
	int* pa = &a;    //一级指针
	int** ppa = &pa; //二级指针
	return 0;
}

上面代码中的ppa就是二级指针

在这里插入图片描述

二级指针有两个*,怎么理解呢?

先看一级指针 ,int* pa = &a,一级指针中的那个*就表示pa是一个指针,剩下的那个int表示指针pa指向一个int类型变量
二级指针也同理
int** ppa = &pa,离ppa近的那个*表示ppa是一个指针变量,剩下的int*表示ppa这个指针指向int*类型
在这里插入图片描述

二级指针的解引用也和一级指针一样:**ppa


指针的内容还有很多
还有 字符指针 ~ 指针数组 ~ 数组指针 ~ 函数指针 ~~~~ 等等
~Coming soon~
🎸🎸🎸

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2、C语言程序规范

目录 1. 代码缩进 2. 变量、常量命名规范 3. 函数的命名规范 4. #include指令 5. 注释 6. main函数 7.函数返回值 8. 变量赋初值 俗话说&#xff0c;“没有规矩&#xff0c;不成方圆。” 如&#xff1a;第一个程序 #include <stdio.h>void main(){printf("…

基于java Springmvc+mybatis 电影院售票管理系统设计和实现以及文档

基于java Springmvcmybatis 电影院售票管理系统设计和实现以及文档 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留…

vue报错汇总

项目场景&#xff1a; 使用vue报错汇总。 1、项目启动不报错也不成功 提示&#xff1a;这里描述项目中遇到的问题&#xff1a; 项目启动时&#xff0c;一直启动不成功&#xff0c;末句提示 98% emitting Copyplugin… 原因分析&#xff1a; 最有可能是因为require或者import了…

系统设计技巧:使用Postgres作为发布/订阅和作业服务器

如果在项目中需要发布/订阅和作业服务器&#xff0c;可以尝试使用 Postgres。它将为您提供大量数据完整性和性能保证&#xff0c;并且不需要您或您的团队学习任何新技术。如果你正在做任何足够复杂的项目&#xff0c;你将需要一个 发布/订阅[1] 服务器来处理事件。本文将向你介…

黑马“兔年限定”春节礼盒准时送达,快来领!

哈咯艾瑞巴蒂&#xff0c;我是播妞前几天一个热搜引起了我的注意# 原来兔年要打384天的工 #看到这标题播妞突然头皮发紧我搜索了一下&#xff0c;原来是......农历癸卯兔年全年共有384天今年的春节是2023年1月22号2024年的春节是2月10号从今年春节到明年的春节算一年由于“闰二…

XCTF:ics-05

测试了所有功能点&#xff0c;大部分没有做出来&#xff0c;只有设备维护中心可以点击 查看源码&#xff0c;发现云平台设备维护中心这里有一个超链接 看到变量传参page&#xff0c;有点像页面文件包含功能&#xff0c;那有可能存在文件包含&#xff0c;测试下&#xff1a; …

ORB-SLAM2 --- LoopClosing::CorrectLoop函数

目录 1.函数作用 2.函数流程 3.code 4.函数解析 4.1 结束局部地图线程、全局BA&#xff0c;为闭环矫正做准备 4.2 根据共视关系更新当前关键帧与其它关键帧之间的连接关系 4.3 通过位姿传播&#xff0c;得到Sim3优化后&#xff0c;与当前帧相连的关键帧的位姿&#xf…

什么是计算机中的高速公路-总线?

文章目录总线是什么&#xff1f;常见总线类型有哪些&#xff1f;总线的串行和并行的区别&#xff1f;数据总线地址总线CPU的寻址能力32位CPU最大支持4G内存&#xff1f;控制总线总线的共享性和独自性系统总线的结构单总线结构双总线结构三总线结构总线传输的四个阶段总线仲裁集…

谷粒商城项目笔记之高级篇(二)

目录1.7 认证服务1.7.1 环境搭建1&#xff09;、创建认证服务微服务2&#xff09;、引入依赖3)、添加相应的域名4&#xff09;、动静分离5&#xff09;、nacos中注册6&#xff09;、配置网关7)、测试访问登录页面8&#xff09;、实现各个页面之间跳转1.7.2 验证码功能1)、验证码…

C++的类介绍(封装特性)

一、类的定义 1.1定义 类是c语言对编程思想的概括深化&#xff0c;其前期的C语言使能面向过程的语言&#xff0c;思想是注重对程序每一步的理解&#xff1b;而面向过程的是C语言之父把生活的类与对象的思想应用于程序设计之中&#xff0c;把程序抽象成一个个对象。 C面向对象…

将时间序列转换为指定的频率并指定填充方法来填充缺失值的DataFrame.asfreq()方法

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 时间序列的插值操作 提升序列中时间密度 DataFrame.asfreq() 选择题 关于以下python代码说法错误的一项是? import pandas as pd myIndexpd.date_range(1/12/2023,periods3,freqT) dfpd.D…

深入了解 LinkedBlockingQueue阻塞队列

1. 前言 今天来逐个方法解析下LinkedBlockingQueue. 其实跟上篇文件深入了解ArrayBlockingQueue 阻塞队列很类似。只不过底层实现不同。其实看名字就能看出来到底依据什么实现的。好了&#xff0c;废话不多说了&#xff0c;接下来让我们开始吧 至于API的使用情况跟ArrayBlockin…

JS垃圾回收

什么是GC GC就是Garbage Collection,程序工作过程中会产生很多垃圾&#xff0c;这些垃圾是程序不用的内存或者是之前用过了&#xff0c;以后不会再用的内存空间&#xff0c;而GC就是负责回收垃圾的。当然也不是所有的语言都会自带GC&#xff0c;比如Java、Python、Javascript等…

电脑修改用户(User)文件夹名称

情景&#xff1a;Windows 11 的用户名与 C 盘&#xff08;系统盘&#xff09;中的文件夹名称不对应&#xff08;可能是由于重装系统导致的&#xff09;&#xff0c;例如我笔记本中系统用户名是 “fly”&#xff0c;但文件夹名称却是“16490”。 Step 1&#xff1a;打开Administ…

智能图像处理:基于边缘去除和迭代式内容矫正的复杂文档图像校正

本文简要介绍ACM MM 2022录用论文“Marior: Margin Removal and Iterative Content Rectification for Document Dewarping in the Wild”的主要工作。该论文针对现有的矫正方法只能在紧密裁剪的文档图像上获得较为理想的矫正效果这一不足&#xff0c;提出了一个新的矫正方法Ma…

基于webrtc多人音视频的研究(一)

众所周知&#xff0c;WebRTC非常适合点对点&#xff08;即一对一&#xff09;的音视频会话。然而&#xff0c;当我们的客户要求超越一对一&#xff0c;即一对多、多对一设置多对多的解决方案或者服务&#xff0c;那么问题就来了&#xff1a;“我们应该采用什么样的架构&#xf…

利用AirTest实现自动安装APK-跳过vivo手机安装验证

利用AirTest实现自动安装APK-跳过vivo手机安装验证 前言 最近在帮测试组看个问题&#xff0c;他们在自动化测试的时候&#xff0c;通过adb install 命令在vivo手机上安装apk的时候出现”外部来源应用&#xff0c;未经vivo安全性和兼容性检测&#xff0c;请谨慎安装“的提示页面…

仅需一行Python代码,为图片上版权!

哈啰&#xff0c;大家好&#xff0c;我是派森酱&#xff0c;一个Python技术爱好者。今天一个朋友跟我吐槽&#xff1a;前段时间&#xff0c;我辛辛苦苦整理的一份XX攻略&#xff0c;分享给自己的一些朋友&#xff0c;结果今天看到有人堂而皇之地拿着这份攻略图片去引流&#xf…

多项目同时进行时,做好进度管理很重要

进行多项目同时进行时&#xff0c;做好进度管理非常重要。最简单的方法是使用项目管理软件&#xff0c;可以帮助你组织和跟踪多项目的进度。 此外&#xff0c;需要定期审核每个项目的进度&#xff0c;并对项目进行必要的调整&#xff0c;以确保每个项目都能按时完成。 1、多项…

1579_AURIX_TC275_MTU中的ECC机理以及各种寄存器实现

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) 这一夜的信息全是寄存器地址信息&#xff0c;在了解功能的时候都是非关键信息。后续的内容整理中&#xff0c;这部分类似的信息我都会跳过。 在这个系列的MCU中&#xff0c;ECC实现了单bit…