零基础非科班也能掌握的C语言知识14 指针

1.内存与地址

1.1 指针

我们知道计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中,那我们买电脑的时候,电脑上内存是8GB/16GB/32GB等,那这些内存空间如何⾼效的管理呢?
其实也是把内存划分为⼀个个的内存单元,每个内存单元的⼤⼩取1个字节。
计算机常见的单位:

>bit——比特位 1Byte=8bit
Byte——字节 1KB=1024Byte
KB 1MB=1024KB
MB 1GB=1024MB
GB 1TB=1024GB
TB 1PB=1024TB
PB

1.2指针

指针,是C语言中的一个重要概念及其特点,也是掌握C语言比较困难的部分。指针也就是内存地址,指针变量是用来存放内存地址的变量,在同一CPU构架下,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。

那我们如何该如何理解指针呢?
其中,每个内存单元,相当于⼀个学⽣宿舍,⼀个字节空间⾥⾯能放8个⽐特位,就好⽐同学们住的⼋⼈间,每个⼈是⼀个⽐特位。每个内存单元也都有⼀个编号(这个编号就相当于宿舍房间的⻔牌号),有了这个内存单元的编号,CPU就可以快速找到⼀个内存空间。⽣活中我们把⻔牌号也叫地址,在计算机中我们把内存单元的编号也称为地址。C语⾔中给地址起了新的名字叫:指针。
最简单理解的方法:
在这里插入图片描述
可以看到这个机带RAM就是我的内存16GB=161024MB=1610241024KB=161024*1024Byte,有这么多个字节,一个字节有一块地,每块地都有一个唯一的地址,如果有n个字节,那么地址的数就有0~n-1个,换句话说指针就是地址

1.3如何理解编址

在这里插入图片描述
在这里插入图片描述
用我的理解来解释的话就是
在三维空间中,每一块的地址早早的就固定好了只是没使用如某块地如津市西青区宾水西道399号这块地,我们是不是知道它的地址了但是这块地是用来干嘛呢,当然在现在是建了一个大学。而在内存中就是这样的每块地都能知道它的指针,但是用来干嘛呢我们没决定罢了。
在这里插入图片描述

2.指针变量和地址

2.1取地址操作符

理解了内存和地址的关系,我们再回到C语⾔,在C语⾔中创建变量其实就是向内存申请空间,⽐如:
在这里插入图片描述
那我们如何得到a的地址呢?
这里就要用到取地址操作符(&
在这里插入图片描述
值得注意的是在内存中我们默认每一个指针与一个字节的空间去搭配,举例来说就是一个指针我们不去考虑它的类型的情况下默认是与一个字节的空间去搭配,而一个类型是int的指针我们认为它包含四个默认的指针。

2.2指针变量和解引用操作符(*)

2.2.1指针变量

那我们通过取地址操作符(&)拿到的地址是⼀个数值,⽐如:0x006FFD70,这个数值有时候也是需要存储起来,⽅便后期再使⽤的,那我们把这样的地址值存放在哪⾥呢?答案是:指针变量中。
比如:
在这里插入图片描述

2.2.2 如何拆解指针类型

大家可以看到上面指针变量的类型是int*,大家在进行指针这块的练习一定要了解优先级(今天会更新)的问题不然很难去理解后面的指针数组,数组指针,函数指针的内容。
在这里插入图片描述
指针的学习肯定是一个较大的挑战。
在这里插入图片描述
可以看的时候做一下
在这里插入图片描述

2.2.3解引用操作符

在这里插入图片描述

2.3指针变量的大小

在这里插入图片描述

在这里插入图片描述
当然有关指针大小这方面的内容深挖下去肯定会有更多的知识,但现在作者能力浅薄往后如果能理解肯定也会讲解一下

3.指针变量类型的意义

指针变量的⼤⼩和类型⽆关,只要是指针变量,在同⼀个平台下,⼤⼩都是⼀样的,为什么还要有各种各样的指针类型呢?
其实指针类型是有特殊意义的,我们接下来继续学习。

3.1指针的解引用

在这里插入图片描述
规范使用,一定要养成一个严谨的代码风格,这对你的程序帮助是巨大的

3.2指针±整数

在这里插入图片描述
我们看到*前面的int,char这些不就是我前面讲的高度吗(也就是几个字节长度),当然还有其他后面也会提到

3.3 void*指针

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
正如艾伦·佩利所说的在探究难以实现的问题时,简化是唯一的方法。在如此繁多的指针中解决类型问题,难道我们接受一个指针就要用一个相应的类型吗,未免太多繁琐重复,我们都用void来接收再动用void返回不就行了吗?学到后面大家就会发现如此的便利

4.const修饰指针

4.1const修饰变量

在这里插入图片描述
在这里插入图片描述

4.2 const修饰指针变量

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

如何理解const对指针的作用我觉得归根结底还是要回到结合性的内容
const int*p,*右结合性因此const修饰的是(*p),int*const p,此时const修饰的是p,此时解引用操作符结合的是(const p)

5.指针运算

指针的基本运算有三种,分别是:

  • 指针±整数
  • 指针-指针
  • 指针的关系运算

5.1指针±整数

因为数组在内存中是连续存放的,只要知道第⼀个元素的地址,顺藤摸⽠就能找到后⾯的所有元素。
在这里插入图片描述
指针±整数一定要结合类型来看是int就跳过四个字节,是char就跳过一个字节

5.2指针-指针

在这里插入图片描述
指针-指针得到的是元素个数,值得注意的是指针-指针一定是相同的类型,比如对int*p的指针变量只能用同样的指针变量去减

5.3指针的关系运算

在这里插入图片描述
当然指针和指针之间也可以比较大小,因为本身指针也是一串十六进制的数字

6.野指针

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

6.1野指针成因

6.1.1 指针未初始化

在这里插入图片描述
这一点其实放在其他变量也是一样的,可能有时候我们写代码图方便就对相同类型的变量进行统一的声明,如果后面进行赋值了那没问题但如果你没去赋值(实际上如果你后面赋值程序编译器依然会认为你的指针变量可能指向NULL),它指向的就是一个随机值只不过没野指针危害大不至于让程序崩溃,因此不管后面是否重新对指针变量赋值一定要初始化指针变量

6.1.2指针越界访问

在这里插入图片描述

6.1.3指针的空间释放

在这里插入图片描述
test调用之后return &n(返回了一个指针),但是函数时存放在栈上的当它返回时栈的空间就被回收了因此此时你虽然得到了一个指针,但这指针没有开辟空间。

6.2如何规避野指针

  • 指针初始化
  • 小心指针越界
  • 避免返回局部变量的地址
  • 指针变量不再使⽤时,及时置NULL,指针使⽤之前检查有效性
    在这里插入图片描述

7.assert断言

在这里插入图片描述
在这里插入图片描述
往往程序出错误我们可以通过assert的办法去排除是不是指针的问题,可惜作者学习的不到位现在还不太能理解它的深刻作用

8.传值调用和传址调用

通过这两的区别就能体会到指针的作用
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在函数那块的内容博主已经简单提及过了

9.数组名的理解

在这里插入图片描述

在这里插入图片描述

  • sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩,单位是字节
  • &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素的地址是有区别的)除此之外,任何地⽅使⽤数组名,数组名都表⽰⾸元素的地址。

在这里插入图片描述
在这里插入图片描述

10.使用指针访问数组

在这里插入图片描述
这里简单来讲一下下标访问操作符[]和解引用操作符简单来说就是
arr[i] == *(p+i);大家可以自己替换一下啊效果是一样的

11.一维数字传参的本质

在这里插入图片描述

在这里插入图片描述
所以当我们sizeof(arr),一定要想这里arr是数组名还是一个形参。

12.二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪⾥?
这就是⼆级指针 。
在这里插入图片描述
当然也有三级指针和四级指针。

13.指针数组

在这里插入图片描述

14.指针数组模拟二维数组

在这里插入图片描述
解释一下parr[i][j]
parr[i][j]等于*( *(parr+i)+j)等于(arri+j)

15.字符指针变量

在这里插入图片描述

16.数组指针变量

16.1数组指针变量是什么

之前我们学习了指针数组,指针数组是⼀种数组,数组中存放的是地址(指针)。
数组指针变量也就是指向数组的指针变量
在这里插入图片描述

这块内容还是要回到优先级的内容来,首先[]的优先级优于*同时[]是左结合性的,其次[]是左结合性的
在这里插入图片描述
当然我们就这样代入公式也就是(*p)就是告诉你我这里是指针
在这里插入图片描述

17.二维数组传参的本质

在这里插入图片描述
在这里插入图片描述
也就是可以改成void test(int (*p)[5], int r, int c)

4.函数指针变量

4.1函数指针变量的创建

在这里插入图片描述
在这里插入图片描述

4.2函数指针变量的使用

在这里插入图片描述
可能大家这里会觉得直接用函数名调用函数不就行了吗,就如同电势的概念一样会有一个这样的概念往往都是为了方便我们去解决实际问题。等到后面讲到qsort函数时大家就会理解函数指针的方便

5.函数指针数组

在这里插入图片描述

6.转移表

函数指针数组的⽤途:转移表

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void menu()
{
	printf("**********计算器***************");
	printf("**1.add*******************2.sub**");
	printf("**3.mul*******************4.div**");
	printf("************0.exit***************");
	printf("*********************************");
	printf("*********************************");
}
int add(int x, int y)
{
	return x + y;
}
int sub(int x, int y)
{
	return x - y;
}
int mul(int x, int y)
{
	return x * y;
}
int div(int x, int y)
{
	return x / y;
}
int main()
{
	int(*clac[4])(int x, int y) = { add,sub,mul,div };
	int x, y;
	int input = 1;
	int ret = 0;
	do
	{
		menu();
		printf("请输入\n");
		switch (input)
		{
		case 1:
		{
			scanf("%d %d\n", &x, &y);
			int ret = clac[0](x, y);
			printf("%d\n", ret);
			break;
		}
		case 2:
		{
			scanf("%d %d\n", &x, &y);
			int ret = clac[1](x, y);
			printf("%d\n", ret);
			break;
		}
		case 3:
		{
			scanf("%d %d\n", &x, &y);
			int ret = clac[2](x, y);
			printf("%d\n", ret);
			break;
		}
		case 4:
		{
			scanf("%d %d\n", &x, &y);
			int ret = clac[3](x, y);
			printf("%d\n", ret);
			break;
		}
		case 0:
		{
			break;
		}
		default:
		{
			printf("输入错误,请重新输入\n");
		}
		}
	} while (input);
	return 0;
}
/
int main()
{
	int(*clac[4])(int x, int y) = { add,sub,mul,div };
	int input = 0;
	int ret = 0;
	while(scanf("%d", &input)!=EOF)
	{
		if (input >= 1 && input <= 4)
		{
			scanf("%d%d",&x,&y);
			int ret = clac[input - 1](x, y);
			printf("%d\n", ret);
		}
		else if(input == 0)
		{
			return 0;
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
	}
	return 0;
}

以上就是有关指针的内容了,当然要写学会指针还得学习其他更多方面的内容包括qsort函数的实现等等,当然后面博主会将这些作为练习来来讲解。希望这些东西对你的C语言学习有所帮助
将近万字的博客还希望大家多多点赞关注评论,若有问题还劳烦大家多多指正

  • 40
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值