【C语言】初阶指针的简单使用 _指针运算 指针和数组的关系[初阶篇 _学习专用]

news2025/1/11 14:13:06

1.指针是什么?

在学习指针的时候,我们经常会看到下面这段代码:

int main()
{
	int a = 10;
	int* pa = &a;
	*pa = 20;
}

之前并没有接触过指针的朋友们看到后可能是一头雾水,根本不知道从哪里去理解;下面我们就通过一些场景慢慢的去理解:

试想一下:如果你要给你的好朋友寄过去一些好吃的,然而你并不知道地址,这时你也许就会很懊恼;但如果你知道了你的好朋友的地址,你就可以通过快递把这些好吃的送到你朋友的身边。

把我们日常所说的地址类比到计算机,其实那些地址对应的就是计算机内存中的内存单元编号,也就是我们所说的指针。

比如说一栋宿舍楼,你在303号房间,你的朋友在605号房间,那么你就需要通过楼层和门牌号来找到你朋友所在的位置。

内存单元编号 = 地址 = 指针(指针变量,习惯于称为指针)。

指针就是存放地址的变量;下面通过一幅图来更好的理解上面的代码:

 * :解引用操作符,也叫间接访问操作符。

*pa就是找到0x0012ff40这片地址空间存储的数据,进而可以进行打印和修改。

这里还涉及到一个大小端存储的问题,我们所用到的vs2019是小端字节序存储(低位数据存储在低地址),把存储的地址倒着读取就是a的地址。 

指针的大小

int main()
{
	printf("%d\n", sizeof(int*));
	printf("%d\n", sizeof(char*));
	printf("%d\n", sizeof(long*));
	printf("%d\n", sizeof(long long*));
	printf("%d\n", sizeof(float*));
	printf("%d\n", sizeof(double*));
	return 0;
}

运行结果:

这里有很多人会有疑问:为什么结果是一样的,并且大小都是4?

那么接着往下看:

对于32位机器,假设有32根地址线,那么32根地址线产生的地址就会是:

1个字节 = 8个比特位;所以把这32个字节存储起来需要4个比特位;sizeof(指针变量)在32位机器下的结果就是4。

在64位机器下,地址是由64根地址线组成的,和32位机器下存储的道理是一样的——指针的大小是8个字节;

1.在32位机器下,指针的大小是一定的,都是4个字节。

2.在64位机器下,指针的大小也是一定的,都是8个字节。

2.指针和指针类型

指针也是变量,也是有类型的:

int a = 10;
int* pa = &a;//pa指向的空间时int类型,pa是一个整型指针
char ch = 'a';
char* pc = &ch;//pc指向的空间时char类型,pc是一个字符指针

2.1 解引用指针变量 

不同类型的指针变量解引用访问权限不同,光说不练假把式,下面通过两段程序来进一步了解:

程序1:

int main()
{
	int arr[10] = { 0 };
	char* pc = (char*)arr;
	for (int i = 0; i < 40; i++)
	{
		*pc = 'x';
		pc++;
	}
	return 0;
}

  

程序2:

int main()
{
	int arr[10] = { 0 };
	int* pa = arr;
	for (int i = 0; i < 10; i++)
	{
		*pa = 0x11223344;
		pa++;
	}
	return 0;
}

 

通过vs的调试窗口可以发现,int类型的指针解引用时可以访问四个字节,char类型的指针解引用时可以访问一个字节;

指针类型决定了解引用时的访问权限。

2.2 指针步长

不同的指针类型不仅解引用时访问权限有所不同,步长也是不同的:

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

 

指针的类型决定了指针向前或者向后一步所走的步长;

整型指针一步的步长为4个字节,字符型为1个字节。

3.野指针

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

3.1 野指针成因

1.指针未初始化

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

2.指针越界访问

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

3.已经销毁的栈帧

int* test()
{
	int a = 10;
	return &a;
}
int main()
{
	int* pa = test();//出了test函数,栈帧销毁
	printf("%d\n", *pa);
	return 0;
}

3.2 如何避免野指针 

1.指针初始化

2.小心指针越界

3.指针指向空间释放即使置NULL

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

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

int main()
{
	int* p = NULL;
	int a = 10;
	p = &a;
	if (p != NULL)
	{
		*p = 20;
	}
	return 0;
}

4.指针运算

4.1 指针+-整数

指针+整数:

int main()
{
	int arr[10] = { 0 };
	int* pa = arr;
	for (int i = 0; i < 10; i++)
	{
		*(pa+i) = 10;//指针+整数
	}
	return 0;
}

指针-整数和指针+整数是相似的,这里就不过多解释了。 

4.2 指针-指针

指针减指针是有前提的,相减的两个指针必须是连续的,一般常用语数组。

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

指针-指针得到的是指针之间元素的个数; 

指针减指针实现库函数strlen:

int my_strlen(char* str)
{
	char* ch = str;
	while (*str != '\0')
	{
		str++;
	}
	return str - ch;
}
int main()
{
	char arr[] = "abcdef";
	int ret = my_strlen(arr);
	return 0;
}

5.指针和数组 

首先,我们来了解一个概念——数组名;

数组名:首元素地址;下面两种情况除外;

1.sizeof(arr),arr表示整个数组,sizeof(arr)求出的是整个数组的大小。

2.&arr,此时arr表示整个数组,&arr取出的是整个数组的地址。

 

&arr和arr打印出的地址是相同的,那我们怎么证明&arr取出的是整个数组的地址呢?

int main()
{
	int arr[10] = { 0 };
	printf("%p\n", &arr);
	printf("%p\n", &arr + 1);

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

 

&arr+1所走的步长是40个字节,刚好是数组的大小,说明&arr取出的确实是整个数组的地址。 

6.二级指针

int main()
{
	int a = 10;
	int* pa = &a;

	int** ppa = &pa;

	return 0;
}

 

 

初阶指针的使用到这里就结束了,对以上内容存在疑问的可以直接私信博主,Fighting! Fighting! Fighting!

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

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

相关文章

STM32开发时HardFault错误的排查

STM32开发时HardFault错误的排查 本篇是 嵌入式开发-STM32硬件I2C驱动OLED屏 一文的扩展。 把相关的问题记录一下&#xff0c;给遇到HardFault_Handler问题的朋友做个参考。 故障现象 做STM32开发&#xff0c;经常遇到HardFault错误&#xff0c;也就是程序不会正常运行&…

WPF动画(2)

动画的生命周期 从技术的角度看&#xff0c;WPF动画只是暂时的&#xff0c;这意味着它们不能真正改变基本属性的 值&#xff0c;当动画处于活动状态时&#xff0c;只是覆盖了属性的值 。 单向动画&#xff0c;在动画运行结束后会保持处于活动状态&#xff0c;这是因为动画需要…

(十一)手写简单的Spring框架

文章目录第一步&#xff1a;搭建环境第二步&#xff1a;准备好要管理的Bean第三步&#xff1a;准备myspring.xml配置文件第四步&#xff1a;编写MyApplicationContext接口第五步&#xff1a;编写MyClassPathXmlApplicationContext第六步&#xff1a;采用Map集合存储Bean第七步&…

第十一章 Golang面向对象编程(下)

面向对象编程三大特性 基本介绍 Golang仍然有面向对象编程的继承&#xff0c;封装和多态的特性&#xff0c;只是实现的方式和其他OOP语言不一样。 封装 面向对象编程思想-抽象 我们在前面去定义一个结构体的时候&#xff0c;实际上就是把一类事物共有的属性&#xff08;字段…

Presto 之 explain and explain analyze的实现

一. 前言 本文主要探索在Presto中Explain功能是如何实现的。在Presto中&#xff0c;Explain用法有两种&#xff0c;一种是单纯的explain&#xff0c;此场景只会显示经过RBO优化后的执行计划&#xff0c;但是查询并不会真正地执行。第二种是explain analyze&#xff0c;此场景会…

JVM常用参数

JVM内存相关的几个核心参数 -Xms&#xff1a;Java堆内存初始大小-Xmx&#xff1a;Java堆内存的最大大小-Xmn&#xff1a;Java堆内存中的新生代大小&#xff0c;扣除新生代剩下的就是老年代的内存大小了-XX:PermSize&#xff1a;永久代大小-XX:MaxPermSize&#xff1a;永久代最…

疾控物资管理系统-疾控中心物资管理系统

一、系统概述 东识科技&#xff08;DONWIT&#xff09;疾控中心物资管理系统&#xff08;智物资DW-S300&#xff09;是依托互3D技术、云计算、大数据、RFID技术、数据库技术、AI、视频分析技术对RFID智能仓库进行统一管理、分析的信息化、智能化、规范化的系统。 随着疫情的突…

(STM32)从零开始的RT-Thread之旅--GPIO

上一篇&#xff1a; (STM32)从零开始的RT-Thread之旅--基础项目构建与时钟配置 无论什么开发板&#xff0c;最先调试的肯定是GPIO&#xff0c;一般用来用作指示灯或者按键输入。本篇只是很简单的GPIO应用&#xff0c;没有具体分析RTT框架实现。 首先先创建一个BSP文件夹&…

机器人操作系统ROS(21) jetson nano安装torch tensorflow

安装torch、tensorflow其实跟普通在Linux系统安装没有区别&#xff0c;但是Linux是arch64位的&#xff0c;而jetson是aarch64位的&#xff0c;所以还是不太一样。 另外一个坑是&#xff1a;购买的创乐博的机器人&#xff0c;已经安装ros&#xff0c;但是安装torh的时候需要apt …

使用Spring实现工厂+策略模式

使用Spring实现工厂策略模式 这里使用发短信业务&#xff1a; 不同短信有不同模板但是发送方法都相同只是发送内同不同 1. 定义短信发送策略接口&#xff1a; //策略接口 public interface SmsTemStrategy {public void sendSms(Map<String,String> params); }2.短信…

【Python】Numpy傅里叶变换总结

文章目录简介fft简介 Fourier变换极其逆变换在数学上的定义如下 F(ω)∫−∞∞f(t)e−iωtdtf(t)π2∫−∞∞F(ω)eiωtdωF(\omega)\int^\infty_{-\infty}f(t)e^{-i\omega t}\text dt\\ f(t)\frac{\pi}{2}\int^\infty_{-\infty}F(\omega)e^{i\omega t}\text d\omega F(ω)∫−…

飞行机器人专栏(八)-- AGX Xavier 通信、控制及视觉应用开发

目录 0. Introduction of Jetson Developer kits 1. 硬件对比 Jetson 模组系列 Jetson AGX Orin 系列 Jetson Orin NX 系列 Jetson AGX Xavier 系列 Jetson Xavier NX 系列 Jetson TX2 系列 Jetson Nano 2. 应用场景 1. Introduction of AGX Xavier Taking Perform…

HTML+PHP+MySQL实现新闻列表模块(1+X Web前端开发中级 例题)——初稿

&#x1f4c4;题目要求 阅读下列说明、效果图、MySQL数据库操作和代码&#xff0c;进行动态网页开发&#xff0c;填写&#xff08;1&#xff09;至&#xff08;15&#xff09;代码。&#x1f9e9;说明 该程序为一个html和php混合的新闻列表模块&#xff0c;使用PHP语言&#x…

【深入浅出Spring6】第九期——Spring对事务的支持

因为很多系统都包含事务&#xff0c;所以Spring提供了相关的Api&#xff0c;属于AOP切面编程的二次封装 那么什么是事务&#xff08;Transaction&#xff09;呢&#xff1f; 指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)在一个业务流程中可能需要多条DML联合执…

【Hack The Box】Linux练习-- OpenAdmin

HTB 学习笔记 【Hack The Box】Linux练习-- OpenAdmin &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月22日&#x1f334; &#x…

Flutter组件--OverflowBox、SizedOverflowBox(子组件超出父组件裁剪)

1.OverflowBox介绍 OverflowBox 允许子控件超出父控件的边界。这个特性主要可以用来实现文字或者按钮角标的. OverflowBox构造函数 const OverflowBox({Key? key,this.alignment Alignment.center,this.minWidth,this.maxWidth,this.minHeight,this.maxHeight,Widget? ch…

java面试强基(8)

String、StringBuffer、StringBuilder 的区别&#xff1f; 可变性 ​ String 是不可变的。StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类&#xff0c;在 AbstractStringBuilder 中也是使用字符数组保存字符串&#xff0c;不过没有使用 final 和 private …

Air780E涂鸦云远程开关-LuatOS

Air780E涂鸦云远程开关-LuatOS 涂鸦智能在远程开关和灯控领域可以算是龙头了&#xff0c;今天就来学习一下&#xff0c;如何接入涂鸦云平台 一、涂鸦云准备 注册账号不写了&#xff0c;自己注册账号即可 1、创建产品 点击产品->极速智能化->产品开发页面的创建产品 …

JDK动态代理

可以针对一些不特定的类或者一些不特定的方法进行代理 可以在程序运行时动态变化代理规则 代理类在程序运行时才创建代理模式成为动态代理 代理类并不是在Java代码中定义好的 而是在程序运行时根据在Java代码中指示动态生成的 Proxy JDK动态代理 面向接口 import java.lang.r…

前后端分离页面(从数据库到前端、后端手把手教你如何搭建 -- 功能实现:增加查询)

目录 一、准备条件 前台 后台 二、数据库设计 建议不要导入&#xff0c;导入有乱码的风险&#xff1b;新建查询运行下面代码更快捷。 三、导入前端项目 导入后使用命令行打开黑窗口&#xff0c;测试一下有没有npm -v环境 如果出现命令无效&#xff0c;就使用管理员身份打开…