首页 > 其他分享 >指针初阶及使用

指针初阶及使用

时间:2024-09-12 23:50:10浏览次数:11  
标签:arr 初阶 字节 int 地址 数组 使用 指针

指针的使用

指针是什么

我们对指针的理解大多数就是地址,那究竟是不是呢 ?

  1. 指针是内存中一个最小单元的编号,也就是地址
  2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量

我们想要了解指针,还有一个东西要清楚,就是内存。

请添加图片描述

可以看到是一个字节一个字节分开的,但是为什么不是两个字节或者四个字节在一起呢 ?我们平常在写代码的时候就是经常用int (4个字节),但是可以想想如果你用4个字节4个字节,表示char的时候怎么表示。如果用4个字节表示是不是有点浪费啊!大家可以自己想一想这个问题。

#include <stdio.h>
int main()
{
 int a = 10;//在内存中开辟一块空间
 int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
//a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量
//中,p就是一个之指针变量。
 return 0;
}

32位下:

请添加图片描述

64位下:

请添加图片描述

这个时候我们可以想想为什么在32位下是4个字节,64位下是8个字节 ?

对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电平(低电压)就是(1或者0)
32个0和1序列可以组成2的32次方个地址
32个0和1序列正好是4个字节,1个字节是8位,正好可以放下。
64位的和32位的同理

指针和指针类型

在指针类型中我们可以类比一下变量,指针也是有类型的。

char  *pc = NULL;	//存放 char 类型变量的地址。
int   *pi = NULL;	//存放 int 类型变量的地址。
short *ps = NULL;	//存放 short 类型变量的地址。
long  *pl = NULL;	//存放 long 类型变量的地址。
float *pf = NULL;	//存放 float 类型变量的地址。
double *pd = NULL;	//存放 double 类型变量的地址。

我们可以想一想指针类型的意义。

变量可以加减一个整数,指针可以吗 ?

答案是可以的。(展示是32位下)

请添加图片描述

请添加图片描述

我们可以看到 int 的4个字节,我们又知道地址是1个字节1个字节分的,int n=10,我们对这个n取地址,这个地址会是什么呢 ? 上面我们可以看到n的地址就是首字节的地址,而对指针进行加1,int 类型加了4,而char 类型加了1,我们就可以得出一个结论了,指针的类型决定了指针向前或者向后走一步有多大(距离)。

我们在对指针的解引用操作一下。

请添加图片描述

请添加图片描述

请添加图片描述

请添加图片描述

请添加图片描述

请添加图片描述

首先是 int* 类型的指针,我们可以看到 int* 对 int 的改变我们接下来看另一种情况。

请添加图片描述

请添加图片描述

请添加图片描述

请添加图片描述

请添加图片描述

请添加图片描述

请添加图片描述

我们通过对内存的观察可以清晰的看出不同类型的指针解引用的时候可以访问多少个字节。我们在此也可以得出一个结论。

指针的类型决定了对指针解引用的时候有多大的权限(能操作几个字节)。

就如上述的:char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。

野指针

我们来聊一下什么是野指针呢 ?

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

造成野指针的原因都是些什么呢 ?

1.指针未初始化。

#include <stdio.h>
int main()
{ 
 	int *p;//局部变量指针未初始化,默认为随机值
    *p = 20;
 	return 0;
}

我们在定义局部变量的时候知道,如果我们不对这个变量进行初始化操作,那么这个变量就是一个随机值,而指针未初始化,也是一个随机值,只不过这个随机值变成了地址,我们可以试着想一下,如果我们不对这个指针进行初始化,我们就不知道这个指针所指向的位置是哪里。这是不是一件很危险的事情啊,假如我想对这个指针进行一些操作,我是不是要知道这个指针里面的内容啊。不知道这不是乱来吗 ?

2.指针的越界访问

#include <stdio.h>
int main()
{
    int arr[10] = {0};
    int *p = arr;
    int i = 0;
    for(i=0; i<=11; i++)
   	{
        //当指针指向的范围超出数组arr的范围时,p就是野指针
        *(p++) = i;
   	}
    return 0;
}

请添加图片描述

请添加图片描述

我们可以想一下,我们定义了一个 int arr[10]的数组,就会为我们分配10个 int 类型大小的地址,当我们把10个都访问了,该访问下一个,就是上图所指向的地方,我们知道这个地址里面放的是什么吗 ? 我们并不知道,当我们访问他的时候,编译器就会强制报错,我们不能访问他。

3.指针指向的空间释放

这里先了解一下,这和动态申请内存有关,就是我们会动态申请一块内存,那这个内存释不释放的问题,后面会更。

我们既然知道野指针会对我们造成很不好的影响,那我们就应该要想办法规避野指针,那我们该如何的进行规避野指针呢 ?

  1. 指针初始化
int* p=NULL;
  1. 小心指针越界
#include <stdio.h>
int main()
{
    int arr[10] = {0};
    int *p = arr;
    int i = 0;
    for(i=0; i<=11; i++)
   	{
        //当指针指向的范围超出数组arr的范围时,p就是野指针
        *(p++) = i;
   	}
    return 0;
}
  1. 指针指向空间释放,及时置NULL

这里是动态内存会有体现。

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

我们知道,函数在调用结束的时候,这块内存会被回收,就是说我们不能够在随心所欲的访问这一块的内容了,这个时候我们不是得到了一个地址,但是这个地址我们是不能够访问的,这个指针也就是一个野指针,就好比你曾经是这个房子的主人,但是你把这个房子卖给其他人了,但是这个房子的钥匙你还有,你有钥匙你就能随心所欲的进入这个房子吗 ? 这个时候,显然是不可以的啊。

  1. 指针使用之前检查有效性

指针运算

1.指针±整数

#define N_VALUES 5
int main()
{
	float values[N_VALUES];
	float* vp;
	//指针+-整数;指针的关系运算
	//下面两个for循环的意思是一样的。
	for (vp = &values[0]; vp < &values[N_VALUES];)
	{
		*vp++ = 0;	//这一句的意思和下面注释的两句意思是一样的
		//*vp=0;
		//vp++;
	}
	for (vp = &values[0]; vp < &values[N_VALUES];vp++)
	{
		//*vp++ = 0;	
		*vp=0;
	}
	
}

上述代码就可以将5个元素设置成0

2.指针-指针

请添加图片描述

请添加图片描述

这里我们可以试着猜想一下,指针-指针是什么了,好像是两个指针之间的元素个数啊。我们在来个例子感受一下。

我们实现一个模拟求字符串长度的函数。我们平常的写法是这样的。

请添加图片描述

现在我们知道指针可以-指针了,我们又可以拓展一个新的写法。

请添加图片描述

是不是也能实现功能啊。这也更加的印证了指针-指针得到的是什么。

指针和数组

先来一段代码

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

请添加图片描述

我们可以看到结果是一样的,说明了数组名就是首元素的地址。

那既然数组名是首元素的地址,那我们可不可以拿一个指针来接受一下。

int arr[10] = {1,2,3,4,5,6,7,8,9,0};
int *p = arr;//p存放的是数组首元素的地址

我们来写一个例子看一下吧。

#include <stdio.h>
int main()
{
    int arr[] = {1,2,3,4,5,6,7,8,9,0};
    int *p = arr; //指针存放数组首元素的地址
    int sz = sizeof(arr)/sizeof(arr[0]);
    int i=0;
    for(i=0; i<sz; i++)
   	{
        printf("&arr[%d] = %p   <====> p+%d = %p\n", i, &arr[i], i, p+i);
   	}
    return 0;
}

上述代码会怎么样输出呢 ?

请添加图片描述

这里我们可以看到 p+i 其实计算的是数组 arr 下标为i的地址。那我们就可以直接通过指针来访问数组。

用数组下标来遍历数组

请添加图片描述

用指针遍历数组

请添加图片描述

二级指针

我们学习了指针,这个时候又来了个二级指针,我们知道指针存放的是地址,那么二级指针又是存放的什么啊 ?

请添加图片描述

请添加图片描述

请添加图片描述

这里我们可以看出来二级指针存放的就是一级指针变量的地址。这里是不是有点套娃的感觉。*ppa 通过对ppa中的地址进行解引用,这样找到的是 pa , *ppa 其实访问的就是 pa.

指针数组

指针数组又是一个什么东西呢 ?是指针 ? 是数组 ?

答案:是数组,用来存放指针的数组

我们接触过整形数组和字符数组。

请添加图片描述

但是指针数组是怎样的呢 ?

请添加图片描述
上述这样的。但是有什么用呢 ?

请添加图片描述

这样一对比是不是就有点意思了。

请添加图片描述

这样一看,这是不是有点像二维数组啊,我们可以看到当我们parr[ i ]的时候得到的是什么,是不是数组的元素啊,而数组的每个元素都是一个地址,地址就可以用指针来表示,parr[ i ][ j ]是不是也能取到每个数组里面的每一位啊。

标签:arr,初阶,字节,int,地址,数组,使用,指针
From: https://blog.csdn.net/2402_84602321/article/details/142186176

相关文章

  • 项目完成小结:使用DjangoStarter v3和Taro开发的微信小程序
    前言不知不觉已经九月了,又到了一年的开学季,我每年都想做的项目墙甚至连个影子都没有…......
  • mybatis exists 中使用代替in关键字
      使用场景,in适合数据量小的时候,exists适合数据量大的时候。<iftest="torqueRecordPageDTO.vinList!=nullandtorqueRecordPageDTO.vinList.size>0">andexists(select1from(<foreachcollection="......
  • 3D地形图制作新纪元,教你使用PS中的3D Map Generator制作3D地形图
    大家好!紧接上一篇文章,我将为大家介绍如何使用ps中的3Dmapgentor生成三维地形。首先,先为各位观众介绍一下这款插件,3DMapGenerator是一款功能强大的Photoshop插件,主要用于快速生成地形图等三维地图。具有以下特点和功能:3DMapGenerator插件可用于制作地形图、城市规划、......
  • 如何使用python中的math
    首先,导入math函数库。一、计算平方根输入一个数值,保存在变量n中。用函数sqrt,计算变量平方根的值。二、计算幂可以用函数exp,计算e的x次幂。三、计算对数设置两个数,保存在变量n和a中。接着,用log函数计算以a为基数n的对数。运行程序,其结果如下图所示。......
  • Linux编译器-gcc/g++使用
    1.背景知识1.预处理(进行宏替换)2.编译(生成汇编)3.汇编(生成机器可识别代码)4.连接(生成可执行文件或库文件)2.gcc如何完成格式gcc[选项]要编译的文件[选项][目标文件]预处理(进行宏替换)编译(生成汇编)汇编(生成机器可识别代码)连接(生成可执行文件或库文件)......
  • 【Python使用】嘿马python基础入门全体系教程第9篇:高阶函数,函数应用:学生管理系统【附
    本教程的知识点为:计算机组成计算机是由什么组成的?1.硬件系统:2.软件系统:目标运算符的分类1.算数运算符2.赋值运算符3.复合赋值运算符判断语句和循环语句if嵌套1.if嵌套的格式2.if嵌套的应用if嵌套执行流程容器:字符串、列表、元组、字典字符串介绍一.认识字......
  • 使用WebSocket协议实现在ESP32上音频接收播放
     主要目的:学习WebSocket通讯协议和ESP32开发所需配置:Pycharm,python3.12,ESP32S3N16R8, 扬声器(8欧,2W), 功放模块:MAX98357I2SAMP。一、介绍 1、WebSocket协议 WebSocket是一种网络通信协议,位于OSI模型的应用层。它提供了在单个TCP连接上进行全双工通信的能力,使得客......
  • [20240912]记录使用tnsping遇到的问题.txt
    [20240912]记录使用tnsping遇到的问题.txt--//tnsping用来检测数据库是否连接存在许多局限性,记录自己在使用tnsping遇到的问题.1.环境:--//关闭数据库开启监听.SYS@book>shutdownimmediate;Databaseclosed.Databasedismounted.ORACLEinstanceshutdown.--//服务端监听配置......
  • WPF 尝试使用 WinML 做一个简单的手写数字识别应用
    最近我看了微软的AI训练营之后,似乎有点了解WindowsMachineLearning和DirectML的概念,于是我尝试实践一下,用WPF写一个简单的触摸手写输入的画板,再使用大佬训练好的mnist.onnx模型,对接WinML实现一个简单的手写数字识别应用本文属于WinML的入门级博客,我将尝试一步步......
  • mysql 使用binlog还原数据
    MySQL的二进制日志(BinaryLog,简称binlog)是MySQL数据库的一个重要功能,它记录了所有的修改数据库内容的操作(如INSERT、UPDATE、DELETE等),但不包括SELECT和SHOW这类操作。这些日志对于数据恢复、复制和数据审计等场景非常有用。1.确认binlog是否开启登录MySQL后,可以通过SH......