探索指针的奇妙世界,程序中的魔法箭头(上)

目录

  • 一.指针是什么
  • 二.指针和指针类型
    • 1.指针加减整数
    • 2.指针的解引用
  • 三.野指针
    • 1.野指针形成的原因
      • (1)指针未初始化
      • 指针越界访问
    • 2.如何规避野指针
      • (1)指针初始化
      • (2)小心指针越界
      • (3)指针指向的空间释放,及时置NULL
      • (4)避免返回局部变量的地址
      • (5)指针使用之前检查有效性
  • 四.指针运算
    • 1.指针加减整数
    • 2.指针减指针
    • 3.指针的关系运算
  • 五.指针和数组
  • 六.二级指针
  • 七.指针数组

一.指针是什么

指针是内存中最小单元(字节)的编号,也就是地址
我们平时口中所说的指针,通常说的是指针变量。
总结:指针就是地址,平时口头说的指针是指针变量

指针变量:我们通过取地址操作符取出变量的内存起始地址,把地址存放到一个变量中,这个变量就是指针变量。

#include<stdio.h>

int main()
{
	int a = 1;
	int* pa = &a; //pa是专门用来存放地址(指针)的,pa被称为指针变量。
	return 0;
}

总结:
指针变量就是用来存放地址的变量。(存放在指针中的值都被当成地址处理)
一个内存单元有唯一的地址。
地址的大小在32位平台上是4个字节,在64位平台上是8个字节

二.指针和指针类型

变量有不同的类型,比如整型,浮点型等等。指针也有这样的类型。

1.指针加减整数

#include<stdio.h>

int main()
{
	int a = 0x11223344;
	int* pa = &a;
	printf("%p\n", pa);
	printf("%p\n", pa + 1);

	return 0;
}

在这里插入图片描述

所以指针的类型决定了指针向前或向后走一步有多大距离

#include<stdio.h>

int main()
{
	char a = 0x11223344;
	char* pa = &a;
	printf("%p\n", pa);
	printf("%p\n", pa + 1);

	return 0;
}

在这里插入图片描述

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(long*));
	printf("%d\n", sizeof(float*));
	printf("%d\n", sizeof(double*));

	return 0;
}

在这里插入图片描述

我们通过计算不同类型指针变量的大小,发现在64位平台上均是8个字节。
那么指针类型的意义是什么呢?

在这里插入图片描述
在这里插入图片描述
如果改为char类型,44 33 22 11不会变成00 00 00 00,而是会变成 00 33 22 11

总结:
int*的指针解引用可以访问4个字节
char*的指针解引用可以访问1个字节
所以指针类型可以决定指针解引用的时候访问多少个字节(指针的权限)

三.野指针

指向的位置不可知(随机,不正确,没有明确限制)的指针被称为野指针。

1.野指针形成的原因

(1)指针未初始化

#include<stdio.h>

int main()
{
	int* p; //局部变量指针未初始化,默认为随机值
	*p = 10; 
	return 0;
}

指针越界访问

#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;
}

2.如何规避野指针

(1)指针初始化

1.明确知道指针初始化为谁的地址,就直接初始化
2.不知道指针初始化为什么值,暂时初始化为NULL

//指针初始化
#include<stdio.h>
int main()
{
	int a = 10;
	int* p1 = &a;
	int* p2 = NULL;
	return 0;
}

(2)小心指针越界

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

当释放空间后赶紧置空,避免成为野指针。大街上一条狗脱离主人,要把它栓到一棵树上,避免成为野狗。

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

(5)指针使用之前检查有效性

if (p != NULL)
{
	//使用
}

四.指针运算

1.指针加减整数

#include<stdio.h>

int main()
{
	int arr[10] = { 0 };
	int* p = &arr[0];
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		*p = i;
		p++;
	}
	p = arr;
	for (i = 0; i < sz; i++)
	{
		printf("%d\n", *(p + i));
	}
	//for (i = 0; i < sz; i++)
	//{
	//	printf("%d\n", arr[i]);
	//}
	return 0;
}

2.指针减指针

我们知道指针实际上就是地址,指针减指针也就是地址减去地址

>#include<stdio.h>

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

在这里插入图片描述

结论:指针减去指针得到的数值的绝对值是两个指针之间的元素个数
指针和指针相减的前提是两个指针指向同一块空间

//计算字符串长度
#include<stdio.h>

//int length(char* p) 
//{
//	int count = 0;
//	while (*p != '\0')
//	{
//		count++;
//		p++;
//	}
//	return count;
//}

//int length(char* p)
//{
//	if (*p == '\0')
//		return 0;
//	else
//		return 1 + length(p + 1);
//}

int length(char* p)
{
	char* start = p;
	while (*p != '\0')
	{
		p++;
	}
	return p - start;
}

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

3.指针的关系运算

指针的关系运算就是指针与指针之间比大小。

看这样一段代码

#define N 5
float v[N];
float* vp;
for (vp = &v[N]; vp > &v[0]; )
{
	*--vp = 0;
}

在这里插入图片描述

将上述代码稍作修改

#define N 5
float v[N];
float* vp;
for (vp = &v[N]; vp >= &v[0]; vp--) //这里变成了大于等于
{
	*vp = 0;
}

在这里插入图片描述

这段代码没什么问题也更容易理解。但是标准规定允许指向数组元素的指针与指向数组最后一个元素后面位置的指针比较,但是不允许与指向第一个元素前面位置的指针比较
在这里插入图片描述

五.指针和数组

我们通常所说的指针是指针变量,不是数组,是专门用来存放地址的。
数组是一块连续的空间,用来存放相同类型的数据。
联系:数组名是数组首元素的地址,地址就是指针。当我们知道数组首元素的地址,又因为数组是连续存放的,所以可以通过指针访问数组

//通过指针访问数组
#include<stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d\t%d\n", *(p + i), arr[i]);
	}
	return 0;
}

在这里插入图片描述

我们可以看到 *(p + i)与arr[i]是等价的

六.二级指针

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

	return 0;
}

在这里插入图片描述
在这里插入图片描述

#include<stdio.h>
int main()
{
	int a = 10;
	int* p = &a;
	int** pp = &p;//pp就是二级指针变量
	return 0;
}

在这里插入图片描述
二级指针变量就是用来存放一级指针变量的地址
一级指针变量用来存放变量的地址,这里就是a的地址

在这里插入图片描述

七.指针数组

指针数组就是存放指针的数组,也就是说指针数组是数组,并不是指针。

#include<stdio.h>
int main()
{
	char arr1[] = "ni hao";
	char arr2[] = "hello";
	char arr3[] = "happy";

	char* parr[] = {arr1,arr2,arr3};//指针数组
	return 0;
}

在这里插入图片描述

打印这三个字符串

	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%s\n", parr[i]);
	}

在这里插入图片描述

数组parr里面有3个元素,分别是数组arr1,arr2,arr3
通过指针数组,将这3个数组联系起来了

#include<stdio.h>

int main()
{
	int arr1[] = { 11,12,13,14,15 };
	int arr2[] = { 21,22,23,24,25 };
	int arr3[] = { 31,32,33,34,35 };

	int* parr[] = { arr1,arr2,arr3 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", parr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

这里类似于二维数组,并不是真正的二维数组。真正的二维数组的存储在内存中是连续的。

上述代码的结果:
在这里插入图片描述

这很像是二维数组打印出来的结果,区别:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

用Python实现Excel中的Vlookup功能

目录 一、引言 二、准备工作 三、实现Vlookup功能 1、导入pandas库 2、准备数据 3、实现Vlookup功能 4、处理结果 5、保存结果 四、完整代码示例 五、注意事项 六、总结 一、引言 在Excel中&#xff0c;Vlookup是一个非常实用的函数&#xff0c;它可以帮助我们在表…

014-信息打点-JS架构框架识别泄漏提取API接口枚举FUZZ爬虫插件项目

014-信息打点-JS架构&框架识别&泄漏提取&API接口枚举&FUZZ爬虫&插件项目 #知识点&#xff1a; 1、JS前端架构-识别&分析 2、JS前端架构-开发框架分析 3、JS前端架构-打包器分析 4、JS前端架构-提取&FUZZ 解决&#xff1a; 1、如何从表现中的JS提取…

1.11马原

同一性是事物存在和发展的前提&#xff0c;一方的发展以另一方的发展为条件 同一性使矛盾双方相互吸收有利于自身的因素&#xff0c;在相互作用中各自得到发展 是事物发展根本规律&#xff0c;唯物辩证法的实质和核心 揭示了事物普遍联系的根本内容和变化发展的内在动力 是贯…

VIM工程的编译 / VI的快捷键记录

文章目录 VIM工程的编译 / VI的快捷键记录概述笔记工程的编译工程的编译 - 命令行vim工程的编译 - GUI版vim备注VIM的帮助文件位置VIM官方教程vim 常用快捷键启动vi时, 指定要编辑哪个文件正常模式光标的移动退出不保存 退出保存只保存不退出另存到指定文件移动到行首移动到行尾…

Java面试汇总——jvm篇

目录 JVM的组成&#xff1a; 1、JVM 概述(⭐⭐⭐⭐) 1.1 JVM是什么&#xff1f; 1.2 JVM由哪些部分组成&#xff0c;运行流程是什么&#xff1f; 2、什么是程序计数器&#xff1f;(⭐⭐⭐⭐) 3、介绍一下Java的堆(⭐⭐⭐⭐) 4、虚拟机栈(⭐⭐⭐⭐) 4.1 什么是虚拟机栈&…

【开源】基于JAVA语言的软件学院思政案例库系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统管理员2.2 普通教师 三、系统展示四、核心代码4.1 查询思政案例4.2 审核思政案例4.3 查询思政课程4.4 思政案例点赞4.5 新增思政案例评语 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的软件学…

2024最新:optee系统开发精讲 - 课程介绍

&#xff08;本课程中如有涉及代码或硬件架构&#xff0c;则对应的版本号&#xff1a;TF-A 2.80&#xff0c;optee 3.20, Linux Kernel 6.3&#xff0c;armv8.79.0的aarch64&#xff09; &#xff08;注意&#xff1a; 该课程没有PPT&#xff0c;该课程是对照代码讲解的&#x…

六、Netty核心模块组件

目录 6.1 BootStrap&#xff0c;ServerBootStrap6.2 Future&#xff0c;ChannelFuture6.3 Channel6.4 Selector6.5 ChannelHandler 以及其实现类6.6 Pipeline 和 ChannelPipeline6.7 ChannelHandlerContext6.8 ChannelOption6.9 EventLoopGroup和其实现类 NioEventLoopGroup6.1…

力扣 第 122 场双周赛 解题报告 | 珂学家 | 脑筋急转弯 + 滑窗反悔堆

前言 整体评价 倒开差点崩盘&#xff0c;T4这个反悔堆写吐了&#xff0c;T3往众数上去猜了&#xff0c;幸好case良心。 T1. 将数组分成最小总代价的子数组 I 思路: 取 nums[1:] 的最小2个值 可以部分排序&#xff0c;这样更快捷 class Solution {public int minimumCost(in…

WorkPlus:构建高效协作的企业即时通讯解决方案

在现代企业中&#xff0c;高效沟通是实现协作和改善工作效率的关键。而企业即时通讯工具成为了推进沟通的利器。作为一款高质量的企业即时通讯解决方案&#xff0c;WorkPlus以其卓越的性能和独特的功能&#xff0c;助力企业构建高效协作的新格局。 为什么选择WorkPlus作为企业即…

一文读懂「RAG,Retrieval-Augmented Generation」检索增强生成

Retrieval-Augmented Generation&#xff08;RAG&#xff09;作为机器学习和自然语言处理领域的一大创新&#xff0c;不仅代表了技术的进步&#xff0c;更在实际应用中展示了其惊人的潜力。 RAG结合了检索&#xff08;Retrieval&#xff09;和生成&#xff08;Generation&#…

windows Server 退域操作

要将运行Windows Server 2003的域控制器从Active Directory环境中退出&#xff08;降级&#xff09;&#xff0c;您需要按照以下步骤操作&#xff1a; ### 步骤1&#xff1a;转移角色与功能 - 如果这台服务器拥有任何活动目录角色&#xff0c;如FSMO&#xff08; Flexible Sin…

AI教我学编程之C#类的实例化与访问修饰符

前言 在这篇文章中&#xff0c;我将带大家深入了解C#编程语言的核心概念&#xff0c;包括类的实例化、访问修饰符的应用&#xff0c;以及C#中不同数据类型的默认值。我会通过逐步分析和具体实例&#xff0c;详细解释如何在C#中正确创建和操作对象&#xff0c;并探讨如何通过访…

设计模式——装饰者模式

更多内容&#xff0c;前往 IT-BLOG 现实生活中常常需要给某类产品动态增加新的功能&#xff0c;如&#xff1a;给面条各种调味品。在软件开发过程中&#xff0c;有时想用一些现存的组件。这些组件可能只是完成一些核心功能。但在不改变其架构的情况下&#xff0c;可以动态地扩展…

Java毕业设计-基于jsp+servlet的大学生学业规划咨询服务平台管理系统-第84期

获取源码资料&#xff0c;请移步从戎源码网&#xff1a;从戎源码网_专业的计算机毕业设计网站 项目介绍 基于jspservlet的大学生学业规划咨询服务平台管理系统&#xff1a;前端 jsp、jquery、ajax&#xff0c;后端 servlet、jdbc&#xff0c;角色分为管理员、学生&#xff1b…

C++ :命名空间域

目录 冲突与命名&#xff1a; 举个例子&#xff1a; 全局与局部&#xff1a; 域作用限定符&#xff1a; 命名空间域&#xff1a; 冲突与命名&#xff1a; 在C语言中&#xff0c;我们通常会使用stdlib.h 而stdlib.h 本质上是一个函数的库&#xff0c;在程序中使用的大多数…

C++(14)——string的模拟实现

前几篇文章中介绍了关于以及其相关函数的使用&#xff0c;为了更清楚的了解这些函数的作用&#xff0c;本篇文章通过模拟实现的方式来加深对于函数作用原理的理解。 目录 1. String的整体框架&#xff1a; 1.1 成员变量&#xff1a; 1.2 构造函数&#xff1a; 1.3 析构函数…

第十一站:多态练习ODU

实现动态切换 ODU.h #pragma once #include <iostream> using namespace std; #define ODU_TYPE_311_FLAG "311" #define ODU_TYPE_335_FLAG "335" enum class ODU_TYPE {ODU_TYPE_311,ODU_TYPE_335,ODU_TYPE_UNKNOW };class ODU{ public:ODU();//发…

Elasticsearch的映射操作

本文来记录下Elasticsearch的映射操作 文章目录 映射的概述 映射的概述 Elasticsearch与mysql数据库对比 映射的概述 有了索引库&#xff0c;等于有了数据库中的 database。索引库(index)中的映射&#xff0c;类似于数据库(database)中的表结构(table)。创建数据库表需要设置字…

SpringBoot实现文件上传和下载实现全过程(值得珍藏)

1. 引言 在Web应用中&#xff0c;文件上传和下载是常见的需求。Spring Boot框架提供了强大的支持和便利的API&#xff0c;使得开发者可以轻松地实现文件上传和下载功能。本文将详细介绍如何在Spring Boot应用中实现文件上传和下载&#xff0c;包括实现原理和完整的代码示例。 …