C语言指针

216 阅读3分钟

概述

学好C语言,必须学好指针,本篇适合指针有个印象但是还没有弄懂弄透的同学,指针比较复杂的原因是因为:

  • 二义性:*操作符有两种意思,申明提取,这样让没经过大量练习的同学在阅读代码的时候需要判断,个人觉得这是C语言设计的缺陷,搞两个操作符不行么?
  • 不直观:指针是变量,但是存放的是地址,拿到值需要提取,无形之中给大脑增加了一层理解记忆负担,简单的代码还好,代码复杂后理解难度增加。
  • 套娃:指针可以一直套,也可以和各种类型组合,大脑不像计算机能放几层堆栈,不经过刻意练习,一般就2层,要想快速反应比较难。

先记住:

  • 一个内存单元的容量一般是一个字节
  • 32位系统存放地址的大小空间就是32位4字节,同理64位8字节
  • 指针的计算是根据指针类型大小偏移的,比如int型指针,偏移就是4个字节
  • []的优先级比*要高

指针类型

指针放的是地址,地址的大小是固定的,4或者8字节,指针类型是用于数据提取的时候,比如int型指针,在提取的时候就向后读4个字节。void*通用指针类型,只能打印指针地址,但是不能提取数据,也不能进行算术运算。

  1. 普通指针:int *p
  2. 二级指针:int **p
  3. 数组指针:int p[]
  4. 方法指针:void (*p)(int,int)

普通指针

int i = 6;
int *p = &i; //养成习惯:*跟着变量 代表他是指针变量。

用一个抽象内存图来理解:

image.png

p  == 0x0004;
&p == 0x0001;
*p == 6;

可以把变量p理解成变量i的地址的别名

二级指针

int* *pp = &p;

image.png

pp == &p;
*pp == &a;
**pp == 6;

三级指针以此类推。

数组指针

数组是一个比较特殊的指针,不用定义*,是常量只能读不能写

int arrayP[5] = {}; // arrayP是数组指针
int (*p)[5] = &arrayP; // p也是数组指针

数组有两坑需要注意

  1. sizeOf(数组名),表示的是整个数组,不是首元素的大小,而是整个数组的大小
  2. &数组名,表示的是整个数组 其余的都只是代表首元素地址。
    看看下面这些打印值你能否知道:
int a[] = {1, 2, 3, 4};
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(a + 0));
printf("%d\n", sizeof(*a));
printf("%d\n", sizeof(a + 1));
printf("%d\n", sizeof(a[1]));
printf("%d\n", sizeof(&a));
printf("%d\n", sizeof(*&a));
printf("%d\n", sizeof(&a + 1));
printf("%d\n", sizeof(&a[0]));
printf("%d\n", sizeof(&a[0] + 1));

指针数组:int *p[]

指针数组容易和数组指针混淆,指针数组是数组,里面的元素是指针

int a = 10;
int b = 20;
int c = 30;
int *p1 = &a;
int *p2 = &b;
int *p3 = &c;
int *ps[3] = {p1, p2, p3};
printf("%p\n", *ps);
printf("%d\n", **ps);

函数指针

函数指针就是函数的地址。一般用作回调

char* myFunction(char* dest,const char* src) {} // 目标函数定义
char* (*mf)(char*,const char*) = myFunction // 函数指针

函数指针数组

int (*parr[4])(int, int) = {};

特殊情况:野指针

1.指针未初始化

int *p; // 野指针
int *p = NULL;//正确的变量初始化

局部指针变量不初始化,就被初始化随机值
2. 指针越界访问,比如数组
3. 指针指向的内存空间释放也就是范围指针
比如方法调用,子方法返回了一个指针,但是指针所指向的局部变量已经释放。