C语言——指针初阶

news2024/11/16 22:56:10

 哈喽,大家好,今天我们来学习C语言中的指针,今天主要学习初阶指针,后期我们将继续学习指针进阶。

目录

1. 指针是什么

2. 指针和指针类型

2.1 指针+-整数

2.2 指针的解引用

3. 野指针

3.1 野指针成因

3.2 如何规避野指针

4. 指针运算

4.1 指针+-整数

4.2 指针-指针

4.3 指针的关系运算

5. 指针和数组

6. 二级指针

7. 指针数组


1. 指针是什么

指针是什么?

在 C 语言中,指针是一种特殊的变量,能够存储另一个变量的内存地址。指针变量可以用来访问、修改存储在内存中的数据。

将一个变量的地址存储在指针变量中,可以通过解引用操作符(*)来访问指针所指向的变量的值。例如,可以通过以下方式声明和使用一个整型变量和一个指向该变量的指针:

在这个例子中,我们定义了一个名为 a 的整型变量,它的值为 10。然后,我们定义了一个指向 a 的指针变量 p,使用地址运算符 & 来获取 a 的地址,并将该地址存储在指针 p 中。最后,我们通过解引用操作符 * 来输出指针 p 所指向的变量的值,即输出了变量 a 的值 10

指针理解的2个要点:

1. 指针是内存中一个最小单元的编号,也就是地址

2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量

我们可以用下图来理解内存: 

  • 在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以 一个指针变量的大小就应该是4个字节。
  • 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地 址。

指针的大小:

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

在32位机器下指针大小都是4字节

 在64位机器下指针大小都是8字节

总结 

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

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

2. 指针和指针类型

这里我们在讨论一下:

指针的类型

我们都知道,变量有不同的类型,整形,浮点型等。那指针有没有类型呢?

准确的说:有的。

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

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

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

那指针类型的意义是什么?

作用一:

 如果用int*类型的指针pa来接收a的地址,当改变*pa,a的4个字节中的内容都发生了改变:

 如果用char类型的指针pa来接收a的地址,当改变*pa,只有一个字节中的内容发生了改变:

 int*的指针解引用访问4个字节,char*类型指针解引用访问1个字节

总结: 指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。

比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。

左用二:

观察下面代码:

 我们发现:

int*类型的pa加1,地址向后增加了4位

char*类型的pb加1,地址向后增加了1位

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

2.1 指针+-整数

在C语言中,指针可以进行加减整数的操作,这个操作的含义是在指针所指向的地址上加上或减去一个整数量,从而实现指针位置的移动。

以下是一些示例代码:

 在这个例子中,我们定义了一个整型数组a和一个字符型指针s。

对于整型指针p,它指向数组a的第一个元素,并且可以使用指针加法将p加1,指向数组a的第二个元素。

对于字符指针s,它指向一个字符串的第一个字符,并且通过指针加法将s加2,指向字符串的第3个字符。

使用指针访问数组:

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

这段代码中,使用指针变量p直接访问数组元素,相当于p + i所指向的内存位置就是数组arr[i]的地址。在这个地址上进行赋值操作,实现了对数组值的修改。同时,使用指针加法能够更方便地对数组元素进行遍历。

需要注意的是,指针加减整数的操作有时会越过数组的边界,访问到不合法的内存位置。因此,在进行指针加减整数的操作时,应该保证指针不会越界。

2.2 指针的解引用

这段代码演示了如何对一个整型变量n的高/低位进行操作,并观察内存的变化。代码中定义了一个整型变量n,并给它赋予十六进制值0x11223344,然后分别使用指向char类型的指针pc和指向int类型的指针pi对n进行操作。

首先,将指针pc指向n,并将它转换为指向char类型的指针。这样,pc能够对n高/低位进行逐一操作,因为char类型只占用一个字节。接着,将pc指向的第一个字节赋值为0,这将会将n的最低字节设为0,其他字节不变。

然后,通过指针pi对整型变量n进行操作。因为pi是指向整型变量n的指针,所以它可以对整个n进行操作。将pi指向的值赋值为0,这将会将整个n的值设为0。

需要注意的是,在对整型变量n的高/低位进行操作时,必须要考虑机器的大小端问题,否则可能会导致数据的读取和写入出现问题。

总结:

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

3. 野指针

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

野指针是指没有被初始化或已经被释放的指针,它指向内存中未知的地址或者不可访问的地址。野指针的出现可能会导致程序的崩溃、内存泄漏等问题。

3.1 野指针成因

 1.指针未赋初值,或者被赋了一个未知的值,导致指针指向一个不可知的地址。

 2.指针已经被释放,但是程序还尝试去访问该指针所指向的内存空间。

代码一:

代码二:

 此程序存在返回局部变量的指针问题,即将一个指向局部变量的指针返回给调用者。具体问题在 test() 函数中,函数中定义的 a 变量是一个局部变量,函数执行完毕后,系统会收回这个变量的内存空间,在该函数中申请的内存空间随即被释放。所以 a 变量所处的内存空间在函数执行完毕后已经被释放了,返回的指针 &a 指向的内存空间已经不再是该变量的内存空间,因此这个指针是无效的,称为“悬挂指针”。

3.指针越界,指向了不属于该指针所指向内存空间的位置。

代码一:

 代码二:

这段程序中存在指针p越界的问题,导致arr数组发生了内存越界的现象,它的具体问题在 “for” 循环中的语句 *(p++) = i;循环次数超过了数组的长度,因此当指针 p 指向数组 arr 区域外时就会出现非法访问,是一种常见的野指针错误。

更改方法可以是检查循环范围,修改为循环10次即可.

3.2 如何规避野指针

1. 指针初始化

在定义指针变量时,最好将其初始化为空指针,防止指针未初始化就被使用,造成意料之外的行为.

2. 小心指针越界

要避免指针越界,应该注意以下几点:

  1. 确认数组长度:在定义数组时,要确保数组长度足够,避免出现数组访问越界的情况。

  2. 检查数组下标:在使用数组下标时,要确保数组下标不会越界,比如当下标小于 0 或大于等于数组长度时,就可能出现数组越界的情况。

  3. 指针的作用域:由于指针变量的生命周期很长,要确保指针变量所指向的内存空间还没有被释放,避免因为指针变量所指向的内存空间已经被释放导致的指针越界的问题。

  4. 指针的使用范围:在使用指针时,要确保指针变量所指向的内存空间的有效范围,避免访问指针所指向的内存空间以外的内存,造成指针越界的情况。

  5. 指针的类型:当使用指针访问一个内存块时,要确保指针的类型和所指向的内存块的类型匹配,避免出现类型不匹配导致的访问越界问题。

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

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

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

在使用指针变量之前,最好先检查指针是否为空,以避免空指针引发的异常或错误

4. 指针运算

4.1 指针+-整数

这部分内容和2.1一样,补充新的例子:

通过将vp指向数组第一个元素的地址(&values[0]),可以使用for循环对整个数组进行遍历。

for循环中的语句使用了指针加法,每次使vp指向下一个数组元素。在数组元素前加上*,可以取得vp所指向的数组元素的值,并将这个值赋为0。通过这样的方式,vp指向了数组中的每一个元素,并将它们都设为了0。注意,在循环中没有定义循环变量,条件检查和迭代都在了循环语句中,这也是一种常见的for循环写法。

需要注意的是,循环中的条件判断是vp < &values[N_VALUES],在指针比较中,指针实际上是对应一个内存地址,比较的是这个地址的大小,它的判断依据是地址升序排列,所以vp指向的地址在比较时要小于&values[N_VALUES],否则就越界了。

4.2 指针-指针

分析下面代码的结果:

 在这段代码中,计算了数组中两个元素的地址差,通过指针相减的方式得到了它们之间的元素个数。由于数组元素在内存中是连续存储的,它们之间的地址差就是它们之间的元素个数。

 指针-指针运算的前提条件:

只有当指针指向同一数组的元素时,才能进行指针相减运算。

4.3 指针的关系运算

 代码简化, 这将代码修改如下:

 

 实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行。

标准规定:

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

5. 指针和数组

指针和数组之间的联系:

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

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

 

6. 二级指针

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

这就是二级指针 。

 

 二级指针的地址存放在三级指针里:

 

 

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]);
	}

	return 0;
}

 下面是int型指针数组使用例子:

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	//指针数组
	int* parr[] = { arr1,arr2,arr3 };
	int i = 0, j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			//printf("%d ", parr[i][j]);
			printf("%d ", *(parr[i] + j));
		}
		printf("\n");
	}
	return 0;
}

 这段代码演示了一个包含指针数组的简单例子。在这个例子中,我们定义了三个整型数组 arr1arr2 和 arr3,然后创建了一个指针数组 parrparr中的每个元素都是一个指向整型数组的指针,即 arr1arr2 或 arr3

在这个例子中,我们可以通过 *(parr[i] + j) 来访问第 i 个指针元素指向的整型数组中的第 j 个元素。

这种方式与使用 parr[i][j] 的效果是一样的,只是表达方式略有不同。这里使用指针和数组下标的方式是指针运算的一种形式,即先使用 parr[i] 获取指向整型数组的指针,然后使用 *(parr[i] + j) 访问指针所指向的数组。

 

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

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

相关文章

苹果浏览器safari打不开网页怎么办?参考方法在这!

案例&#xff1a;为啥苹果自带的浏览器打不开网页&#xff1f;输入名称或者网址&#xff0c;打开的网页都是空白的。 【不知道怎么回事&#xff0c;我在safari浏览器里面输入网址&#xff0c;但是打不开正确的网页&#xff0c;一直打开的是空白网页。】 当您使用苹果自带的浏览…

构件连接器

构件连接器 1.构件连接器是什么 构件之间传递信息的器件称为构件连接器&#xff0c;简称为连接器。connector。 构件连接器就是在构件之间进行信息传递的通道&#xff0c;可以通过该通道实现信息由一个构件的端口传递 给另一个构件的端口或者是接口。 2.常见的连接器关系 委…

Python-PyEcharts绘制柱状图

更多优秀文章&#xff0c;请关注个人微信公众号&#xff1a; 程序猿小杨 Python-PyEcharts绘制柱状图 一、简介 核心创建流程&#xff1a; 1.通过Bar()构建一个柱状图对象 2.和折线图一样&#xff0c;通过add xaxis()和add_yaxis()添加x和y轴数据 3.通过柱状图对象的&#x…

Talk预告 | 香港中文大学博士生徐英豪:从不规则的单目图片数据构建3D生成模型

本期为TechBeat人工智能社区第498期线上Talk&#xff01; 北京时间5月18日(周四)20:00&#xff0c;香港中文大学博士生 — 徐英豪的Talk将准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “从不规则的单目图片数据构建3D生成模型”&#xff0c;届时将介绍…

ipa怎么装到苹果手机

下面介绍ipa怎么装到苹果手机&#xff1f; 方法/步骤 进入手机上的设置&#xff0c;如下图所示&#xff1a; 在设置页面中&#xff0c;点击进入通用&#xff0c;如下图所示&#xff1a; 进入通用页面后&#xff0c;点击页面上的描述文件&#xff0c;如下图所示&#xff1a; …

软件测试基础知识整理(五)- 软件开发模型、测试过程模型

目录 一、软件开发模型 1.1 瀑布模型 1.1.1 特点 1.1.2 优缺点 1.2 快速原型模型&#xff08;了解&#xff09; 1.2.1 特点 1.2.2 优缺点 1.3 螺旋模型&#xff08;了解&#xff09; 1.3.1 特点 1.3.2 优缺点 二、测试过程模型 2.1 V模型&#xff08;重点&#xff…

chatgpt赋能Python-python3_6_4怎么用

Python3.6.4简介 Python3.6.4是Python编程语言的一个版本&#xff0c;于2017年12月19日发布。这个版本是Python3系列的一个重要更新版本&#xff0c;包含许多新的特性、改进以及Bug修复。 Python3.6.4有一个众所周知的特点&#xff1a;它是一个不断发展的语言&#xff0c;因此…

springcloud再次学习

对应版本关系&#xff0c;如果不对应可能会报错 在启动类配置&#xff0c;也可以在配置类配置 远程调用使用RestTemplate Eureka配置 步骤 Docker部署Eureka 先创建一个文件将打好的jar包文件去&#xff0c;再写一个Dockerfile文件 负载均衡 &#xff08;LoadBalanced&#xf…

【密码产品篇】动态口令系统密钥体系结构(SM3、SM4)

【密码产品篇】动态口令系统密钥体系结构&#xff08;SM3、SM4&#xff09; 动态口令是一种一次性口令机制&#xff0c;用户无须记忆口令&#xff0c;也无须手工更改口令。口令通过用户持有的客户端器件生成&#xff0c;并基于一定的算法与服务端形成同步&#xff0c;从而作为…

HDMI视频标准

一、常见的显示接口 常见的显示接口有AV、VGA、DVI、HDMI。 AV接口与显示器有3个接口&#xff0c;分别为音频接口、左声道接口、右声道接口。线束太多&#xff0c;被淘汰。 VGA显示接口由于个头较大&#xff0c;不能传输音频&#xff0c;逐渐被淘汰。 DVI不能传送音频也被淘汰&…

公司已有springboot项目引入swagger

公司已有springboot项目引入swagger 1、swagger介绍 官网&#xff1a;https://swagger.io/ Swagger 是一个用于生成、描述和调用 RESTful 接口的 Web 服务。通俗的来讲&#xff0c;Swagger 就是将项目中所有&#xff08;想要暴露的&#xff09;接口展现在页面上&#xff0c;…

iOS版ChatGPT突然上线!Plus用户笑疯了!

大家好&#xff0c;我是五竹。 今天&#xff0c;ChatGPT官方在苹果的应用商店毫无征兆的上线了自己的App——ChatGPT的 iOS版正式上线&#xff0c;Android读者们留下了羡慕的眼泪。 仅仅睡个觉的时间就冲到了苹果商店免费榜第二名 商店里有很多三方的ChatGPTAPP&#xff0c;为…

Su+ELK实现网络监测(3)——实际应用配置

SuELK实现网络监测&#xff08;3&#xff09;——实际应用配置 Suricata一、启动项二、规则使用三、解析eve.json文件四、主要目录及文件位置 ElasticSearch一、启动项二、主要目录及文件位置 Logstash一、启动项二、配置项三、主要目录及文件位置 kibana一、启动项二、可视化三…

公司没有一个会自动化测试的,果断离职了····

面试问到离职原因&#xff0c;我想这是很多面试者的痛&#xff0c;包括我自己&#xff0c;曾经也被离职原因所坑过。 面试回答离职原因简直特么就是巨坑&#xff01; 话说最近我面试了个两三年经验的测试工程师&#xff0c;离职原因说出来就是砸自己的脚&#xff0c;真是感慨…

中间件(二)- Tomcat

中间件&#xff08;二&#xff09;- Tomcat 1. 什么是Tomcat&#xff1f;2. 安装tomcat(linux)2.1 下载2.2 安装2.3 配置环境变量并启动2.4 验证tomcat是否安装成功 1. 什么是Tomcat&#xff1f; Tomcat是一个开源、免费、轻量级的Web服务器。 Tomcat是Apache 软件基金会&…

XSS-labs-level1详解

访问题目url 我们分析一下代码层面&#xff1a; 接受名为“name”的GET参数并在页面上显示它。其中包含了一个对alert()的覆盖。当alert()被调用时&#xff0c;它将显示一个确认框提示&#xff0c;然后将页面重定向到“level2.php?keywordtest”的URL&#xff0c;这将把关键字…

Win10和Win11上设置VS(Visual Studio)默认以管理员权限权限启动设置方法

本次管理员权限启动 如果只需要当前启动为管理员权限&#xff0c;方法如下&#xff1a; 1、使用“开始”菜单 2、根据所使用的 Windows操作系统 版本&#xff0c;执行以下步骤之一&#xff1a; 在 Windows 10 中&#xff0c;打开“开始”菜单&#xff0c;然后滚动到 Visual S…

Mysql——SQL语言入门

1.创建数据库表 【1】创建数据库表t_student &#xff08;1&#xff09;创建数据库&#xff1a; &#xff08;2&#xff09;新建查询 &#xff08;3&#xff09;创建数据库表 ##单行注释 /* 多行注释 多行注释 *//* 建立一张用来存储学生信息的表 字段包含学号、姓名、性别、…

chatgpt赋能Python-python30_3__2

Python30-3**2 – 强大的Python解释器的介绍 Python30-32是一个开源的Python解释器&#xff0c;其名称表示30乘以3的平方&#xff0c;即2700&#xff0c;意味着它是Python 3的改进版本。Python30-32由一群志愿者开发&#xff0c;旨在提供一个高效、强大、易于使用的编程工具&a…

【ChatGPT】无需注册,无需科学上网,无需人工验证的速度超快的 ChatGPT

文章目录 一、ChatGPT介绍二、使用ChatGPT时经常遇到的一些问题三、一个让你呼吸顺畅的 ChatGPT 一、ChatGPT介绍 ChatGPT&#xff0c;全称聊天生成预训练转换器&#xff08;英语&#xff1a;Chat Generative Pre-trained Transformer&#xff09;&#xff0c;是OpenAI开发的人…