C语言学习:16、C语言指针

2023-09-21 16:11:02

指针是C语言的精髓,很多人都觉得指针难学,是因为内心对指针有所恐惧,把自己吓退了。一种应用语言能有多难,只要了解了指针的本质,学习起来就超级简单。

一、什么是指针

1.1、指针就是一种变量,一种特殊的变量,就这么简单。

这个特殊体现在,指针中保存的值是内存中的地址。前面了解过int型变量,char型变量,float型变量,它们保存的是对应类型的数据,int型变量保存的是int型的数据,char型变量保存的是char型的数据,float型变量保存的是float型的数据,而指针这种变量保存的是内存的地址。

1.2、这里引入了内存,那什么是内存,什么是内存地址?

内存是计算机里面的存储部件,就是常说的内存条,最小的存储单元就是一个字节

内存中每个存储单元都有一个编号,这个编号就是内存地址;

比如32位的系统,能范围的地址范围是0x00000000--0xFFFFFFFF,这个0x00000000和0xFFFFFFFF就是一个存储单元的编号,也就是内存地址。这个编号就是一个整型数。

同理64位操作系统的地址范围就是0x0000000000000000--0xFFFFFFFFFFFFFFFF。

我们还应知道,程序在运行的时候,程序里面所有的东西都在内存中,所以我们可以通过内存地址来访问程序中的所有元素。

我们知道:

一个char型变量占1个字节

一个int型变量占4个字节

......

我们既可以通过变量名来访问变量,也可以通过内存地址来访问变量。

1.3、如何获取程序元素的地址

程序元素可以是变量、数组、函数

使用&操作符可以获取程序元素的地址,获得的是元素的起始地址

内存地址本质上是一个无符号的整型,32位系统,内存地址占4个字节;64位系统,内存地址占8个字节。

程序实例1:获取变量的地址

#include <stdio.h>

int main()
{
	int var = 18;

	printf("var的值是%d\n",var);
	printf("var的地址是%p\n", &var); //打印指针类型用%p,取地址用&操作符

    return 0;
}

输出结果:本人电脑使用的是64位操作系统,输出的地址就是64位的

var的值是18
var的地址是0000008146D3FCC4

 通过内存地址可以访问变量,前面有说过,我们获取变量的地址得到的是变量在内存中的起始地址,要想获得一个变量的值,还需要知道变量在内存中的长度,就是占几个内存单元,或者说是占几个字节,比如int型变量就是占4个字节,连续的4个字节。

所以C语言是通过变量的类型来确定变量的长度,用变量的起始地址确定变量的位置。

1.4、指针如何定义

指针的定义语法:

//指针定义语法:type * pointer

 程序实例2:指针的定义

#include <stdio.h>

int main()
{
	char*	pChar;		//定义一个char类型指针
	int*	pInt;		//定义一个int类型指针
	float*	pFloat;		//定义一个float类型指针
	double* pDouble;	//定义一个double类型指针

	printf("pChar的地址是%p\n", &pChar); //打印指针类型用%p,取地址用&操作符
	printf("pInt的地址是%p\n", &pInt); //打印指针类型用%p,取地址用&操作符
	printf("pFloat的地址是%p\n", &pFloat); //打印指针类型用%p,取地址用&操作符
	printf("pDouble的地址是%p\n", &pDouble); //打印指针类型用%p,取地址用&操作符

	printf("pChar占用的空间是%d个字节\n", sizeof(char*)); //char*指针类型占的内存大小
	printf("pInt占用的空间是%d个字节\n", sizeof(int*));
	printf("pFloat占用的空间是%d个字节\n", sizeof(float*));
	printf("pDouble占用的空间是%d个字节\n", sizeof(double*));

	return 0;
}

 输出结果:

pChar的地址是000000F60E6FF5D8
pInt的地址是000000F60E6FF5F8
pFloat的地址是000000F60E6FF618
pDouble的地址是000000F60E6FF638
pChar占用的空间是8个字节
pInt占用的空间是8个字节
pFloat占用的空间是8个字节
pDouble占用的空间是8个字节

1.5、指针内存访问

通过指针访问操作符*来访问内存数据,指针类型占据的内存大小在系统里面是固定的,32位系统的地址就是4个字节,64位系统的地址就是8个字节,前面程序实例2的输出结果就能说明问题。

程序实例3:

#include <stdio.h>

int main()
{
	int var = 0; //定义一个var变量,并初始化为0
	int* pVar = NULL; //定义一个int* 类型的pVar变量,并初始化为0地址

	printf("var = %d\n", var); //打印的值就是0
	printf("var的地址是 %p\n", &var); //打印的值就是0
	printf("pVar = %p\n", pVar); //打印的地址就是0x0000000000000000

	pVar = &var;//把整型变量var的地址赋值给pVar
	*pVar = 18; //经pVar变量所存储的地址所指向的变量赋值18

	printf("var = %d\n", var);
	printf("pVar = %p\n", pVar);

	return 0;
}

输出结果:

var = 0
var的地址是 000000963A2FFA14
pVar = 0000000000000000
var = 18
pVar = 000000963A2FFA14

二、指针与地址的关系

C语言指针的规范:

1、Type*类型的指针只能保存Type类型变量的地址:

        如,int*类型的指针只能保存int类型变量的地址;

        float*类型的指针只能保存float类型变量的地址;

2、不同类型的指针不能相互赋值:

        如。int*类型的指针不能与float*类型的指针相互赋值;

3、不能将普通数值当做地址给指针赋值

        指针保存的地址,必须是系统能分配的内存地址,不能瞎写个数据。

程序实例4:错误代码实例

#include <stdio.h>

int main()
{
	int i = 10;
	float f = 10;

	int* pi = &f; //不同类型指针赋值,会有警告
	float* pf = &f; //正常

	printf("pi = %p, pf = %p\n", pi, pf); //能打印出同样的地址
	printf("*pi = %d, *pf = %f\n", *pi, *pf); //输出不同的值

	return 0;
}

前面我们知道,通过调用函数不能直接交换两个变量的值,用指针就可以轻松实现了。

想要实现函数交换变量的值,就必须要实现在函数的内部修改函数外部的变量。

程序示例5:通过指针实现在函数内部改变函数外部的变量

#include <stdio.h>

void change(int* p)
{
	*p = 18;  //把地址p中的变量赋值为18,实现函数内修改外部变量的值
}

int main()
{
	int var = 0;

	change(&var);
	printf("var = %d\n", var); //打印出18

	return 0;
}

程序实例6:交换两个变量的值

void swap(int* a, int* b)
{
	int t = 0;

	t = *a;
	*a = *b;
	*b = t;
}

int main()
{
	int x = 10;
	int y = 8;

	printf("交换前:x = %d, y = %d\n", x, y);

	swap(&x,&y);
	printf("交换后:x = %d, y = %d\n", x, y);

	return 0;
}

 

三、指针与数组的关系

数组的本质是一片连续的内存,那数组的地址是什么?如何获取?

数组的地址,就是那一片连续地址的起始地址,用取地址符&来获取;

数组名可看作一个指针,代表数组中第0个元素的地址;

当指针指向数组元素时,可进行指针运算

程序实例7:获取数组的地址和数组中元素的地址

#include <stdio.h>

int main()
{
	int arr[] = {1, 2, 3, 4,5};
	
	
	printf("数组名代表arr的地址是%p\n", arr); //数组名代表数组的地址
	printf("数组arr的地址是%p\n", &arr); //打印数组的地址
	printf("数组arr首个元素的地址%p\n", &arr[0]); //打印数组中首个元素的地址

	return 0;
}

输出结果:

数组名代表arr的地址是000000DFC37CF8C8
数组arr的地址是000000DFC37CF8C8
数组arr首个元素的地址000000DFC37CF8C8

&arr与arr在数值上是相同的,但是表示的意义不一样

&arr表示数组arr的地址,类型是int(*)[5];

arr代表数组arr中0号元素的地址,类型是int*

程序实例8:指针运算

#include <stdio.h>

int main()
{
	int arr[] = {1, 2, 3, 4, 5};

	int* p = arr; //p指向数组arr

	printf("*p = %d\n", *p); //打印1
	printf("arr[0] = %d\n", arr[0]); //打印1

	p = p + 1; //指针偏移

	printf("*p = %d\n", *p); //打印2
	printf("arr[1] = %d\n", arr[1]); //打印2

	*p = 100; 
	printf("*p = %d\n", *p); //打印100
	printf("arr[1] = %d\n", arr[1]); //打印100

	
	return 0;
}

指针与数组的等效用法

a[i] == *(a + i)

四、指针与函数的关系

五、指针与堆空间的关系

六、指针经典问题分析

更多推荐

TDengine 与煤矿智能 AI 视频管理系统实现兼容性互认

煤矿行业是一个充满危险和复杂性的领域,具备产业规模大、分布地域广、安全性要求高等特点,为了实现智能化预警、预测等目的,煤矿企业纷纷采用现代化的技术来提高安全性、生产效率和管理水平。煤矿智能AI视频管理系统可以助力企业更好地进行矿工工作环境监测、异常情况报警等工作,从而提高安全性并减少事故风险,在煤矿项目中已经得到了广泛

做接口测试如何上次文件

【软件测试面试突击班】如何逼自己一周刷完软件测试八股文教程,刷完面试就稳了,你也可以当高薪软件测试工程师(自动化测试)在日常工作中,经常有上传文件功能的测试场景,因此,本文介绍两种主流编写上传文件接口测试脚本的方法。首先,要知道文件上传的一般原理:客户端根据文件路径读取文件内容,将文件内容转换成二进制文件流的格式传输给

PASCAL VOC2012数据集详细介绍

PASCALVOC2012数据集详细介绍0、数据集介绍2、PascalVOC数据集目标类别3、数据集下载与目录结构4、目标检测任务5、语义分割任务6、实例分割任务7、类别索引与名称对应关系0、数据集介绍2、PascalVOC数据集目标类别在PascalVOC数据集中主要包含20个目标类别,下图展示了所有类别的名称以及所

关于String、StringBuffer、StringBuilder

1.String可以被继承吗?String类由final修饰,所以不能被继承。扩展阅读在Java中,String类被设计为不可变类,主要表现在它保存字符串的成员变量是final的。Java9之前字符串采用char[]数组来保存字符,即privatefinalchar[]value;Java9做了改进,采用byte[]数

【人工智能】企业如何使用 AI与人工智能的定义、研究价值、发展阶段的深刻讨论

前言人工智能(ArtificialIntelligence),英文缩写为AI。它是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。人工智能是新一轮科技革命和产业变革的重要驱动力量。📕作者简介:热爱跑步的恒川,致力于C/C++、Java、Python等多编程语言,热爱跑步,喜爱音乐

Maven 直接依赖、间接依赖、依赖冲突、依赖仲裁

文章目录直接依赖和间接依赖依赖冲突Maven的依赖仲裁最短路径优先先声明优先手动解决依赖冲突直接依赖和间接依赖在项目中直接引入的依赖叫做直接依赖,而那些被动引入的就叫间接依赖比如上图中,A是我们的项目,我们在项目中直接引入了B模块,所以B和A的关系就是直接依赖,而B工程内部引入了C,所以B和C也是直接依赖关系,如果B工

Python编程指南:利用HTTP和HTTPS适配器实现智能路由

嗨,爬虫大佬们!今天我要为大家分享一篇关于如何利用HTTP和HTTPS适配器来实现智能路由的Python编程指南。在现代互联网应用中,路由功能起着至关重要的作用,而利用Python编程语言实现智能路由则可以为我们的应用带来更高的灵活性和性能优化。接下来,让我们一起深入了解这个令人激动的主题吧!1、了解HTTP和HTTP

简单易上手,亚马逊云科技Amazon CodeWhisperer个性化辅助功能成为开发者好帮手

AmazonCodeWhisperer介绍AmazonCodeWhisperer是亚马逊云科技出品的一款基于机器学习的通用代码生成器,可实时提供代码建议。类似Cursor和GithubCopilot编码工具。在编写代码时,它会自动根据您现有的代码和注释生成建议。从单行代码建议到完整的函数,它可为您提供各种大小和范围的个

关于React Hooks的面试题及其答案

请解释一下ReactHooks是什么,以及它的优点和缺点是什么?Hooks是React16.8版本引入的一种新特性,它允许你在不写class的情况下操作state和其他React特性。Hooks是一种特殊的函数,可以让你“钩入”React的特性。它的优点是让编写组件更简单方便,同时可以自定义hook把公共的逻辑提取出来

面试中的技术趋势:如何展示你跟进最新技术的能力

🌷🍁博主猫头虎(🐅🐾)带您GotoNewWorld✨🍁🦄博客首页——🐅🐾猫头虎的博客🎐🐳《面试题大全专栏》🦕文章图文并茂🦖生动形象🐅简单易学!欢迎大家来踩踩~🌺🌊《IDEA开发秘籍专栏》🐾学会IDEA常用操作,工作效率翻倍~💐🌊《100天精通Golang(基础入门篇)》🐅学会Gol

小程序测试基础知识分享,获取专业测试报告就找卓码软件测评

近年来,随着互联网的快速发展,小程序测试成为了一个重要的环节。而小程序测试的内容以及注意事项则会直接影响到产品的质量和用户体验。卓码软件测评作为专业的软件测试公司,在软件测试方面有着丰富的经验。下面将从多个角度来详细描述小程序测试的内容和注意事项,并分析测试对产品的作用。一、小程序测试的内容1、功能测试:各项功能的稳定

热文推荐