【C】指针详解(一篇文章带你玩转指针)

news2024/9/20 22:01:56

指针详解

  • 指针是什么?
  • 指针和指针类型
    • 指针加减整数
    • 指针的解引用
  • 野指针
    • 野指针的成因
    • 如何规避野指针
  • 指针和数组的关系
    • 数组名是什么?
  • 二级指针
    • 二级指针是什么?
    • 二级指针的运算
  • 字符指针
  • 指针数组和数组指针
    • 指针数组
    • 数组名和&数组名
    • 数组指针
      • 数组指针的使用
  • 函数指针和函数指针数组
    • 函数指针
    • 函数指针数组

很多人学习C语言都在为指针头疼,今天一篇文章带你玩转指针。

指针是什么?

在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。
我们前辈综合考虑,将一个内存单元的大小定义为一个字节。
每一个内存单元都对应一个编号,我们通过这个编号就可以找到这块内存,那么编号是怎么产生的呢?
编号是有电子信号产生的,我们32位机器下就有32根地址线,64位机器下就有64根地址线,我们以32位机器举例,每一根地址线能产生0/1二进制数字,所以32根地址线就有2的32次方种编号,也就是2的32次方个字节的内存,4个GB的内存。而要将这32个比特位存储起来,就需要4个字节的内存,所以在32位机器下,不管什么指针大小都是4个字节。以此类推,那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。
总结一下:

指针是用来存放地址的,地址是唯一标示一块地址空间的。
指针的大小在32位平台是4个字节,在64位平台是8个字节。

指针和指针类型

指针是有类型的,整型数据的地址要放到整型指针中,字符的地址要放到字符指针中。那么都有哪些指针类型呢?
在这里插入图片描述
在这里我们可以看到定义一个指针的方法就是

类型名* 名称

指针的大小在相同的平台下都是相同的,那么指针类型有什么用呢?

指针加减整数

代码演示:

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

运行结果:
在这里插入图片描述
这里的地址都是16进制表示的,我们可以看到pc加一跳过了1个字节,pi加一跳过了4个字节,所以指针类型就决定了指针加减整数跳过的字节数。
总结:

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

指针的解引用

解引用就是通用过指针来访问指向的内容,只需要在指针变量前加*就可以了。
代码演示:

#include <stdio.h>

int main()
{
	 int n = 0x11223344;
	 char *pc = (char *)&n;
	 int *pi = &n;
	 *pc = 0;   
	 *pi = 0;   
	
	 return 0;
}

当执行完*pc = 0后,n变为了
在这里插入图片描述
执行完pi = 0后,n变为了
在这里插入图片描述
我们可以看到不同的指针类型解引用访问的字节数也是不相同的。
总结

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

野指针

概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)指针变量在定义时如果未初始化,其值是随机的,指针变量的值是别的变量的地址,意味着指针指向了一个地址是不确定的变量,此时去解引用就是去访问了一个不确定的地址,所以结果是不可知的。
野指针非常的危险,它指向了不确定的空间,会非法的访问内存。

野指针的成因

1.指针未初始化
2.指针的越界访问
3.指针指向的空间被释放

如何规避野指针

  1. 指针初始化(不确定的先初始化为NULL)
  2. 小心指针越界
  3. 指针指向空间释放即使置NULL
  4. 指针使用之前检查有效性

指针和数组的关系

数组名是什么?

我们看下面代码

#include <stdio.h>
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	printf("%p\n",arr);
	printf("%p\n", &arr[0]);
	return 0;
}

运行结果:
在这里插入图片描述
我们可以看到,数组名就是首元素的地址。那么我们就可以讲数组名放到一个指针中,就可以通过这个指针来访问这个数组。
例如:

#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))// 这里的*(p+i)==p[i]
	 }
	 return 0;
}

二级指针

二级指针是什么?

指针变量也是变量,是变量就有地址,二级指针就是来存放指针变量的地址的。
![在这里插入图片描述](https://img-blog.csdnimg.cn/1c26f7c8930640deab1203d1982ba099.png#pic_center

二级指针的运算

在这里插入图片描述
这里的ppa就是一个二级指针。
*ppa 通过对ppa中的地址进行解引用,这样找到的是 pa , *ppa 其实访问的就是 pa .
**ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a .

字符指针

字符指针是char*,我们一般是用它来存放字符的地址的。但是还有一种用法:

int main()
{
    char* pstr = "hello bit.";//这里是把一个字符串放到pstr指针变量里了吗?
    printf("%s\n", pstr);
    return 0;
}

这里是将这个字符串的首字符的地址放到pstr中去了。
我们看下面的一段代码:

#include <stdio.h>

int main()
{
	char str1[] = "hello world.";
	char str2[] = "hello world.";
	char* str3 = "hello world.";
	char* str4 = "hello world.";
	if (str1 == str2)
		printf("str1 and str2 are same\n");
	else

		printf("str1 and str2 are not same\n");

	if (str3 == str4)
		printf("str3 and str4 are same\n");
	else

		printf("str3 and str4 are not same\n");

	return 0;
}

我们先来分析一下,我们开辟了两个数组,这两个数组中存放的内容是一样的,但是这两个数组的地址肯定不一样,所以第一个肯定是not same,后面的指针变量,指向的都是"hello world."的首元素h的地址,所以他俩肯定一样的,所以第二个肯定是same。我们来看一下结果:
在这里插入图片描述

果然,答案和我们想的一样。

指针数组和数组指针

指针数组

指针数组顾名思义,肯定是一个数组,但是数组里面的每个元素都是指针,例如:

int* arr1[10]; //整形指针的数组

char *arr2[4]; //一级字符指针的数组

char **arr3[5];//二级字符指针的数组

我们这里可以利用指针数组模拟一个二维数组:

#include <stdio.h>

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	int* Arr[] = { arr1,arr2,arr3 };
	return 0;
}

这里的Arr就类似于一个二维数组,但是二维数组的数据是连续存放的,这里的arr1,与arr2是不连续的。

数组名和&数组名

我们先看下面一段代码:

#include <stdio.h>

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

运行结果:

在这里插入图片描述
在这里我们可以看到,数组名和&数组名表示的地址相同,都是首元素的地址,但是数组名加一跳过的是一个该元素类型的大小,而&数组名跳过的是整个数组的大小,这就是他们的区别。

数组指针

数组指针顾名思义就是指针了。它指向的是整个数组,存放的是数组的地址。

int (p2)[10];(p2就是一个数组指针)
[]的优先级要高于
号的,所以必须加上()来保证p先和结合。
p先和
结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个
指针,指向一个数组,叫数组指针。

数组指针的使用

数组指针一般使用与二维数组传参的时候,例如:

#include <stdio.h>

void print(int(*p)[5], int x, int y) //这里的int(*p)[5] 也可以写成int p[][5]
{
	for (int i = 0; i < x; i++)
	{
		for (int j = 0; j < y; j++)
		{
			printf("%d ", p[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { 0 };
	print(arr,3,5);
	return 0;
}

我们这里传参传的是arr,arr是一个二维数组,首元素就是一个int [5]的一个数组,它的地址就需要用数组指针来接收。

函数指针和函数指针数组

函数指针

函数指针也是一个指针,它是指向函数的,函数也是有地址的,函数指针就是来存放函数的地址的。
我们看下面一段代码:

#include <stdio.h>

void test()
{
	 printf("hehe\n");
}

int main()
{
	 printf("%p\n", test);
	 printf("%p\n", &test);
	 return 0;
}

运行结果:
在这里插入图片描述
我们可以看到函数名和&函数名都是该函数的地址。
那么函数指针该怎么表达呢?

void (pfun1) ();
pfun1先和
结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。

我们只要知道了函数的返回类型,以及参数,就可以创建函数指针了。

函数指针数组

要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

int (parr1[10]])();
parr1 先和 [] 结合,说明parr1是数组,数组的内容是什么呢? 是 int (
)() 类型的
函数指针。

函数指针的用途:转移表

今天的分享就到这里了,感谢大家的关注和支持!

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

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

相关文章

开源一款轻量线程池项目

基于Arrow的轻量线程池 大家好&#xff0c;我是光城&#xff0c;最近花了几周业余时间&#xff0c;开发出这款轻量线程池&#xff0c;代码也全部开源啦&#xff0c;欢迎大家star。 本线程池的设计与实现会有涉及非常多的知识&#xff0c;这些内容也都会以视频的方式分享在知识星…

分析气象数据:向Python Cartopy地图中添加循环点

大家好&#xff0c;在使用Python和Cartopy对气候数据进行可视化分析的过程中&#xff0c;有一个叫做循环点&#xff08;cyclic point&#xff09;的术语&#xff0c;它在地理空间栅格数据可视化领域中很重要。 1.循环点的定义和作用 有时&#xff0c;当我们试图绘制地理空间数…

性能测试的基本概念和重要指标

这里写自定义目录标题 一、什么是性能测试二、为什么要学习性能三、性能测试与功能测试的区别1、性能测试和功能测试的区别&#xff1f;2、性能测试是在什么时候做?3、我们在做我们的产品的时候&#xff0c;我们只做功能不做性能可不可以&#xff1f;4、性能测试主要是这几个方…

金融计量学第2课堂-金融时间序列线性模型

量化策略开发&#xff0c;高质量社群&#xff0c;交易思路分享等相关内容 一、相关性和平稳性 1、相关性 &#xff08;1&#xff09;皮尔森相关系数 皮尔森相关系数是最常见、最常用的一个相关系数计算方法。作为衡量两个随机变量x和y线性相关程度的重要指标&#xff0c;在这…

赋能智能智造-RK3568智能主板助力机器人产业高速发展

机器人作为现代制造业的重要一环&#xff0c;正在以惊人的速度推动着生产效率和智能化水平的提升&#xff0c;它们在生产线上的准确操作和高效工作&#xff0c;为企业带来了巨大的竞争优势。关于工业机器人的编程和控制技术&#xff0c;在过去几年中已经有了很多发展和新的应用…

十.Redis持久化

十.Redis持久化 一&#xff1a;RDB持久化二&#xff1a;AOF持久化三&#xff1a;扩展 redis持久化 Redis是内存数据库&#xff0c;如果不将内存中的数据库状态保存到磁盘&#xff0c;那么服务进程退出&#xff0c;服务器中的数据库状态也会消失&#xff0c;所以Redis提供了持久…

《Spring系列》第18章 监听器Listener

前言 陆续阅读了Spring Boot源码中&#xff0c;有不少地方都用到了监听器。每次看到监听器的代码都一头雾水&#xff0c;不懂其中的设计理念&#xff0c;这次决定肝一篇监听器的博文。 一、监听器 1.概述 何为监听器&#xff1f;就是用来监听程序执行的。监听器可以做什么事…

青岛大学_王卓老师【数据结构与算法】Week05_03_队列的定义和特点_学习笔记

本文是个人学习笔记&#xff0c;素材来自青岛大学王卓老师的教学视频。 一方面用于学习记录与分享&#xff0c; 另一方面是想让更多的人看到这么好的《数据结构与算法》的学习视频。 如有侵权&#xff0c;请留言作删文处理。 课程视频链接&#xff1a; 数据结构与算法基础…

Vue3通透教程【十七】Vite构建TS版本Vue项目

文章目录 &#x1f31f; 写在前面&#x1f31f; 创建TS版本的Vue3项目&#x1f31f; 插件安装&#x1f31f; 写在最后 &#x1f31f; 写在前面 专栏介绍&#xff1a; 凉哥作为 Vue 的忠实 粉丝输出过大量的 Vue 文章&#xff0c;应粉丝要求开始更新 Vue3 的相关技术文章&#…

DocFastSearchTool(文档快速搜索工具)开发日志

目录 项目介绍 项目调研背景 项目需求分析 开发环境 项目涉及基础知识点 项目设计 设计基础 项目框架or架构 项目框架的搭建 系统工具模块--遍历目录 SQLite数据库 在Windows上安装SQLite SQLite命令 SQLite-C/C的API 安装SQLite源码 数据库操作的重要接口 连接…

PB从入坑到放弃(三)数据窗口

PB从入坑到放弃&#xff08;三&#xff09;数据窗口 写在前面一、 数据窗口画板1.1 Design 视窗1.1.1 General tab页设置1.1.2 Generation tab页设置1.1.3 Prefixes tab页设置 1.2 Preview 视窗1.2.1 查找数据1.2.2 翻页1.2.3 增加、删除数据 1.3 Control List 视窗1.4 Data 视…

学生成绩分析项目

数据采集 导入必要的库 import pandas as pd import matplotlib.pyplot as plt import seaborn as sns加载数据集 df pd.read_csv(D:\\桌面\\数据\\student_marks.csv)显示数据框的前几行 # 显示数据框的形状 print("Shape of the dataframe:", df.shape)#显示…

pycharm最新版默认菜单栏等工具不见了

原因 pycharm2022.3新版本&#xff0c;默认使用新UI&#xff08;如下图&#xff09;&#xff0c;这让很多小伙伴来说不太适应&#xff0c;我还是习惯旧版本的界面。在网上搜了许多恢复旧版本界面的教程&#xff0c;说的解决方案都是由于“手贱把菜单栏给隐藏了”&#xff0c;这…

C语言“教父“

在中国&#xff0c;"C语言教父"一般指的是C语言网的创始人黄老师。他通过C语言网为广大学习者提供了丰富的学习资源和知识分享平台&#xff0c;包括大量的C语言教程、C语言题目以及相应的文档、资源等等&#xff0c;为C语言的推广、教学做出了巨大的贡献和推动作用

Go自带库的使用说明

Go 中的时间操作 Golang中与时间有关的操作&#xff0c;主要涉及到 time 包&#xff0c;核心数据结构是 time.Time&#xff0c;如下&#xff1a; type Time struct {wall uint64ext int64loc *Location }1、获取时间相关函数 1.1 获取当前时间 // 返回当前时间&#xff0c…

腾讯云TRTC服务实现小程序语音/视屏会议

腾讯云TRTC服务的入门 TRTC 是腾讯云主打全平台互通的多人音视频通话和低延时互动直播解决方案。TRTC服务有多种客户端的支持&#xff0c;对于IOS、Android、React native等都支持的比较好&#xff0c;我们主要在于 IOS、Android、Web三端进行处理&#xff0c;其中 TRTC Web S…

HarmonyOS学习路之开发篇—流转

流转概述 介绍 随着全场景多设备生活方式的不断深入&#xff0c;用户拥有的设备越来越多&#xff0c;每个设备都能在适合的场景下提供良好的体验&#xff0c;例如&#xff1a;手表可以提供及时的信息查看能力&#xff0c;电视可以带来沉浸的观影体验。但是&#xff0c;每个设备…

网络变压器常见封装

1、单口千兆&#xff08;Single Port&#xff09; 封装类型常见型号Dimension SMD24_1 PIN Pitch&#xff1a;1.00 Length&#xff1a;15.1 Width&#xff1a;7.1/10.0 Height&#xff1a;6.00 JWD&#xff1a; SG24002G Group-tek&#xff1a; HST-24015SR SMD24_2 PIN…

pyspark

连接命令&#xff1a; pyspark --master spark://node1:7077 一个application 大任务可以分解成 多个小任务 jobs&#xff0c; 一个job又可以分解成多个 stages 阶段&#xff0c; 一个stage又可以分解成 多个tasks&#xff08;可以认为是两个线程&#xff09; standalone Zo…

使用Gradio库创建交互式滑块组件

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…