普通指针扫盲

news2024/10/2 8:29:44

一、什么是指针

C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节有唯一的内存地址。
CPU 通过内存寻址对存储在内存中的某个指定数据对象的地址进行定位。这里,数据对象是指存储在内存
中的一个指定数据类型的数值或字符串,它们都有一个自己的地址,而指针便是保存这个地址的变量。也
就是说:指针是一种保存变量地址的变量。

前面已经提到内存其实就是一组有序字节组成的数组,数组中,每个字节大大小固定,都是 8bit。对这些
连续的字节从 0 开始进行编号,每个字节都有唯一的一个编号,这个编号就是内存地址。示意如下图:
在这里插入图片描述

这是一个 4GB 的内存,可以存放 2^32 个字节的数据。右侧侧的连续的十六进制编号就是内存地址,每个内
存地址对应一个字节的内存空间。而指针变量保存的就是这个编号,也即内存地址。

二、为什么要使用指针

在C语言中,指针的使用非常广泛,因为使用指针往往可以生成更高效、更紧凑的代码。总的来说,使用
指针有如下好处:
1)指针的使用使得不同区域的代码可以轻易的共享内存数据,这样可以使程序更为快速高效;
2)C语言中一些复杂的数据结构往往需要使用指针来构建,如链表、二叉树等;
3)C语言是传值调用,而有些操作传值调用是无法完成的,如通过被调函数修改调用函数的对象,但是这
种操作可以由指针来完成,而且并不违背传值调用。

三、如何声明一个指针

3.1 声明并初始化一个指针

指针其实就是一个变量,指针的声明方式与一般的变量声明方式没太大区别:

int *p; // 声明一个 int 类型的指针 p
char *p // 声明一个 char 类型的指针 p
int *arr[10] // 声明一个指针数组,该数组有10个元素,其中每个元素都是一个指向 int
int (*arr)[10] // 声明一个数组指针,该指针指向一个 int 类型的一维数组
int **p; // 声明一个指针 p ,该指针指向一个 int 类型的指针

指针的声明比普通变量的声明多了一个一元运算符 “”。运算符 “” 是间接寻址或者间接引用运算符。当它
作用于指针时,将访问指针所指向的对象。在上述的声明中: p 是一个指针,保存着一个地址,该地址指
向内存中的一个变量; *p 则会访问这个地址所指向的变量。
声明一个指针变量并不会自动分配任何内存。在对指针进行间接访问之前,指针必须进行初始化:或是使
他指向现有的内存,或者给他动态分配内存,否则我们并不知道指针指向哪儿,这将是一个很严重的问
题,稍后会讨论这个问题。初始化操作如下:

/* 方法1:使指针指向现有的内存 */
int x = 1;
int *p = &x;// 指针 p 被初始化,指向变量 x ,其中取地址符 & 用于产生操作数内存地址

/* 方法2:动态分配内存给指针 */
int *p;
p = (int *)malloc(sizeof(int) * 10);// malloc 函数用于动态分配内存
free(p);// free 函数用于释放一块已经分配的内存,常与 malloc 函数一起使用,要使用这两个函数需要头文件 stdlib.h

指针的初始化实际上就是给指针一个合法的地址,让程序能够清楚地知道指针指向哪儿。

3.2 未初始化和非法的指针

如果一个指针没有被初始化,那么程序就不知道它指向哪里。它可能指向一个非法地址,这时,程序会报
错,在 Linux 上,错误类型是 Segmentation fault(core dumped),提醒我们段违例或内存错误。它也可
能指向一个合法地址,实际上,这种情况更严重,你的程序或许能正常运行,但是这个没有被初始化的指
针所指向的那个位置的值将会被修改,而你并无意去修改它。用一个例子简单的演示一下:

#include "stdio.h"
int main()
{
	int *p;
	*p = 1;
	printf("%d\n",*p);
	return 0;
}

这个程序可以编译通过,但是运行的话会报错,报错信息如下:
在这里插入图片描述

要想使这个程序运行起来,需要先对指针 p 进行初始化:

#include "stdio.h"
int main()
{
	int x = 1;
	int *p = &x;
	printf("%d\n",*p);
	*p = 2;
	printf("%d\n",*p);
	return 0;
}

这段代码的输出结果如下:
在这里插入图片描述

可以看到,对指针进行初始化后,便可以正常对指针进行赋值了。

3.3 NULL指针

NULL 指针是一个特殊的指针变量,表示不指向任何东西。可以通过给一个指针赋一个零值来生成一个
NULL 指针。

#include "stdio.h"
int main()
{
	int *p = NULL;
	printf("p的地址为%p\n",p);
	return 0;
}
/***************
* 程序输出:
* p的地址为(nil)
***************/

可以看到指针的地址为nil,即空值,也可以理解为0。在大多数的操作系统上,程序不允许访问地址为 0
的内存,因为该内存是为操作系统保留的。但是,内存地址 0 有一个特别重要的意义,它表明改指针不指
向一个可访问的内存位置。

四、指针的运算

C 指针的算术运算只限于两种形式:

1) 指针 + - 整数

可以对指针变量 p 进行 p++、p–、p + i 等操作,所得结果也是一个指针,只是指针所指向的内存地址相比于 p 所指的内存地址前进或者后退了 i 个操作数。比如,0x10000000等是内存地址的十六进制表示(数值是假定的),p 是一个 int 类型的指针,指向内存地址 0x10000008 处。则 p++ 将指向与 p 相邻的下一个内存地址,由于 int 型数据占 4 个字节,因此 p++ 所指的内存地址为 1000000c。其余类推。不过要注意的是,这种运算并不会改变指针变量 p 自身的地址,只是改变了它所指向的地址。

2)指针 - 指针

只有当两个指针都指向同一个数组中的元素时,才允许从一个指针减去另一个指针。两个指针相减的结果的类型是 ptrdiff_t,它是一种有符号整数类型。减法运算的值是两个指针在内存中的距离(以数组元素的长度为单位,而不是以字节为单位)。举个例子:

#include "stdio.h"
int main()
{
	int a[10] = {1,2,3,4,5,6,7,8,9,0};
	int sub;
	int *p1 = &a[2];
	int *p2 = &a[8];
	sub = p2-p1;
	printf("%d\n",sub);// 输出结果为 6
	sub = p1-p2;
	printf("%d\n",sub);// 输出结果为 -6
	return 0;
}

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

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

相关文章

【项目精选】病历管理系统设计与实现(源码+视频)

点击下载源码 企业财务管理系统主要用于电子病历来提高医院各项工作的效率和质量,促进医学科研、教学;减轻各类事务性工作的劳动强度,使他们腾出更多的精力和时间来服务于病人。本系统结构如下: 电子病例系统: 病人登…

通过知识库深度了解用户的心理

自助服务知识库的价值是毋庸置疑的,如果执行得当,可以帮助减少客户服务团队的工作量,仅仅编写内容和发布是不够的,需要知道知识库对客户来说是否有用,需要了解客户获得的反馈,如果你正确的使用知识库软件&a…

哪款蓝牙耳机打电话好用?打电话音质好的蓝牙耳机

现在几乎是人人离不开耳机的时代。在快节奏的生活和充满嘈杂声音的世界中,戴着耳机听歌,是每个人生活中最不可或缺的一段自由、放松的时光,下面小编就来分享几款通话音质好的蓝牙耳机。 一、南卡小音舱蓝牙耳机 动圈单元:13.3mm…

伯恩光学再成被执行人:多次因劳动纠纷被起诉,曾冲刺港交所上市

近日,贝多财经从天眼查APP了解到,伯恩光学(深圳)有限公司(下称“伯恩光学”)因《伯恩光学(深圳)有限公司与温*燕劳动合同纠纷的案件》一事,被广东省深圳市龙岗区人民法院…

服务端开发之Java备战秋招面试篇6-Java各种并发锁

努力了那么多年,回头一望,几乎全是漫长的挫折和煎熬。对于大多数人的一生来说,顺风顺水只是偶尔,挫折、不堪、焦虑和迷茫才是主旋律。我们登上并非我们所选择的舞台,演出并非我们所选择的剧本。继续加油吧! 目录 1、Java中主流锁分类体系介绍 2、乐观锁CAS原理刨…

wordpress 占用内容过高的解决方案

WordPress占用内存过高可能由多种因素引起,以下是一些可能的原因和解决方法: 主题和插件: WordPress的主题和插件是最常见的内存占用原因。确保使用的主题和插件是最新版本,并且只使用必需的插件。禁用不需要的插件,并…

【Linux】文件时间-ACM

文章目录文件时间-acmAccessChangeModify文件时间-acm 我们可以使用stat 文件名的方式查看对应的文件的时间信息 Access 表示文件最近一次被访问的时间 文件的访问 实际也就是文件的读取 实际操作中,文件的Access时间可能没有变化,这是因为在新的Linux内核中,Access时间不…

JavaScript 闭包的理解

1、概念 一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域 2、简单理解 闭包 内层函数 引用的外层函数变量 简单代码示例: function outer(){let i 1; // 被内层函数引用的变量function fn(){ // 内层函数console.log(i…

用javascript分类刷leetcode5.二分查找(图文视频讲解)

二分搜索 时间复杂度O(logn) 步骤&#xff1a; 从数组中间的元素开始&#xff0c;如果中间的元素正好是目标值&#xff0c;搜索结束如果目标值大于或小于中间的元素&#xff0c;则在大于或小于中间的元素的那一半继续搜索 代码模版 //二分查找伪代码模版 while (left <…

3.1 模拟栈+表达式求值+模拟队列+单调栈

模拟栈 题目链接 栈的数组模拟非常简单&#xff0c;不详细描述 设置一个指针指向栈顶第一个元素即可 STL中stack实现已经更新在STL_Stack #include<iostream> #include<string>using namespace std;const int N1e51; int m; string s; int stack[N]; int p;//指针…

Cartesi 2023 年 2 月回顾

2023年2月28日&#xff0c;通过ETH Denver和Cartesi的在线全球黑客马拉松一起开启黑客马拉松赛季!ETH Denver 正在热火朝天的进行着&#xff0c;我们正在为3月25日开始的首个全球在线黑客马拉松做准备。但这并不是本月发生的所有事情。我们在继续扩展和发展在全世界各地的社区&…

【软件测试】接口测试和接口性能测试,资深测试老鸟的总结......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 只会独立使用工具&a…

程序员必备!最值得收藏的宝藏网站大盘点

做为程序员&#xff0c;没有收藏点宝藏网站都说不过去。 除了常见的大家熟知的知乎、谷歌、b站、CSDN、掘金等&#xff0c;今天将介绍一些其他更加实用的宝藏网站&#xff0c;程序员小伙伴们可以按需收藏哦&#xff5e; 1.菜鸟教程&#xff1a;https://www.runoob.com/ 国内…

大数据处理技术导论(6) | Datawhale组队学习46期

文章目录1. hive 概述2. hive 与传统关系型数据库的对比3. hive 数据类型4. hive 数据模型5. hive 实战5.1 创建表5.2 修改表5.3 清空表、删除表5.4 其他命令项目地址 https://github.com/datawhalechina/juicy-bigdata&#xff0c;感谢项目团队的付出。本次主要学习 hive 相关…

web,h5海康视频接入监控视频流记录三(后台node取流)

前端vue&#xff0c;接入ws视频播放 云台控制 &#xff0c;回放预览&#xff0c;都是需要调对应的海康接口。相当于&#xff0c;点击时&#xff0c;请求后台写好的接口&#xff0c;接口再去请求海康的接口 调用云台控制是&#xff0c;操作一次&#xff0c;不会自己停止&#x…

元宇宙如何在未来5年影响你的业务

自新冠疫情暴发以来&#xff0c;虽然数字经济的和实体经济受到了严重的冲击和影响&#xff0c;但这也加速了元宇宙在全球的发展。区块链、数字资产和非同质化代币&#xff08;NFTs&#xff09;的兴起进一步推动了世界对元宇宙的需求。元宇宙被定义为用户可以在其中进行互动的虚…

HiveSQL一天一个小技巧:如何将分组内数据填充完整?

0 需求1 需求分析需求分析&#xff1a;需求中需要求出分组中按成绩排名取倒数第二的值作为新字段&#xff0c;且分组内没有倒数第二条的时候取当前值。如果本题只是求分组内排序后倒数第二&#xff0c;则很简单&#xff0c;使用row_number()函数即可求出&#xff0c;但是本题问…

Netty之io.netty.util.concurrent.Promise与io.netty.util.concurrent.Future初解

目录 目标 Netty版本 Netty官方API 三者之间的关系 基本使用方法 java.util.concurrent.Future io.netty.util.concurrent.Future io.netty.util.concurrent.Promise 目标 了解io.netty.util.concurrent.Promise与io.netty.util.concurrent.Future的基本使用方法。了解…

操作系统权限提升(二十四)之Linux提权-明文ROOT密码提权

系列文章 操作系统权限提升(十八)之Linux提权-内核提权 操作系统权限提升(十九)之Linux提权-SUID提权 操作系统权限提升(二十)之Linux提权-计划任务提权 操作系统权限提升(二十一)之Linux提权-环境变量劫持提权 操作系统权限提升(二十二)之Linux提权-SUDO滥用提权 操作系统权限…

Netty学习(二):线程模型

目录 一、线程模型基本介绍 二、传统阻塞IO服务模型 2.1 工作原理图 2.2 模型特点 2.3 问题分析 三、Reactor模式 3.1 完善传统阻塞I/O服务模型 3.2 Reactor模型原理图 3.3 Reactor模式中核心组成 3.4 Reactor模式分类 四、单 Reactor 单线程 4.1 原理图 4.2 方案说…