初阶c语言之浅识指针

news2025/1/10 16:53:22

 

学习导航:>

1.指针是什么?

2.指针和指针类型

2.1指针+-整数

2.2指针的解引用

3.野指针

3.1野指针成因

3.2如何规避野指针

4.指针运算

4.1指针+-整数

4.2指针-指针

4.3指针的关系运算

5.指针和数组

6.二级指针

7.指针数组


1.指针是什么?


指针理解的2个要点:

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

 

指针变量

我们可以通过 & (取地址操作符)取出变量的内存起始地址,把地址可以存放到一个变量中,这个
变量就是指针变量
#include <stdio.h>
int main()
{
	int a = 10;//在内存中开辟一块空间
	int* p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
	//a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量中,p就是一个之指针变量。
	return 0;
}
总结:
指针变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)。
那这里的问题是:
一个小的单元到底是多大?(1个字节
如何编址?
经过仔细的计算和权衡我们发现一个字节给一个对应的地址是比较合适的。
对于 32 位的机器,假设有 32 根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压和低电平(低电压)就是(1或者 0)
那么 32 根地址线产生的地址就会是:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
...
11111111   11111111   11111111   11111111
这里就有 2 32 次方个地址。 每个地址标识一个字节,那我们就可以给 (2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G 的空闲进行编址。
同样的方法,那 64 位机器,如果给 64 根地址线,那能编址多大空间,自己计算。
这里我们就明白:
32 位的机器上,地址是 32 0 或者 1 组成二进制序列,那地址就得用 4 个字节的空间来存储,所以
一个指针变量的大小就应该是 4 个字节。
那如果在 64 位机器上,如果有 64 个地址线,那一个指针变量的大小是 8 个字节,才能存放一个地址。
总结:
指针是用来存放地址的,地址是唯一标示一块地址空间的。
指针的大小在 32 位平台是 4 个字节,在 64 位平台是 8 个字节

2.指针和指针类型


这里我们在讨论一下:指针的类型
我们都知道,变量有不同的类型,整形,浮点型等。那指针有没有类型呢?
准确的说:有的。
当有这样的代码:
int num = 10;
p = &num;
要将 &num num 的地址)保存到 p 中,我们知道 p 就是一个指针变量,那它的类型是怎样的呢?
我们给指针变量相应的类型。
	char* pc = NULL;
	int* pi = NULL;
	short* ps = NULL;
	long* pl = NULL;
	float* pf = NULL;
	double* pd = NULL;
这里可以看到,指针的定义方式是: type + *
其实:
char* 类型的指针是为了存放 char 类型变量的地址。
short* 类型的指针是为了存放 short 类型变量的地址。
int* 类型的指针是为了存放 int 类型变量的地址。
那指针类型的意义是什么?

2.1指针+-整数


#include <stdio.h>
//演示实例
int main()
{
	int n = 10;
	char* pc = (char*)&n;
	int* pi = &n;

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

其执行结果:

 我们发现,

当指针为char*类型时,指针+1,其步长为1

当指针为int*类型时,指针+1,其步长为4

总结: 指针的类型决定了指针向前或者向后走一步有多大(距离)。

2.2指针的解引用


//演示实例
#include <stdio.h>
int main()
{
	int n = 0x11223344;
	char* pc = (char*)&n;
	int* pi = &n;
	*pc = 0;   //重点在调试的过程中观察内存的变化。
	*pi = 0;   //重点在调试的过程中观察内存的变化。
	return 0;
}

总结:
指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)
比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。

3.野指针


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

3.1野指针成因

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

由于p为随机值,所以我们通过解引用操作符修改这块未知的空间所存放的值是极其危险的。

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;
}
3. 指针指向的空间释放
这里放在动态内存开辟的时候讲解,这里可以简单提示一下

3.2如何规避野指针

1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放即使置 NULL
4. 避免返回局部变量的地址
5. 指针使用之前检查有效性
#include <stdio.h>
int main()
{
	int* p = NULL;
	//....
	int a = 10;
	p = &a;
	if (p != NULL)
	{
		*p = 20;
	}
	return 0;
}

4.指针运算


4.1指针+-整数

同2.1.

4.2指针-指针

指针减指针可以用来计算字符数组的长度或字符串的长度

例如:

int my_strlen(char* s)
{
	char* p = s;
	while (*p != '\0')
		p++;
	return p - s;
}
#include <stdio.h>
int main()
{
	char str[] = "hello world";
	int len = my_strlen(str);
	printf("%d", len);
	return 0;
}

4.3指针的关系运算

当我们在写一个循环时,一不注意就会发生数组的越界访问

例如:

for(vp = &values[N_VALUES]; vp > &values[0];)
{
    *--vp = 0;
}
//既有向前的越界访问,又有向后的越界访问,可谓是糟糕透顶

我们将代码修正如下:

for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
    *vp = 0;
}
实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行。
标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与
指向第一个元素之前的那个内存位置的指针进行比较。

5.指针和数组


我们看一个例子:
#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;
}

运行结果:

可见数组名和数组首元素的地址是一样的。

结论: 数组名表示的是数组首元素的地址 。(2种情况除外,数组章节讲解了)
那么这样写代码是可行的:
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 的地址。
那我们就可以直接通过指针来访问数组。
如下:
#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("%d ", *(p + i));
	}
	return 0;
}

6.二级指针


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

a的地址存放在pa中,pa的地址存放在ppa中。

pa是一级指针,而ppa是二级指针。

对于二级指针的运算有:

*ppa 通过对 ppa 中的地址进行解引用,这样找到的是 pa *ppa 其实访问的就是 pa 。
int b = 20;
*ppa = &b;//等价于 pa = &b;
**ppa 先通过 *ppa 找到 pa , 然后对 pa 进行解引用操作: *pa ,那找到的是 a 。
**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;

7.指针数组


指针数组是指针还是数组?

答案:是数组。是存放指针的数组。

数组我们已经知道整形数组,字符数组:
int arr1[5];
char arr2[6];

那指针数组是怎样的?

我们通过仿照大概可以写出来:

	int* arr3[5];
	char* arr4[6];

我们可以这样描述它,arr3数组包含5个元素,每个元素都是int* 类型的。

小结:指针的内容远不止这些,这里我们只是对指针做出简单的认识,更加高阶的指针知识请参照c语言进阶系列-指针详解。

本章完!

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

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

相关文章

动态路由协议解析(rip)

因为今天是1024程序猿节&#xff0c;小刘就在这里祝各位程序猿大佬们节日快乐啦 作者介绍&#xff1a; &#x1f4b0;作者&#xff1a;小刘在C站 ✨每天分享课堂笔记 &#x1f338;夕阳下&#xff0c;是最美的绽放 目录 动态路由协议和 rip 1.动态路由 2.动态路由协议工作…

非线性方程求根——牛顿迭代法

一、牛顿法 1.实质&#xff1a;牛顿法实质上是一种线性方法&#xff0c;其基本思想是将非线性方程f(x)0逐步归结为某种线性方程来解。 2.牛顿法公式&#xff1a; 已知方程f(x)0有近似解xk,假设&#xff0c;将f(x)在点xk泰勒展开&#xff0c;有则方程f(x)0可近似表示为&#…

【开关电源六】buck电路的输出纹波定量分析走一波?

在TI官网发现一篇关于降压开关电源的输出纹波定量分析&#xff0c;看完觉得挺不错的&#xff0c;于是大概整理翻译了一下分享出来。以往我们分析开关电源的一次纹波都是定性分析&#xff0c;知道输出电压纹波和电感纹波电流、输出滤波电容ESR等有关&#xff0c;今天我们用数学、…

再看 Logback 源码

三大组件 Logback 构建在三个主要的类上&#xff1a;Logger&#xff0c;Appender 和 Layouts。这三个不同类型的组件一起作用能够让开发者根据消息的类型以及日志的级别来打印日志。 Logger 类作为 logback-classic 模块的一部分。Appender 与 Layouts 接口作为 logback-core 的…

VapSR

1024刚过还得搬砖 注意机制在设计高级超分辨率&#xff08;SR&#xff09;网络中起着关键作用。在这项工作中&#xff0c;作者们通过改进注意机制设计了一个高效的SR网络。VapSR以更少的参数优于当前的轻量级网络。董超团队开源超大感受野注意力超分方案 paper&#xff1a;ht…

【数据挖掘 | 可视化】 WordCloud 词云(附详细代码案例)

&#x1f935;‍♂️ 个人主页: 计算机魔术师 &#x1f468;‍&#x1f4bb; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 开发环境 编辑器&#xff1a; jupyter notebook 解释器&#xff1a; python 3.7在七夕节中&#xff0c;博主写了一篇为女友收集…

自学Vue之路——Vue介绍及基本语法

今日内容概要 前端发展介绍Vue的快速使用插值语法指令系统之文本指令指令系统之事件指令指令系统之属性指令 前端发展介绍 HTML(5)、CSS(3)、JavaScript(ES5、ES6、ES13)&#xff1a;编写一个个的页面 -> 给后端(PHP、Python、Go、Java) -> 后端嵌入模板语法 -> 后端…

Arduino基础知识

目录&#xff1a; 第1章 概述 1.1 Arduino简介 1.2 Arduino内部结构 第2章 Arduino编程 2.1 Arduino开发环境 2.2 Arduino语言概述 2.3 Arduino基本函数 第3章 Arduino通信教程 3.1 SPI通信 3.2 红外通信 3.3 WiFi通信 3.4 蓝牙通信 第4章 Arduino实验 1、接收串…

【学生管理系统】权限管理之用户管理—查询所有用户并关联相关角色

目录 一、查询所有用户&#xff08;关联角色&#xff09; 1&#xff09;后端 2&#xff09;前端 &#x1f49f; 创作不易&#xff0c;不妨点赞&#x1f49a;评论❤️收藏&#x1f499;一下 一、查询所有用户&#xff08;关联角色&#xff09; 1&#xff09;后端 修改javaB…

牛客小题练手 | 二叉树(三)

&#x1f308;刷题&#xff0c;面试&#xff0c;求职&#xff0c;快来牛客网一起成为offer收割机&#xff01; 点击注册收割offer 一、BM32 合并二叉树 描述 已知两颗二叉树&#xff0c;将它们合并成一颗二叉树。合并规则是&#xff1a;都存在的结点&#xff0c;就将结点值加…

前端无法渲染CSS文件

&#x1f680; 优质资源分享 &#x1f680; 学习路线指引&#xff08;点击解锁&#xff09;知识定位人群定位&#x1f9e1; Python实战微信订餐小程序 &#x1f9e1;进阶级本课程是python flask微信小程序的完美结合&#xff0c;从项目搭建到腾讯云部署上线&#xff0c;打造一…

Air780E模块PPP应用开发指南

目录PPP拨号windows下PPP拨号配置标准调制解调器设置拨号连接验证拨号结果linux下PPP拨号1.安装 PPP 拨号软件2.创建拨号脚本文件/etc/ppp/peers/air-ppp/etc/ppp/peers/air-chat-connect/etc/ppp/peers/air-chat-disconnect3.创建好以上三个文件后就可以通过pppd拨号了4.拨号成…

机械转码日记【21】list使用及list的模拟实现

目录 前言 1.list的使用 1.2sort和unique 2.list的模拟 2.1构造函数 2.2push_back() 2.3迭代器 2.3.1简洁版 2.3.2升级版&#xff08;重要&#xff09; 2.4insert和erase与迭代器失效 2.4.1list的迭代器失效 2.5析构函数 2.6深拷贝构造 前言 list是我们数据结构…

Web前端 | JavaScript(DOM编程)

✅作者简介&#xff1a;一位材料转码农的选手&#xff0c;希望一起努力&#xff0c;一起进步&#xff01; &#x1f4c3;个人主页&#xff1a;每天都要敲代码的个人主页 &#x1f525;系列专栏&#xff1a;Web前端 &#x1f4ac;推荐一款模拟面试、刷题神器&#xff0c;从基础到…

Qt文档阅读笔记-Hello Speak Example

官方的这个例子比较有意思&#xff0c;在此记录下&#xff0c;方便以后查阅。 Hello Speak Example 这个例子主要是使用QTextToSpeech类将用户自定义输入的文本转换为口语&#xff0c;包括高低音、声音大小、读速。并且能够选择语言和声音。 包含的文件如下&#xff1a; 本篇博…

一文搞懂【知识蒸馏】【Knowledge Distillation】算法原理

知识蒸馏算法原理精讲 文章目录知识蒸馏算法原理精讲1. 什么是知识蒸馏&#xff1f;2. 轻量化网络的方式有哪些&#xff1f;3. 为什么要进行知识蒸馏&#xff1f;3.1 提升模型精度3.2 降低模型时延&#xff0c;压缩网络参数3.3 标签之间的域迁移4. 知识蒸馏的理论依据&#xff…

【JavaWeb】初识HTTP学习

文章目录JavaWeb之HTTP学习1、HTTP相关基本概念2、数据格式介绍2.1 请求的数据格式2.2 响应的数据格式案例JavaWeb之HTTP学习 1、HTTP相关基本概念 什么是HTTP&#xff1f; HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是一个简单的请…

Spring Cloud基本介绍

✨ Spring Cloud基本介绍1.微服务中的相关概念1.1服务的注册与实现1.2负载均衡1.3熔断1.4链路追踪1.5API网关2.Spring Cloud的介绍2.1基本认识2.2Spring Cloud的架构2.2.1Spring Cloud的核心组件2.2.2Spring Cloud体系结构2.3Spring Boot和Spring Cloud的区别与联系2.3.1 Sprin…

【Spring】简单的登录案例和配套知识

本篇文章接着介绍 Spring 的相关知识&#xff0c;主要通过一个非常非常简单用户登录案例来介绍&#xff0c;各位大佬们路过记得赏小的一颗赞&#x1f929; 文章目录1. 演示一下 Spring 管理类的模式2. 用户登录案例2.1 准备的对象和其功能2.1.1 User2.1.2 UserController2.1.3 …

05、JavaWeb启程——JDBC详解

1、JDBC概述 1、持久化概述 【简介】&#xff1a; 持久化指的是把内存中的数据存储到可掉电存储设备中以供之后使用。 2、JDBC概述 【简介】&#xff1a; JDBC是一种用于执行SQL语句的Java API&#xff0c;可以为多种关系型数据库提供统一的访问。 【JDBC本质】&#xff1…