第11讲:深入理解指针(1)

1.内存和地址

2.指针变量和地址

3.指针变量类型的意义

4.指针运算

5.野指针

6.指针的使用和传址调用

7.const修饰指针

8.assert断言

1.内存和地址

1.1内存是什么?

酒店一般分为多个房间,内存就像一个大酒店,内存的“房间”叫内存单元,一个内存单元的大小是一个字节。

1.2什么是指针?

指针就是地址,是内存单元的地址。酒店的房间有房间号,内存单元也有“房间号”,内存单元的“房间号”叫指针,指针==地址,是一个意思。

1.3什么是编址?

每个内存单元都有“房间号”(地址),但是,这个房间号不会被记录下来。因为不需要记录,就像钢琴的琴键上没写“do re mi fa so la xi”,虽然没写,但是演奏者可以正常演奏!编址也是这个道理!

1.4地址总线。

cpu和内存之间有大量数据交互,地址作为一个数据,在传输时需要依靠地址总线,32位机器有32根地址总线,64位机器有64根,每根线都有两种状态(电脉冲的有无),分别表示0或1,那么32位机器就有2^32种地址。

2.指针变量和地址

2.1什么是指针变量?

指针变量就是一种变量,这个变量是专门用来储存指针的。

复习:什么是变量?

变量是可以变化的量,可以将其理解为一个容器,用来储存各种各样的数据。

2.2取地址操作符(&)

&是单目操作符,用于取出变量的地址。

%p是格式化打印地址

我们看第五行,我们创建了一个变量“a”,当我们创建这个变量时(即使不赋值),都会向内存申请一块连续的空间,那我们就可以打印a的地址。

取出a的地址,再用%p格式化打印到屏幕,可以看到00CFFBD0(16进制),而这个16进制数就是变量a的地址。

提问:变量a是int型的,应占4个字节,这4个字节对应4个指针,那么,我们打印出来的是哪一个地址?

答:最小的那个!

2.3什么是指针类型?

 指针类型是一种类型,指针类型也有多种,比如int*(整数指针类型),float*(实型指针类型)。

我们看第六行,pa是一个指针,*表示pa是一个指针,int表示pa指针指向int型,int*表示pa是整数类型的指针。

2.4解引用操作符(*)

作用,通过地址找到地址里存放的东西。

我们从第六行开始看,pa是一个指向整型的指针变量,取出a的地址,将其放入pa这个指针变量中。

再看第7行,用解引用操作符(*)找到pa里存放的东西(变量a),并将其赋值为1,那么变量a里装的就是1了。

看第8行,没什么好说的。

主要看第九行,将pa接引用,找到里面装的变量a,将其打印,值为1.

2.5指针变量的大小是多少?

取决于环境,在32位环境下,地址要用32个二进制数表示,每8位占一个字节,共战4个字节。同理,在64位环境下占8字节。

3.指针变量类型的意义

还是这个图,我用它来卖个关子:第六行*前的int看起来没什么用,为什么要加个int。

3.1指针的解引用

科普:1.%x是用16进制格式化打印。

           2.(char*)是类型强转,将a的地址取出,强转类型,并放入char*类型的pc中。

           3.一个16进制位占4个2进制位,0X11223344正好占满4个字节。

           4.一般情况下,一个数字在内存中是倒着存的,例如0x11223344,44被存在最小的地址                 中,而11在最大的地址中。

看图,pa的地址和pc的地址是相同的,但是两个变量的类型不同,所以解引用后访问的字节数不同,int*型的指针变量解引用后能访问4个字节,所以能正常打印。

但是,char*类型的指针变量解引用后只能访问一个字节,也就是访问最小的字节,所以只能打印44,可见指针变量所指向的类型不同,访问的权限不同。所以,地址指向的类型要写明白!

3.2指针+-整数

科普:一般情况下,数组名是数组首元素的地址,所以arr就是一个指针,指向第一个元素。

显然打印的第一个值是1。

我们知道数组内的元素的地址在内存上是连续的,所以只要给第一个元素的地址加上4(int型占4个字节,所以每个元素的地址相差4,我在数组那一章讲了),但是把,我们看看第7行,我们访问第二个元素,应该给arr加上4才对啊,为什么是加1呢?

因为arr所指向的元素是int型,+1会跳过一个int型(也就是加上了4个字节)。

第八行,同理,访问第4个元素只需加三,就能跳过12个字节!

所以,地址指向的类型要写明白!地址指向不同的类型,加1所跳过的字节数是不同的。

3.3 void* 指针

void*指针是无具体类型的指针,void*型指针变量能容纳任何类型的指针。

a的地址不经过强转,是不能放入其他类型的指针变量的。

但是,void*型指针变量除外。

void*不能直接进行指针运算,例如,解引用是不可以的,具体怎么用,后续的进阶会讲。

4.指针运算

4.1指针-指针

BC-C8=C 转成10进制是12.

结果不应该是12吗?为什么哦?

我们从两个角度分析

角度1:地址相减计算的是两地址间元素的个数。

角度2:&arr【3】是第4个元素的地址,等价于arr+3(每+1跳过一个整型)。

5.野指针

5.1什么是野指针?

指向的位置不明确的指针叫野指针。

5.2为什么会出现野指针?

1.指针位初始化。

2.指针越界访问。

指针没有第三个元素,arr+2后就越界了。

3.指针指向的空间被释放。

test函数结束后,变量a的空间被回收,n的地址已经不能访问了,此时n的地址就成了野指针。

5.3如何规避野指针?

方法1:初始化指针。

方法2:小心指针越界。

方法3:指针变量不再使用是,给该变量赋值NULL。

给该变量赋值NULL,该变量包含的是空指针,不要和void*混淆。

方法4,不要在函数内返回局部变量的地址。

6.指针的使用和传址调用

题目:输入两个值,用函数交换两个值的位置。、

交换失败,该函数交换的是形参的位置,形参只是实参的一份临时拷贝,交换形参不会影响实参。

当我们将实参的地址传上去,在对地址解引用,找到实参,就可以实现实参的交换了。

题目:strlen的模拟实现。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int my_strlen(char* p)
{
	int count = 0;
	while (*p++ != '\0')
		count++;
	return count;
}
int main()
{
	char arr[] = "qwertyuiop";
	int a = my_strlen(arr);
	printf("%d", a);
	return 0;  
}

至此,指针的基础已经讲完了!!!

朋友们,进阶开始!!!

7.const修饰指针

const的作用是为变量做一些限制,使变量不能被修改。

const也可以写在int的左边。

但是啊,我们可以绕一下,取出a的地址,解引用找到变量a,再修改。


显然,这是在胡闹,在打破语法规则,换成c++直接给你报错,所以,我们应该给变量的地址也进行const修饰,把路封死!

在*左边const是对变量a(int*p1)修饰,在右边是对a的指针修饰。

好了,现在就安全一些了。

8.assert断言

8.1:库函数assert被assert.h头文件所包含。

8.2用法:assert(语句);

语句不成立,就会报错,一般是用来验证一个指针是不是空指针,若为空,assert可以使程序报错!

  • 31
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

INUYACHA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值