斗帝理解C语言----指针(1)

目录

前言

正文

1.内存与地址

2.指针变量与地址 

2.1取地址操作符(&)

2.2指针变量

2.3如何拆解指针变量类型

2.4解引⽤操作符  

2.5指针变量的⼤⼩

2.6指针变量类型的意义

2.7void* 指针

2.8野指针

2.9传值调⽤和传址调⽤


前言

本座身为斗帝,参悟过无数的天阶斗技。今日所学的C语言,吾认为最重要的即为对于指针的理解。只有将指针这一内容参悟理解,才能真正深入理解C语言的核心。

正文

1.内存与地址

在理解指针前,我们要先了解内存与地址:

假设有⼀栋宿舍楼,把你放在楼⾥,楼上有100个房间,但是房间没有编号,你的⼀个朋友来找你玩, 如果想找到你,就得挨个房⼦去找,这样效率很低,但是我们如果根据楼层和楼层的房间的情况,给每个房间编上号,如:
⼀楼: 101 102 103. ..
⼆楼: 201 202 203. ..
...
有了房间号,如果你的朋友得到房间号,就可以快速的找房间,找到你。
⽣活中,每个房间有了房间号,就能提⾼效率,能快速的找到房间。

如果把上⾯的例⼦对照到计算机中,⼜是怎么样呢? 我们知道计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中,那我们买电脑的时候,电脑上内存是 8GB/16GB/32GB 等,那这些内存空间如何⾼效的管理呢?

其实也是把内存划分为⼀个个的内存单元,每个内存单元的大小取一个字节。其中,每个内存单元,相当于⼀个学⽣宿舍,⼀个字节空间⾥⾯能放8个⽐特位,就好⽐同学们住的⼋⼈间,每个⼈是⼀个⽐特位。

一个内存单元==1字节==8比特位

每个内存单元也都有⼀个编号(这个编号就相当于宿舍房间的⻔牌号),有了这个内存单元的编 号,CPU就可以快速找到⼀个内存空间。生活中我们将门牌号称为地址,在计算机中我们也将内存单元的编号称为地址。而在C语言中赋予了这个地址一个新的名称----指针

所以我们可以理解为:内存单元的编号==地址==指针 

2.指针变量与地址 

2.1取地址操作符(&)

理解了内存和地址的关系,我们再回到C语⾔,在C语⾔中创建变量其实就是向内存申请空间,
用于存放这个变量的数据。

如:

int a=1;

 这个代码就是创建了一个整形变量a,用于存放整数1。而这个整形变量a需要向内存申请4个字节的地址,其中每个字节都有地址。

那么,如何获得这些字节的地址呢?

这⾥就得学习⼀个操作符(&)----取地址操作符

&a就可以获得a所占4个字节中地址较⼩的字节的地址。

虽然整型变量占⽤4个字节,我们只要知道了第⼀个字节地址,顺藤摸⽠访问到4个字节的数据也是可⾏的。

2.2指针变量

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

这里的变量p就是一个指针变量,用于存放整形变量a的地址。

指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址。

2.3如何拆解指针变量类型

我们看到变量p的类型是 int* ,我们该如何理解指针的类型呢?
int a=1;
int*p=&a;
这⾥p左边写的是 int* * 是在声明p是指针变量,⽽前⾯的 int 是在说明变量p指向的是整型(int)类型的对象
这样我们就可以理解了:*是声明这个变量是指针变量,而*前面的类型指的是这个指针变量所指向
的地址对应的变量类型
举一反三,那么我们如何创建一个char类型的指针变量呢?
同理:
char a='x';
char*p=&a;

2.4解引⽤操作符  

我们将地址保存起来,未来是要使⽤的,那怎么使⽤呢?
在现实⽣活中,我们使⽤地址要找到⼀个房间,在房间⾥可以拿去或者存放物品。 C语⾔中其实也是⼀样的,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针)指向的对象,这⾥必须学习⼀个操作符叫解引⽤操作符(*)
int a=1;
int*p=&a;
*p=0;
上面的代码就使用了解应用操作符,*p意思 就是通过p中存放的地址,找到指向的空间, *p其实就是a变量了;所以*p = 0,这个操作符是把a改成了0。
*p==a。

2.5指针变量的⼤⼩

指针变量做为一个变量也是有大小的,那么一个指针变量的大小是多少呢?

在32位的环境下一个地址的大小为32个比特位,指针变量是⽤来存放地址的,那么指针变量也得能存放这32个比特位的信息,所以32位环境下一个指针变量的大小为32个比特位,即4个字节。而在64位的环境下,一个地址的大小为64个比特位,所以一个指针变量的大小为64个比特位,即8个字节

注意:指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。

2.6指针变量类型的意义

指针变量的⼤⼩和类型⽆关,只要是指针变量,在同⼀个平台下,⼤⼩都是⼀样的,为什么还要有各种各样的指针类型呢?
其实指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)。⽐如: char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节。
此外,指针的类型还决定指针+-整数的结果, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过4个字节。
指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。

2.7void* 指针

在指针类型中有⼀种特殊的类型是 void * 类型的,可以理解为⽆具体类型的指针(或者叫泛型指
针),这种类型的指针可以⽤来接受任意类型地址。但是也有局限性, void* 类型的指针不能直接进 ⾏指针的+-整数和解引⽤的运算。
那么 void* 类型的指针到底有什么⽤呢?
⼀般 void* 类型的指针是使⽤在函数参数的部分,⽤来接收不同类型数据的地址,这样的设计可以
实现泛型编程的效果。使得⼀个函数来处理多种类型的数据。

2.8野指针

概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
野指针成因:
1. 指针未初始化
2.指针越界访问
3.指针指向的空间释放
这些操作都会导致指针变成野指针。
如何规避野指针
1. 指针初始化
2.⼩⼼指针越界
3. 指针变量不再使⽤时,及时置NULL,指针使⽤之前检查有效性
4.避免返回局部变量的地址

当指针变量指向⼀块区域的时候,我们可以通过指针访问该区域,后期不再使⽤这个指针访问空间的时候,我们可以把该指针置为NULL。因为约定俗成的⼀个规则就是:只要是NULL指针就不去访问, 同时使⽤指针之前可以判断指针是否为NULL。

我们可以把野指针想象成野狗,野狗放任不管是⾮常危险的,所以我们可以找⼀棵树把野狗拴起来, 就相对安全了,给指针变量及时赋值为NULL,其实就类似把野狗栓起来,就是把野指针暂时管理起来。

不过野狗即使拴起来我们也要绕着⾛,不能去挑逗野狗,有点危险;对于指针也是,在使⽤之前,我们也要判断是否为NULL,看看是不是被拴起来起来的野狗,如果是不能直接使⽤,如果不是我们再去使⽤。

2.9传值调⽤和传址调⽤

学习指针的⽬的是使⽤指针解决问题,那什么问题,⾮指针不可呢?

那就是在函数中使用指针。

假设我们要使用一个函数将两个变量的值交换,我们可能会这样写:

#include <stdio.h>

void swap(int a,int b)
{
int x=0;
x=a;
a=b;
b=x;
}

int main()
{
int a=1,b=2;
printf("a=%d,b=%d\n",a,b);
swap(a,b);
printf("a=%d,b=%d\n",a,b);
return 0;
}

我们是不是会认为交换后输出结果是:a=2,b=1

但实际上是:

可以看出这里的变量a与变量b的值并没有交换。

为什么呢?

其实是在函数swap中创建了两个新的形参a和b来接收实参a和b的值,这两个新的形参是在内存中开辟了新的空间,。当函数结束时就会销毁形参a b,不会影响实参的值。

这就是为什么主函数中a b的值没有改变。

这种把变量本⾝直接传递给了函数的函数调用,叫传值调⽤
结论:实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实
参。
那怎么办呢?
我们现在要解决的就是当调⽤Swap函数的时候,Swap函数内部操作的就是main函数中的a和b,直接 将a和b的值交换了。那么就可以使⽤指针了,在main函数中将a和b的地址传递给Swap函数,Swap函数⾥边通过地址间接的操作main函数中的a和b,并达到交换的效果就好了。
#include <stdio.h>

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

int main()
{
int a=1,b=2;
printf("a=%d,b=%d\n",a,b);
swap(&a,&b);
printf("a=%d,b=%d\n",a,b);
return 0;
}

改进的函数的输出结果:

我们可以看到新的swap函数,顺利完成了任务,这⾥调⽤swap函数的时候是将变量的地址传递给了函数,这种函数调⽤⽅式叫:传址调⽤
传址调⽤,可以让函数和主调函数之间建⽴真正的联系,在函数内部可以修改主调函数中的变量;所以未来函数中只是需要主调函数中的变量值来实现计算,就可以采⽤传值调⽤。如果函数内部要修改主调函数中的变量的值,就需要传址调⽤

  • 20
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值