玩转指针(1)

一.内存与地址

①何为指针?

内存分为一个个小的内存单元(1byte),每个内存单元都的编号就是地址。在C语言中,地址也称为指针。

②地址总线的介绍

a.地址总线是用来传递地址信息的。
b.32位电脑上有32根地址总线,每根地址总线都有0和1两种状态,那么1根地址总线可以表示两种含义,两根地址总线可以表示4种含义,32根地址总线可以表示2的32次方种含义。每种含义都代表一个地址。

二、指针变量与地址

①取地址操作符

int main()
{
	/* 变量创建的本质是向内存申请空间。
	   向内存申请了4个字节的空间,用来存放20*/
	int a = 20;
	/* %p是用来打印地址的  */
	printf("%p\n", &a);
	/* &a表示取出a所占的四个字节中较小的地址(低地址)   */ 
	return 0;
}

②得到地址后,该如何存储地址呢?(使用指针变量存储地址)

int main()
{
	int a = 20;
	printf("%p\n", &a);
	/*那么a的地址怎么存储呢?  使用指针变量 */
	int* ch = &a;
	/*取出a所占的四个字节中较小的地址,将它存放到指针变量ch中
	ch的类型是int*
	其中*表示ch是指针变量, int表示ch指向的变量a是int类型
	*/

	char b = 'w';
	char* r = &b;
	/*   char *中的*表示r是指针变量,char表示r指向的变量b是char类型      */
	return 0;
}

③解引用操作符

对指针变量解引用,就可以通过指针变量中存储的地址,找到指针指向的对象
int main()
{
	int a = 20;
	int* pa = &a;
	*pa = 200; /* 通过pa中a的地址,找到a,将a的值改为200   */
	printf("%d\n", a);//200
	return 0;
}

④指针变量的大小

指针变量是用来存储地址的,因此存储地址需要多大的空间,指针变量的大小就是多少。
在32位平台下,含有32根地址总线,每根地址总线含有0和1两种状态,把32根地址总线所组成的二进制序列当做一个地址,则每个地址占4个byte(32bit)。因此在32位平台下,指针变量的大小是4byte。
在64位平台下,含有64根地址总线,每根地址总线都有0和1两种状态,把64根地址总线所组成的二进制序列当做一个地址,则每个地址占8个byte(64bit)。因此在64位平台下,指针变量的大小是8byte。

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

三、指针变量类型的意义

①指针变量的类型决定了对指针解引用的时候,一次能访问几个字节。

int main()
{
	/*0x开头的是16进制数*/
	int a = 0x11223344;
	int* pa = &a;
	/*因为pa是int*类型的指针,因此对pa解引用的时候一次能访问4个字节*/
	*pa = 0;
	/*此时a的值变成了0*/



	int b = 0x11223344;
	char* pb = &b;
	/*因为pb是char*类型的指针,因此对pb解引用的时候,一次只能访问一个字节*/
	*pb = 0;
	/*此时b的值不是0*/
	return 0;
}

②指针变量的类型决定了指针向前或向后走一步走多大距离。

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

void*类型的指针

在这里插入图片描述

由于a是int类型,则&a是int * 类型的地址,当char * 类型的指针接收int * 类型的地址时,编译器会报警告。
为了避免出现这种地址类型不兼容的情况,可以使用void * 类型的指针。 void * 类型的指针可以接受任意类型的地址,但无法对void * 类型的指针进行解引用、指针加减操作。

在这里插入图片描述

⼀般 void* 类型的指针是使⽤在函数参数的部分,用来接收不同类型数据的地址,这样的设计可以实现泛型编程的效果。使得⼀个函数来处理多种类型的数据。

四、const修饰指针

①const修饰变量

被const修饰的变量称为常变量,本质上还是变量,但是变量的值不能被直接修改,具有常量的属性,但可以通过变量的地址,对变量进行修改 。

在这里插入图片描述

思考:为什么n要被const修饰呢?是希望n的值不能被修改,但此时指针变量p拿到n的地址后,间接的将n的值修改了!这不合理的。我们希望p拿到了n的地址也不能修改n的值,那么接下来该怎么做呢?

②const修饰指针变量

const修饰指针变量,可以放在 * 的左边,也可以放在 * 的右边,意义是不⼀样的。
a. const放在 * 左边

在这里插入图片描述

b. const放在 * 右边

在这里插入图片描述

c. const同时放在 * 的左右两边

在这里插入图片描述

五、指针的运算

①指针加减整数

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

②指针减指针

前提:两个指针的类型必须一致,且指向同一块内存空间(比如同一个数组)。
指针相减的绝对值是两个指针之间元素的个数。例如两个int * 的指针相减的绝对值,就是两个指针之间int类型的元素个数;两个char * 的指针相减的绝对值,就是两个指针之间char类型的元素个数。

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

③指针的关系运算

在这里插入图片描述

六、野指针

①野指针的特点?

野指针中存放的地址是随机的(不确定的)。

②野指针成因

a.指针变量作为局部变量时,未初始化。

在这里插入图片描述

b.指针变量越界访问

在这里插入图片描述

c.指针指向的空间释放

在这里插入图片描述

补充:NULL是C语言中定义的标识符常量,它的值为0,0也是地址,只不过这个地址无法访问。(空指针是无法解引用的)

③如何规避野指针

a.指针变量初始化。

当知道指针该指向哪里时,就给它赋值对应的地址;当不知道指针该指向哪里时,就赋值为NULL。
在这里插入图片描述

b.小心指针越界

一个程序向内存申请了哪些空间,通过指针也就只能访问这些空间,若访问了其他的空间,就形成了非法访问。
在这里插入图片描述

程序的返回值不是0,而是除0之外的任何随机值(比如这里的 -1073741819)表示程序崩溃了。
c.当指针不再使用时,及时赋值为NULL
d.使用指针前检查指针是否为空指针

在这里插入图片描述

七、assert的介绍

①何为宏?

#define定义的标识符就是宏,宏是一种预处理指令。宏只是进行单纯的替换,并不参与计算。
#include<stdio.h>
#define m printf
int main()
{
	int a = 1;
	m("%d\n", a);
	return 0;
}

②assert()也属于宏,这个宏常常被称为“断⾔”。使用assert()前要包含头文件<assert.h>

③宏assert( )的作用

assert()接收一个表达式作为参数,如果表达式的结果为真(返回值为非0),则程序正常运行;如果表达式的结果为假,则程序会报错,并给出报错的具体位置。

在这里插入图片描述

④assert的优点

可以开启或关闭assert( )。当确定程序没有问题时,就不需要用assert( )进行检查。此时只需在#include前加上#define NDEBUG,就可以关闭assert( )。

在这里插入图片描述

⑤assert的缺点

因为引入了额外的检查,增加了程序的运⾏时间。

⑥宏assert()一般是在Debug版本中使用,在release版本中选择禁用掉就行。在VS编译器中的release版本中,assert( )直接就是优化掉了,不起作用。这样在debug版本写有利于程序员排查问题,在 Release 版本不影响用户使用时程序的效率。

八、指针的使用与传值调用

①strlen函数的模拟实现

在这里插入图片描述

②函数的传值调用

写一个函数交换两个整型变量的值

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

既然以上方法行不通,该如何交换a,b的值呢?这就需要用到接下来讲的传址调用了。

③函数的传址调用

在这里插入图片描述

④传值调用与传址调用的区别

传址调用是真正的将主函数与被调用的函数产生了关联。将来如果在被调函数中只需要接收主函数中变量的值,就使用传值调用;如果需要在被调函数中修改主函数中变量的值,就采用传址调用。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值