C语言指针详解

1.内存和地址

        在内存中,一个内存单元可以放8位数据,也就是8-bit,我们可以为内存单元进行编号,也就是我们所说的地址,在C语言中,我们可以认为 地址 == 指针,指针里存放的就是地址。

        在32位的电脑中,有32根地址总线,每根线只有两态,表⽰0,1【电脉冲有⽆】,那么用二进制来进行表示就可以得到 2^32,也就是我们可以为内存编写的最大地址编号为2^32。64位同理可知。

2.指针变量

        & 取地址操作符

               在我们写代码的是时候,如果我们想要知道某个元素的地址,可以使用下图的方式

                来取出整型a的地址;

int a = 0;
// &表示取出a的地址
int *p = &a; //*p 表示p是指针变量 int表示指针指向地址的内容是int类型
printf("%p", p); //打印a的地址

                我们打印出来看下a的地址(这是以16进制表示的地址)

        * 解引用操作符

                如果我们已经知道一个元素的地址了,那该怎么看这个地址中存放的是什么呢?                

int a = 0;
int* p = &a;
printf("%d", *p); //在p前面加 * 对p进行解引用得到指向地址的内容

        打印出来看下,的确就是a的值。

        指针变量的大小

                在windows vs2022中有符号整型变量的的大小是4-byte 一个有符号整型变量所占的内存空间是32-bit,表示大小最大位 2 ^31 -1 (因为最高位用来表示正负不参与计数)。

                以x86环境为例 有32根地址总线 可以表示2^32的内存编号 ,最大位2^32 - 1,明显一个指针变量占用了32-bit的空间,那么同样的大小也是4-byte

                在x64环境下 有64根地址总线 大小是8-byte

        指针变量的类型以及void *指针

        我们一般把指针也称为地址,通常指针变量这么表示: 存放的数据的类型 *指针变量名 = 地址

        如图

int a = 0;
// &表示取出a的地址
int *p = &a; //*p 表示p是指针变量 int表示指针指向地址的内容是int类型
char a = 'a';
char *p = &a;

        拿怎么判断指针的数据类型呢,我们想想平时判断一个元素的数据类型,不就是去掉变量名,剩下的就是数据类型了嘛。指针也是一样的!

int a = 0; //去掉 a, 左侧剩下 int
int *p = &a; //去掉p 左侧剩下int *

        那如果我们不知道元素数据类型或者元素类型不确定呢,我们可以使用void * 类型,它不表示任何数据类型的地址,也无法被解引用,但可以用它来存放数据的地址。

char a = 'a';
void *p = &a;

3.指针运算       

        指针数据类型的意义

        不管是什么数据类型的指针,取出来的永远是元素所占第一个字节的地址,而不同的数据类型指针会影响从首元素起向后共操作几个字节。操作几个字节取决于指针变量去掉*后的数据类型所占内存大小。

以下是在解引用操作中的影响:

例如:int* 类型进行解引用的时候 从首地址往后总共操作4字节,最后得到int 的值;

同理:char* 类型进行解引用的时候 从首地址往后总共操作1字节,最后得到char 的值;

        指针 +- 整数

        同上,当指针加减整数的时候 加减几个字节也是取决于指针的数据类型的   

       新指针(新地址) = 指针(地址) + 整数 * 操作字节数     

int a = 0;
int* p = &a;
printf("%p\n", p);
printf("%p\n", p + 1);

 .

        int* 类型的指针依次操作四个字节, 因此当 p + 1(这里的一可以想成是p往后走了1个整型大小)的时候, p往后走了四个字节,也就是p加上了4。

        指针+-指针

        用上面的公式进行简单推导,可以得到以下公式:

        指针 - 指针 = 整数  (整数就是相隔几个指针存放的数据类型)

int a = 0;
int* p1 = &a;
int* p2 = p1 + 1;
printf("%p\n", p1);
printf("%p\n", p2);
printf("%d\n", p2 - p1);
printf("%d\n", p1 - p2);

  

4.野指针

        危害

                会导致程序报错,影响正常运行

        成因

                1.指针创建时没有初始化

int* p;
printf("%p", p);

 

                 运行一下不出意外的报错了。

                2.指针指向的地址被释放

                这大多是因为指针指向的地址指向的数据是局部变量,局部变量被销毁之后,这个地址就被释放了,不在用来存储之前放的那个局部变量了。

        如何避免

        如果在创建指针变量的时候不知道放什么,或者是指针暂时用不到了,可以放入NULL,NULL的意思是空地址,这时就不能对指针进行解引用等读写的行为,等有需要时,给指针存放新的地址,就可以正常使用啦!

5.二级指针

        指针变量也是变量,也有自己的地址。

        而二级指针,顾名思义,就是存放指针变量地址的指针。和之前指针的命名规则一样  

	int a = 0;
	int* p = &a;
	int** pp = &p; //这里的pp就是二级指针

6.数组传参本质

        数组传参实际上传的是数组首元素的地址

int arr[] = { 0, 1, 2 };
int* p = arr; 

 

7.指针数组

        指针数组就是存放指针类型数据的数组 

int a = 0;
int b = 1;
int c = 2;
int* pa = &a;
int* pb = &b;
int* pc = &c;
int* arr[] = { pa, pb, pc };

        

8.函数指针

                首先我们先创建一个函数

void Add(int x, int y)
{
	printf("%d", x + y);
}

                 然后我们再创建一个函数指针

                写法 函数返回类型 (*指针变量名)(参数类型,参数类型) = 函数名(函数名也表示函数首元素地址)

void (*fun_p)(int , int ) = Add;
int x = 1;
int y = 2;
fun_p(x, y);

 于是我们写了以下程序

void (*fun_p)(int , int ) = Add;
int x = 1;
int y = 2;
fun_p(x, y);

这里使用函数指针来调用函数时 * 符号可加可不加,刚刚说了函数名也就是函数地址,我们平时在调用函数的时候也是直接使用函数名调用的,所以直接使用函数指针变量名调用就可以啦!

over!!!!!!!

  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值