C语言--不可不学的指针

news2024/9/22 17:20:24

1. 指针是什么

内存是什么?

内存是电脑上特别重要的存储器,计算机中的程序的运行都是在内存中进行的,为了有效使用内存,我们会把内存划分为一个个小的内存单元,比如把4G/8G/16G/32G的内存划分为一个个以字节为单位的空间,一个内存单元的大小是一个字节,每个字节(内存单元)都会有一个编号,这个编号就是地址,即编号==地址==指针

指针就是地址,在我们日常的口语中,指针即为指针变量。

指针变量:存放在指针中的任何值都被当做指针变量

指针变量是用来存放地址的,地址是唯一一个标识内存单元的。

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

&a:

&取地址符号,取出a的地址(a是int类型,占4个字节)

int * pa:

pa,指针变量,是专门用来存放地址(指针)的

*,表示pa是指针变量

int,pa指向的a的类型是int

int*,pa这个指针变量的类型

a 是int类型,占用四个字节的空间,将a四个字节的第一个字节的地址存放在pa变量中

指针的大小:在32位平台是4个字节,在64位平台是8个字节。

2. 指针和指针类型

指针定义方式:type+*

type* p;

type是p指向的对象的类型

*说明p是指针变量

p解引用访问的大小是sizeof(type)

char*
short*
int*
long*
float*
double*

无论是什么类型的指针,大小都一样

//无论什么类型的指针,大小都是一样的
int main()
{
	printf("%d\n", sizeof(char*));//1
	printf("%d\n", sizeof(short *));//2
	printf("%d\n", sizeof(int *));//4
	printf("%d\n", sizeof(long*));//
	printf("%d\n", sizeof(float *));//4
	printf("%d\n", sizeof(double*));//8

	return 0;
}

我们可以通过sizeof观察一下

指针类型的意义:

char*类型的指针是为了存放char变量的地址

short*类型的指针是为了存放short变量的地址

int*类型的指针是为了存放int变量的地址

......

指针类型可以决定指针解引用的时候访问多少字节(指针的权限)。

我们以如下代码为例,一个是int*一个是char*:

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

我们可以观察到,当pa类型为int*时,访问了四个字节

我们同样也可以观察到,当pa类型为char*时,访问了一个字节

指针类型也会决定指针+1操作(指针运算)的步长。

我们借助如下代码来研究:

//决定了+1操作的步长
//+/-n 决定了加减n*sizeof(type)个字节
int main()
{
	int a = 10;

	int* pa = &a;
	char* pc = &a;

	printf("%p\n", pa);
	printf("%p\n", pc);

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

观察结果我们可以发现,前两个打印出来的结果是一样的,后两个一个是加4以后的结果,一个是加1以后的结果。由此,指针类型也会决定指针+1操作(指针运算)的步长。

3. 野指针

定义:野指针指向的定义和位置是不可知的(随机的,不正确的,没有明确限制的),很危险

成因:

1.指针未初始化

2.指针越界访问(当指针指向范围超出数组范围,p就是野指针。比如在循环中或者函数的返回值)

例子如下:

int main()
{
	int* p;
	*p = 20;
	printf("%d\n", *p);
	return 0;
}

在此段代码中,局部变量p没有进行初始化时,内容是随机的

这里面p就是一个野指针

如何规避野指针?

1.指针初始化(明确知道指针应当初始化的地址,不知道就暂时置NULL(意思是0))

2.小心指针越界

3.指针指向的空间释放时,及时置NULL

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

如下段代码是一个野指针的案例,程序仍可正常运行,但会出现警告:

int* test()
{
	int a = 110;
	return &a;
}

int main()
{
	int* p = test();
	printf("%d\n", *p);

	return 0;
}

在test()函数中,return &a;在main()函数中,p就是一个野指针。a的空间进入函数test创建,出函数test()则销毁。

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

int main()
{
	int* p;  //野指针
	int* ptr = NULL;//置NULL
	return 0;
}

如上所示,p时野指针,可以类比为一条会乱咬人的野狗,当置NULL以后,相当于给野狗身上拴上绳子

ptr是一个空指针,不能直接使用,没有指向任何有效空间

4. 指针运算

4.1指针+/-整数

不使用下标进行对数组的访问

//指针+/-整数
int main()
{
	int arr[10] = { 0 };
	int* p = &arr[0];
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 1; i <= sz; i++)
	{
		*p = i;
		p++;
	}
	p = arr;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

在这段代码中,第一个for循环进行对于数组arr的初始化,p刚开始指向数组的首元素地址,*p对p进行解引用,修改p的值为i,则数组中第一个元素为1,然后依次进行循环,最终数组结果为{1,2,3,4,5,6,7,8,9,10}。在第一个for循环中,对于p进行修改,p++以后,p的值进行更改,增加四个字节的地址(p是int*类型),*p进而指向数组中的第2个元素。

第二个for循环中,是对赋值后的数组进行打印。p = arr;中,数组名代表数组首元素的地址,在此过程中,*(p + i)并未像第一个for循环对于p值进行修改,而是进行了类似于p+1,p+2...的操作进行对数组一个个的访问。

代码运行结果如下:

在这里,我们需要注意的一点是,[ ]仅仅只是一个操作符,i和arr是它的操作数

即arr[i]===*(arr+i)==*(i+arr)==i[arr]

 4.2指针-指针(地址-地址)

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

前提条件: 指针1和指针2指向了同一块空间,否则是无意义的

//指针和指针相减的前提条件是二者指向了同一块空间
int main()
{
	int arr[10] = { 0 };
	char ch[5] = { 0 };
	printf("%d\n", &arr[4] - &arr[0]);
	printf("%d\n", &arr[0] - &arr[4]);
	printf("%d\n", &ch[0] - &arr[4]);
	return 0;
}

运行结果:

4.3指针的关系运算

指针是有大小的,指针的关系运算就是比较指针的大小

//倒着初始化数组内容
#define N_VALUES 5
int main()
{
	float values[N_VALUES];//浮点数数组values
	float* vp;
	for (vp = &values[N_VALUES]; vp > &values[0];)
	{
		*--vp = 0;
	}
	return 0;
}

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

即为P可以和P2进行比较但不能和P1进行比较

5. 指针和数组

指针变量就是指针变量,不是数组

指针变量的大小是4/8个字节,专门用来存放地址

数组就是数组,不是指针,数组是一块连续的空间,可存放1个或者多个类型相同的数据。

联系:

数组中,数组名其实是数组首元素的地址,数组名==地址==指针

数组是通过指针来访问的

当我们知道数组首元素的地址的时候,因为数组是连续存放的,所以通过指针可以遍历数组。

6. 二级指针

二级指针变量用来存放一级指针变量地址

指针变量也是指针,是变量就有地址,指针变量的地址存在二级指针中~

int**pp;

int*:pp指向的类型

*(第二个):说明pp是指针变量

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

理解方式如下:

7. 指针数组

存放指针(地址)的数组,数组的每个元素都是指针

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "hello world";
	char arr3[] = "cuihua";

	//指针数组
	char* parr[] = { arr1,arr2,arr3 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%s\n", parr[i]);
	}
	printf("%p\n", parr[1]);
	return 0;
}

在上段代码中,parr就是指针数组,一个数组里面存放了3个指针。

数组名是数组首元素地址。

运行结果:

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

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

相关文章

vulnhub系列:Momentum2

vulnhub系列&#xff1a;Momentum2 靶机下载 一、信息收集 nmap扫描C段存活 nmap 192.168.23.0/24目标地址为192.168.23.132 nmap扫描端口 nmap 192.168.23.132发现开放端口&#xff1a;22、80 目录扫描 python3 dirsearch.py -u http://192.168.23.132扫描发现目录&…

【OCR 学习笔记】二值化——全局阈值方法

二值化——全局阈值方法 固定阈值方法Otsu算法在OpenCV中的实现固定阈值Otsu算法 图像二值化&#xff08;Image Binarization&#xff09;是指将像素点的灰度值设为0或255&#xff0c;使图像呈现明显的黑白效果。二值化一方面减少了数据维度&#xff0c;另一方面通过排除原图中…

MyBatis--XML映射文件

MyBatis–XML映射文件 lombok工具包 为了解决声明实体类的get()和set()函数臃肿的问题&#xff0c;我们使用lombok工具包。 我们看一下lombok工具包为我们提供的注解&#xff1a; XML映射文件 我们需要遵守下面的规则&#xff1a; 首先XML映射文件和Mapper接口包应该在同…

用栈访问最后若干元素——682、71、388

682. 棒球比赛&#xff08;简单&#xff09; 你现在是一场采用特殊赛制棒球比赛的记录员。这场比赛由若干回合组成&#xff0c;过去几回合的得分可能会影响以后几回合的得分。 比赛开始时&#xff0c;记录是空白的。你会得到一个记录操作的字符串列表 ops&#xff0c;其中 ops[…

2024 Studio One6.6.1最新版中文版图文破解版教程

在数字音频工作站&#xff08;DAW&#xff09;的浩瀚宇宙中&#xff0c;Studio One始终以其独特的魅力和强大的功能脱颖而出。随着6.6.1版本的发布&#xff0c;这款由PreSonus精心打造的音乐制作软件再次迈上了一个全新的台阶&#xff0c;为音乐创作者们带来了前所未有的创作体…

【C++】vector及模拟实现

&#x1f680;个人主页&#xff1a;奋斗的小羊 &#x1f680;所属专栏&#xff1a;C 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 &#x1f4a5;1、vector的主要函数接口&#x1f4a5;2、vector的模拟实现&#x1f4a5;2.1 构造和析构&…

Python 3 入门基础知识【1】数据类型 安装下载 推荐

Python的重要性这里不多说了&#xff0c;是一门面向对象的解释型语言&#xff0c;使用缩进作为逻辑层次&#xff0c;功能强大且容易上手&#xff0c;其受欢迎程度也在逐年上升的。这里回顾一下python的基础知识&#xff0c;以便自己查看&#xff0c;欢迎正在学习python的同学参…

ArcGIS10.8 安装教程

目录 一、环境及安装包准备 二、安装流程 1、解压安装包ArcGIS_108.rar 2、安装 三、汉化 四、激活 五、自定义菜单&#xff08;可选&#xff09; 六、打开软件按查看 七、安装过程中出现的报错 八、其他 一、环境及安装包准备 安装环境&#xff1a;win7 安装包下载…

[FBCTF2019]RCEService1

打开题目 它给出了提示要求json格式&#xff0c;先尝试一下{"cmd":"ls"} 的确可以执行。接下来就记录过滤了那些关键字。发现键盘上有的特殊符号好像都被过滤。 flag在home目录下&#xff0c;不知道为什么find命令返回为空。。。 cat在这里仍然不能用。…

go语言后端开发学习(六) ——基于雪花算法生成用户ID

前言 在我们日常进行开发的时候,不可避免的会出现对用户表的操作&#xff0c;而为了保证每一个用户的唯一性&#xff0c;这就需要我们创建一个唯一性的id&#xff0c;但是现在有一个问题&#xff0c;如果我们仅仅像通过自增这样方式来创建唯一的id&#xff0c;这无疑是非常不合…

第N5周:Pytorch文本分类入门

本文为365天深度学习训练营 中的学习记录博客原作者&#xff1a;K同学啊 任务&#xff1a; ●1. 了解文本分类的基本流程 ●2. 学习常用数据清洗方法 ●3. 学习如何使用jieba实现英文分词 ●4. 学习如何构建文本向量 一、前期准备 环境安装 这是一个使用PyTorch实现的简单文…

超详细教程:贴片电阻要怎么焊接?

在电子硬件中&#xff0c;焊接是必不可少的一个技能。 但是在实际的学习或者工作过程中&#xff0c;往往会碰上各种各样的焊接条件&#xff0c;焊接环境&#xff0c;如果是对于直插式的元件&#xff0c;比如说焊接一个直插式LED灯或者直插式电解电容&#xff0c;可能很容易焊接…

15个网络工程师必须记住的专业术语和概念

网络工程师在日常工作中确实需要掌握大量的专业术语和概念&#xff0c;这些术语和概念是理解和应用网络技术的基础。以下是一些网络工程师必知的重要术语及其应用场景&#xff1a; 1. IP地址&#xff08;IP Address&#xff09; 定义&#xff1a;互联网协议地址&#xff0c;用…

Elasticsearch 8 RAG 技术分享

作者&#xff1a;来自 Elastic 中国区首席架构师 Jerry 本文由 Elastic 中国区首席架构师 Jerry Zhu 在【AI 搜索 TechDay】上的分享整理而成。【AI 搜索 TechDay】 是 Elastic 和阿里云联合主办的 AI 技术 Meetup 系列&#xff0c;聚焦企业级 AI 搜索应用和开发者动手实践&am…

framebuffer总结

FrameBuffer&#xff0c;可以译作"帧缓冲"&#xff0c;有时简称为 fbdrv。 这是一种独立于硬件的抽象图形设备。 是Linux为显示设备提供的一个接口&#xff0c;把显存抽象后的一种设备&#xff0c; 允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作 什么是…

QT 网络聊天室简易版

视频:qt开发网络聊天w室软件3.4界面开发_哔哩哔哩_bilibili 目录 UI部分 设计稿图 放置控件 界面美化 拖动窗体 设置界面 网络部分 配置对话框 多项目结果和服务器端设计 客户端框架开发 UI部分 设计稿图 放置控件 界面美化 现在我们把窗体自带的标题栏给去了,用我们自…

软件设计之MySQL(1)

软件设计之MySQL(1) 此篇应在JavaSE之后进行学习: 路线图推荐&#xff1a; 【Java学习路线-极速版】【Java架构师技术图谱】 Navicat可以在软件管家下载 使用navicat连接mysql数据库创建数据库、表、转储sql文件&#xff0c;导入sql数据 学习内容&#xff1a; 数据库概述表、…

6.IIC

理论 参考51单片机IIC理论&#xff1a;链接 在起始信号后必须发送一个7位从机地址 1位方向位&#xff0c;用“0”表示主机发送数据&#xff0c;“1”表示主机接收数据 代码编写 IIC三个模式配置 IIC(阻塞模式)配置 主要方式 IIC(中断模式)配置 IIC(DMA模式)配置 阻塞模式 mi…

智源研究院举办第一期数据与行业应用Workshop

近日&#xff0c;北京智源人工智能研究院联合中国互联网协会人工智能工委会、中国AIIA联盟数据委员会、CSDN举办了以“行业革新&#xff0c;数据先行”为主题的第一期数据与行业应用Workshop。来自智源研究院、中国信息通信研究院、航天信息技术有限公司、北京市科学技术研究院…