指针是C语言中的难点,但其本质和其他类型并无不同,下面我们从几个方面来重新认识指针。

在这个过程中,我需要你先抛去对指针类型的认识,先忘掉指针的类型,认识纯粹的指针,然后再重新认识带着类型的指针。

认识纯粹的指针

我们先抛开指针的类型,看看指针是什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<stdio.h>

struct Node{
int a;
int b;
int c;
int d;
};

int main(){
int *a;
unsigned int *b;
double *c;
struct Node * d;
printf("int pointer = %d bytes\n",sizeof(a));
printf("unsigned int pointer = %d bytes\n",sizeof(b));
printf("double pointer = %d bytes\n",sizeof(c));
printf("struct pointer = %d bytes\n",sizeof(d));
return 0;
}

output:

1
2
3
4
int pointer = 8 bytes
unsigned int pointer = 8 bytes
double pointer = 8 bytes
struct pointer = 8 bytes

可以看到不管什么类型的指针的都是占用8个字节(这个结果在64位机器上都是一致的),这种结果是容易理解的,我们知道,指针存储的是一个地址,所以不管什么类型的指针都必须要能够表示这台机器上所有的地址,所以对于32位的机器,指针需要4个字节,同理,如果是64位机器,指针就是8个字节。

64位机器中的64表示该机器有着64位的地址空间

为什么要讲上述这些内容呢,因为上述内容表达了一个重要信息,所谓指针,就是一个8个字节的数值,更具体一点的说,这个数值,其内涵是一个地址

对于指针的使用来说,既然其本质是一个值,也就意味着我们可以对它任意的赋值,至少在语法上我们不需要受到任何限制。

1
2
3
4
5
6
7
8
int main(){
int *a;
a = 10;
a = 100000000;
int b;
a = &b;
return 0;
}

认识引用(&)

对于引用的本质其实相当的简单,我们起初接触&时可能是在下面两种情景中

1
2
3
4
5
6
int *a;
int b;
// 配合scanf
scanf("%d",&b);
// 配合指针使用
a = &b;

上面我们提到了指针其实就是一个8字节的值,那么引用一个变量其实也是一个8字节的值,也就是&b与1,2,3...这样的数值并无不同

那么我们可以进一步认识到,scanf中第二个参数接受的是一个变量的地址,这种用法并不与&绑定,我们可以看下面的代码

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>

int main(){
long long a;
long long b;
b = &a;
scanf("%lld",b);
printf("a = %lld\n",a);
return 0;
}

input:

1
1

output:

1
a = 1

通过上面的代码,不知道你是否可以认识到指针和引用其实就是一个值这一本质

指针的类型与解引用(*)

上面的内容都试图让你忘记指针的类型,理解指针就是一个值这个概念,那么指针的类型对于指针来说到底是什么呢,我们先来看一个简单的例子

1
2
3
4
5
6
7
8
9
10
11
#include<stdio.h>

int main(){
int a = 0x12345678;
int *p = &a;
char *q = &a;
printf("*p = 0x%x\n",*p);
printf("*q = 0x%x\n",*q);
return 0;
}

看着下面的图,猜猜上面的输出是什么

无标题-2023-09-19-2047

output:

1
2
*p = 0x12345678
*q = 0x78

看到这里,应该意识到了一点,指针的类型决定了指针在解引用获取多少字节的数据,因为char类型占用一个字节,所以解引用时指针会向上看一个字节,同理,int类型占用四个字节,所以解引用时会向上看四个字节

那么现在我们可以先总结一下,指针可以分为两个部分看:

  1. 其本身是一个8字节的值,表示一个地址
  2. 指针的类型表示指针在解引用时获取多少字节的数据

指针的声明

如果理解了上面的内容,那么你对指针已经足够清楚了,但是实际上对于C初学者而言,指针最难的部分不在于理解它本身,而是在解读指针的声明,上这个部分与指针关系并不大,但是往往令人费解

比如我们早期遇到的指针数组和数组指针,不知道你现在看到是否可以分清楚,是有技巧还是靠记忆

1
2
int *p[10];
int (*p)[10];

关于这一块的内容,详见我的另一博客,我就不赘述了: C语言声明