【C语言】指针的入门篇,深入理解指针和指针变量

欢迎来sobercq的博客喔,本期系列为【C语言】指针的入门篇,深入理解指针和指针变量

图文讲解指针的知识,带大家理解指针和内存的关系,以及指针的用法,感谢观看,支持的可以给个赞哇。

目录

一、内存和地址

二、变量和地址 

 三、指针变量

四、指针变量的用法(一)

五、指针变量的大小

五、指针变量的用法(二)

六、const关键字

七、指针变量的用法(3)  

八、assert断言

补充:计算机数据存储常见的单位

一、内存和地址

内存,电脑组成的一部分,在这一部分中,我们的cpu在运转的时候从内存中读取数据。那内存的大小又从1G-16G不等等,那它是如何管理的呢?

电脑简易组成: 

 我们把内存划分为一个个的空间大小只有1字节的内存单元,为了找到内存单元,每个内存单元都会有一个内存编号,这样的内存编号在计算机中如果用十六进制表示就如图所示0x00000001……,内存编号我们就把它称作地址。在C语言里,地址就是指针

内存管理:

二、变量和地址 

了解完内存管理,那么变量的本质其实就是向内存申请空间,空间的大小由数据类型来确定。例如int a = 5;实际上就是向内存申请了一个空间大小为int(4字节),用来存放数据五。那么每个内存单元都会有相对应的地址。

 

那如何取出对应的a地址呢?我们就需要用到取地址操作符&。&这个操作符取出的地址是较小的地址,也可以说是第一个字节的地址。

#include<stdio.h>
int main()
{
    int a = 5;
    &a;
    printf("%p\n",&a);
    return 0;
}

 三、指针变量

我们通过取地址操作符(&)拿到的地址是⼀个数值,例如上图的:010FFCB4,这个数值有时候也是需要存储起来,那我们把这样的地址值存放在指针变量中。 

#include<stdio.h>
int main()
{
    int a = 5;
    &a;
    int* ptr = &a;

    return 0;
}

如何理解上述代码? 

 prt的类型是int*,a的地址是0x010FFCB4,我们通过取地址操作符把a的地址拿出来放到p的空间里。

四、指针变量的用法(一)

我们将地址保存起来,那如何去使用地址呢?

我们用*来对指针变量解引用,获得p存放的地址指向变量的值。我们把*也叫做解引用操作符,或者是间接引用操作符。

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

此时*p就相当于a,通过修改*p,我们能修改a的值。 

 

五、指针变量的大小

关键字sizeof可以用来计算指针变量的大小,来看下面一段代码:

#include<stdio.h>
int main()
{
    int a = 5;
    int *p = &a;
     
    char c = 'w';
    char* pc = &c;
    
    printf("%d ",sizeof(p));
    printf("%d ",sizeof(pc))

}

如果编译器在x86(32位)的环境下,那么此时两个值都为4,即是4个字节,如果是x64(64位),那么二者的值都为8,即八个字节。 

指针变量是为了存放地址,所以其大小跟数据类型无关,32位平台下地址是32个bit位,指针变量大小是4个字节,64位平台下地址是64个bit位,指针变量大小是8个字节,只要指针类型的变量,在相同的平台下,大小都是相同的。

五、指针变量的用法(二)

 1.不同类型的大小

首先是看以下类型,在不同环境下的内存大小

x64:

x86:

2.指针解引用 

 我们把num当中的值用11223344都存入进去:

此时num的地址: 

 

此时p的地址:

请读者思考以下代码

1.p指针变量能否存放下以下地址

2.p指针变量解引用后修改几个字节?

#include<stdio.h>
int main()
{
	int num = 0x11223344;

	char* p = &num;
	*p = 0;

	return 0;
}

 答案:

p能存放下num的地址,都是4字节,但其修改时只修改了一个字节。因此类型的作用便是让p在解引用的时候操作的空间大小,例如char操作一个字节,int操作四个字节。

3.指针的运算

指针加减整数

#include<stdio.h>
int main()
{
	int num = 0x11223344;

	int* pn = &num;
	char* p = &num;
	
	printf("&num = %p\n", &num);
	printf("pn地址 = %p\n", pn);
	printf("pn+1地址 = %p\n", pn + 1);
	printf("p地址 = %p\n",p);
	printf("p+1地址 = %p\n", p + 1);

	return 0;
}

 结果如下所示:

 指针加减整数的距离(空间大小)取决于指针类型,即int走四个字节,char走一个字节。

4.指针访问数组

首先我们来回顾一下数组下标访问

因为这里我们定义的是整形数组,所以我们要是想用指针访问数组的话,就必须使用整型指针,即int*。

#include<stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	//指针访问
	int* p = arr;
	for (i = 0; i < sz; i++)
	{
		printf("%d " , *(p + i));
	}

	return 0;
}

 p+i是我们走的步长,比如我们从开始走,一次访问越过四个字节,如下图所示

我们对p+i进行解引用后,得到就是数组存储的值; 

地址是从低到高变化的。 

六、const关键字

 我们知道变量是可以修改的,如果我们不希望这个值被修改,我们要对它进行限制,那const关键字的用法就是限制变量不被修改

例如以下代码

#include<stdio.h>
int main()
{
	const int i = 6;
	printf("%d ", i);
	i = 4;
	return 0;
}

编译器提示我们,表达式左边必须是可修改的。但是我们现在学习了指针,我们可以绕过const这个门卫。

但是我们现在也不希望任何情况都能修改i的值呢?

接下来我们看const修饰指针,const修饰指针分两种情况,一种是在*左边,一种是在*右边。

 假设我们把const放在*左边

#include<stdio.h>
int main()
{
	int n = 5;
	int m = 50;
	
	const int* p = &n;
	*p = 4;
	p = &m;

	return 0;
}

假设我们把const放在左边,修改*p和p的值,来验证const有什么效果,编译器提示我们

所以const如果放在*的左边,限制的是指针所指向的内容,保证指针指向的内容不能通过指针来改变。 但是指针变量本⾝的内容可变。

同理验证const如果放在*右边, 

所以const如果放在*的右边,限制的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针所指向的内容,可以通过指针改变。 

 如果*两边都有const呢?

那它就会集齐两者,使其变成完全不能修改的p。

七、指针变量的用法(3)  

(1).指针-指针

我们说指针就是地址,所以指针减去指针其实就是地址减去地址,但注意指,针减去指针是有前提的,必须在一段空间内才能进行相减,也就是两个指针必须指向同一段空间。

#include<stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int ret = &arr[9] - &arr[0];

	printf("%d ", ret);
	ret = &arr[0] - &arr[9];

	printf("%d ", ret);
	return 0;
}

我们得到的结果是九和负九

所以指针减去指针,其的绝对值就是元素的个数。

(2).指针的关系运算

 指针的关系运算,就是地址的关系运算,地址相互比较。

我们可以利用关系运算来打印数组

#include<stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	
	int* p = arr;
	while (p < arr + sz)
	{
		printf("%d ", *(p++));
	}

	return 0;
}

结果如下: 

(3).野指针 

 概念:野指针说的是指针指向的位置是未知的,不清楚的,(随机的、不正确的、没有明确限制的)

第一种情况是,指针未初始化,造成野指针

第二种情况是,指针越界访问

第三种情况是,指针指向的空间释放

#include<stdio.h>

int* test()
{
	int n = 5;
	return &n;
}

int main()
{
	//指针未初始化
	int* p1;//野指针
	*p1 = 20;

	//指针越界访问
	int arr[10] = { 0 }; 
	int* p2 = &arr[0]; 
	int i = 0;
	for (i = 0; i <= 11; i++)
	{
		//当指针指向的范围超出数组arr的范围时,p就是野指针
		*(p2++) = i;      
	}

	//指针指向的空间释放
	//局部变量出作用域会销毁,如果此刻返回那么指针就是野指针
	int* p3 = test();
	
	return 0;
}

为了规避野指针必须注意的几个点:

1.用NULL赋值指针,初始化指针

2.在写前思考清楚,避免越界访问

3.当指针变量不再使⽤时,及时置NULL,指针使用之前检查有效性

NULL 是C语言中定义的⼀个标识符常量,值是0,0也是地址,这个地址是无法使用的,读写该地址会报错。

(4).指针的传址调用

最经典的就是交换函数中的两个值了

void Swap(int* n1,int* n2)
{
	int tmp = *n1;
	*n1 = *n2;
	*n2 = tmp;
}

int main()
{
	int a = 5;
	int b = 10;
	Swap(&a, &b);
	printf("%d %d", a, b);
	return 0;
}

 结果如下:

(5).指针加减整数

指针加减整数,也向前文讲述的差不多,因为数组在内存中是连续存储的,所以我们通过知道数组首元素地址,就可以知道后面的数组元素值。指针加减整数,它所走的空间大小就是类型的大小。

在这里说明一下p+i等价于&arr[i], *(p+i)==arr[i],

例:字符数组的打印

#include<stdio.h>
int main()
{
	char arr[] = "abcdef";

	char* pc = arr;
	while (*pc != '\0')
	{
		printf("%c ", *(pc++));
	}

	return 0;
}

八、assert断言

C语言提供了一个宏,在头文件assert.h 中定义了宏   assert(),用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行。这个宏常常被称为“断言”。

assert(p);

assert() 宏接受⼀个表达式作为参数。如果该表达式为真(返回值非0), assert()不会产生任何作用,程序继续运行。如果该表达式为假(返回值为零), assert() 就会报错。我们常常使用它来检查指针是否非0。 

当p为空指针,则这里会提示以下报错。

如果我们想在debug的调试下关闭assert断言,我们需要用下列代码

#define NDEBUG 

补充:计算机数据存储常见的单位

计算机常见的存储单位:bit, Byte, KB, MB, GB, TB, PB,……

换算关系:

感谢各位同伴的支持,本期指针就讲解到这啦,如果你觉得写的不错的话,可以给个赞,若有不足,欢迎各位在评论区讨论。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【使用IDEA总结】01——新增作者信息、方法参数返回值

[TOC](目录) 1.类新增作者信息 打开IDEA的Settings&#xff0c;Editor->Code Style->File and Code Templates->Includes->File Header&#xff0c;输入以下作者信息&#xff0c;作者名更换为自己的即可&#xff0c;操作如下图所示 /*** Author Linhaipeng* Date…

实现表达式语言

实现表达式语言 考虑使用大量Scriplet代码嵌入Java代码的JSP页面。过度使用Scriptlet代码使JSP页面变得混乱。因此。开发人员难以阅读和调试页面。另外,网页设计师在编辑表示代码时也会遇到问题。为了解决此类问题,开发无脚本的JSP页面受到推崇。 无脚本的代码使JSP页面易于…

Uipath 实现Excel 文件合并

场景描述 某文件夹下有多个相同结构(标题列相同)的Excel 文件&#xff0c;需实现汇总到一个Excel文件。 常见场景有销售明细汇总&#xff0c;订单汇总等。 解决方案 对于非IT 人员则可使用Uipath 新式Excel活动&#xff0c;通过拖拉实现。也可以通过内存表或使用VB脚本&…

【动态规划初识】不同路径问题

每日一道算法题之不同路径问题 一、题目描述二、思路三、C++代码一、题目描述 题目来源:LeetCode 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finis…

CTFshow web(php文件上传155-158)

web155 老样子&#xff0c;还是那个后端检测。 知识点&#xff1a; auto_append_file 是 PHP 配置选项之一&#xff0c;在 PHP 脚本执行结束后自动追加执行指定的文件。 当 auto_append_file 配置被设置为一个文件路径时&#xff0c;PHP 将在执行完脚本文件的所有代码后&…

opencv通道分离与合并

void QuickDemo::channels_demo(Mat & image) {std::vector<Mat>mv;//通道分离合并split(image,mv);//原图 指针(Mat)imshow("蓝色", mv[0]);imshow("绿色", mv[1]);imshow("红色", mv[2]); } split(image,mv);//原图 指针(Mat) 这里…

华为OD机试 - 分配土地( Python C C++ JavaGo JS PHP)

题目描述 从前有个村庄&#xff0c;村民们在各种田地上插上小旗子&#xff0c;每个旗子上都标识了一个数字。现在&#xff0c;村民们想要找出一个包含相同数字的最小矩形区域&#xff0c;并将这块土地分配给对村庄做出巨大贡献的村民。我们需要找出这个矩形区域的最大面积。 …

网络原理(3)--以太网协议,DNS

&#x1f495;"Echo"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;网络原理(3)–以太网协议,DNS 在网络原理(2)中介绍了网络层中的一个重要的协议–ip协议,网络层关注的通信时的起点和终点,而数据链路层更加"底层"一些,关注的是传输过程…

嵌入式软件设计入门:从零开始学习嵌入式软件设计

&#xff08;本文为简单介绍&#xff0c;个人观点仅供参考&#xff09; 首先,让我们了解一下嵌入式软件的定义。嵌入式软件是指运行在嵌入式系统中的特定用途软件,它通常被用来控制硬件设备、处理实时数据和实现特定功能。与桌面应用程序相比,嵌入式软件需要具备更高的实时性、…

第13讲创建图文投票

创建图文投票实现 图文投票和文字投票基本一样&#xff0c;就是在投票选项里面&#xff0c;多了一个选项图片&#xff1b;、 <view class"option_item" v-for"(item,index) in options" :key"item.id"><view class"option_input&…

MATLAB知识点:randsample函数(★★★☆☆)生成随机样本的函数,可指定有放回和无放回随机抽样

讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 节选自第3章&#xff1a;课后习题讲解中拓展的函数 在讲解第三…

机器学习:Pooling层作用及反向传播

CNN网络在反向传播中需要逐层向前求梯度&#xff0c;然而pooling层没有可学习的参数&#xff0c;那它是如何进行反向传播的呢&#xff1f;此外&#xff0c;CNN中为什么要加pooling层&#xff0c;它的作用是什么&#xff1f; Pooling层 CNN一般采用average pooling或max pooli…

【STM32 CubeMX】GPIO_HAL库源码分析

文章目录 前言一、GPIO_HAL库源码分析1.1 初始化GPIO1.2 HAL_GPIO_Init源码分析GPIO_InitTypeDef初始化结构体HAL_GPIO_Init函数 总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 例如&#xff1a;随着人工智能的不断发展&#xff0c;机器学习这门技…

判断一个时间序列中的元素是否属于一个月的第一天或最后一天

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 判断一个时间序列中的元素 是否属于一个月的第一天或最后一天 Series.dt.is_month_start Series.dt.is_month_end [太阳]选择题 以下代码的输出结果中正确的是? import pandas as pd ts pd.S…

【JavaEE】传输层网络协议

传输层网络协议 1. UDP协议 1.1 特点 面向数据报&#xff08;DatagramSocket&#xff09;数据报大小限制为64k全双工不可靠传输有接收缓冲区&#xff0c;无发送缓冲区 UDP的特点&#xff0c;我理解起来就是工人组成的**“人工传送带”**&#xff1a; 面向数据报&#xff08;…

Javaweb之SpringBootWeb案例之propagation属性案例演示的详细解析

案例 接下来我们就通过一个案例来演示下事务传播行为propagation属性的使用。 需求&#xff1a;解散部门时需要记录操作日志 由于解散部门是一个非常重要而且非常危险的操作&#xff0c;所以在业务当中要求每一次执行解散部门的操作都需要留下痕迹&#xff0c;就是要记录操作…

FL Studio2024最新中文版有哪些其新功能特点?

除了之前提到的特点外&#xff0c;FL Studio 21还有以下一些值得注意的特点&#xff1a; 高效的音频处理&#xff1a;FL Studio 21具备高效的音频处理能力&#xff0c;能够实时处理多轨道音频&#xff0c;提供低延迟的音频播放和录制&#xff0c;确保音乐制作过程中的流畅性和实…

【数据库_MySQL】MySQL彻底卸载

程序员为什么不喜欢关电脑&#xff1f; 你是否注意到&#xff0c;程序员们似乎从不关电脑&#xff1f;别以为他们是电脑上瘾&#xff0c;实则是有他们自己的原因&#xff01;让我们一起揭秘背后的原因&#xff0c;看看程序员们真正的“英雄”本色&#xff01; 卸载 要是你的…

4核16G服务器价格腾讯云PK阿里云

4核16G服务器租用优惠价格26元1个月&#xff0c;腾讯云轻量4核16G12M服务器32元1个月、96元3个月、156元6个月、312元一年&#xff0c;阿腾云atengyun.com分享4核16服务器租用费用价格表&#xff0c;阿里云和腾讯云详细配置报价和性能参数表&#xff1a; 腾讯云4核16G服务器价…

修改npm 的运行命令详解

在Node.js和npm中&#xff0c;你可以通过修改package.json文件中的scripts部分来定义和运行自定义的npm脚本。这些脚本可以是任何你希望在项目中运行的命令&#xff0c;包括启动服务器、运行测试、构建项目等。下面是一些修改npm运行命令的详解和代码示例。 修改npm运行命令的…