← 文章归档

重新认识C指针

文章目录 4 节

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

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

认识纯粹的指针

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

#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:

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个字节的数值,更具体一点的说,这个数值,其内涵是一个地址。

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

int main(){
    int *a;
    a = 10;
    a = 100000000;
    int b;
    a = &b;
    return 0;
}

认识引用(&)

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

int *a;
int b;
// 配合scanf
scanf("%d",&b);
// 配合指针使用
a = &b;

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

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

#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

output:

a = 1

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

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

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

#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:

*p = 0x12345678
*q = 0x78

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

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

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

指针的声明

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

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

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

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