浅析 C 指针及相关运算 近日发现自己对指针的掌握不如之前熟练于是进行了简单复习不过并未涉及到与数组联系起来的指针。ShowYoung 2026/7/5指针指针类型是 C 语言的一种基本类型。C 语言中的不同类型如下基本类型包括算术类型、指针类型。组合类型包括结构体类型、数组类型。除此之外还有一些其他类型比如联合类型、函数类型、void 类型等等。指针根据其指向的地址的数据类型的不同主要可以分为对象指针(指向变量)以及函数指针(指向函数)还有void 指针。对象指针根据其指向的变量的不同类型也可以进行细分比如指向 int 类型就是 int *指向 char 类型就是 char *等等。⭐指针变量的大小与指向什么类型的数据无关所占位数固定为CPU 寻址能力的位数。比如在 32 位 CPU 上指针类型就是 32 位大小也就是占 4 个字节而对 64 位 CPU 来说则是占 8 字节。既然一个指针变量所占内存的大小不会改变那么为什么还要指定一个类型呢为一个指针指定类型主要是为了应对编译器的类型检查编译器在编译过程中会根据指针指向的数据类型对程序进行语义检查看程序有没有错误。复杂指针声明的分析法则左右法则首先从最里面的圆括号未定义标识符看起先往右看再往左看每当遇到圆括号时就应该掉转阅读方向。一旦解析完圆括号里所有的东西就跳出圆括号。重复这个过程直到整个声明解析完毕。以int *((*f)(int))[10]为例分析如下首先从最里面的圆括号看起f是一个指针整个指针表达式因此也就定了性。这条语句声明的是一个指针。然后往右看是一个参数列表说明该指针的类型是一个函数指针。再向左看是一个符号说明该指针指向的函数的返回值是一个指针。此时括号里的东西解析完毕跳出圆括号继续重复这个过程。往右看是一个数组再往左看是int*与int *(*p)[10]类似。p相当于定义了一个数组指针该指针指向的数组的元素类型为int *即指向一个指针数组。我们把以上分析综合就可以得出最后的分析结果这个复杂的指针声明相当于定义一个函数指针该指针指向一个函数这个函数的类型形参为int返回值是一个指向指针数组的指针指针数组中的元素类型为int *。⭐ 指针类型与运算前面提到过指针也有类型主要划分依据就是其指向的地址所存放的内容。如果指向 int那就是 int * 指针指向 void那就是 void * 指针指向函数那就是函数指针。同时指针又是一个变量。既然是变量那就能做运算指针的运算又是如何进行的指针运算⭐指针运算的基础单位是该指针变量指向的数据类型所占大小。对一个指针变量来说能进行的运算仅有以下几种类型加减运算可以是与整数进行加减也能与其他指针变量进行减运算(不可加)也可以自增自减比较运算结果是一个 bool 值赋值运算比如 p1 p2加减运算指针变量作加减运算允许的种类有以下几种指针与整数进行加减、自增自减这个运算就是在指针指向的地址的基础上进行左右偏移。需要注意的是偏移的基础单位是该指针变量指向的数据类型所占大小。比如对一个 int *p 来说p 会向高地址偏移 4 个字节也就是一个 int 类型的大小 p 10 则会偏移 40 个字节也就是 10 个 int 类型的大小。而对与一个大小为 12 字节的结构体数据指针 struct data *p 来说p 会偏移 12 个字节p 10 则会偏移 120 个字节。[!IMPORTANT]对一个数组指针来说也是一样的道理每次偏移的基础单位是该指针指向的数组的大小。比如对一个整型数组 int *a[5] 来说a 就是指向这个数组的指针也就是数组指针。此时 a1就会偏移 4*5 20 个字节也就是一个该类型数组所占字节大小。指针与同类型指针作减法运算这里有两个前提一同类型指针二减法运算原因如下第一如果两个指针不是同类型指针那他们各自的基础单元大小就不同而指针变量不存在自动类型转换所以自然无法进行运算 / 比较。第二两个指针作加法运算没有任何意义所以 C 语言不允许这种情况发生。但是两个指针变量作减法运算则可以求出这两个指针变量之间的地址偏移是一种很有用处的运算。像前面提到的这里运算的基础单元仍旧是指针指向的数据类型的大小所以有如下现象有一整形数组 int a[10]运算 ( a[2] - a[0] ) 的结果并不是 8而是 2。因为对 a[2] 和 a[0]他们的运算时的基础单元是 int 类型所以结果是 2 个基础单元而非 8 字节。如果想得到 a[2] 元素与 a[0] 元素的绝对地址偏移那就要对它们进行强制类型转换转换为指向大小为 1 字节的 char 类型的指针如下​ ( (char *)(a[2]) - (char *)(a[0]) )如此一来运算的基础单元就是 char 类型的大小单位就从 int 变为 char结果就是绝对偏移。前面提到这种运算很有用处具体就是可以用来实现自己的sizeof关键字。示例代码如下#defineMY_SIZE_OF_VAR(var)((char*)(var1)-(char*)(var))不过这个方法只能实现对已经分配好内存的变量的大小的计算。如果想要计算数据类型的大小而非具体变量则实例代码如下#defineMY_SIZE_OF_TYPE(type)((type*)01)上述代码是将绝对地址 0x00000000 转换为 type * 类型的指针之后向上偏移一个基础单元返回绝对地址。由于是从 0x00000000 开始进行偏移的所以返回的地址就是该数据类型所占字节大小。比较运算比较运算也只能发生在同类型的指针变量之间主要是用来判断哪个变量的地址更大可以用来辅助求两个指针变量的地址偏移示例如下size_tcal_shift_posi(int*a,int*b){if(ab)return((char*)a-(char*)b);elsereturn((char*)b-(char*)a);}赋值运算这个没什么好讲的就是给指针变量赋值示例如下/* 初始化就赋值 */ int a 10; int *p a; /* 后续赋值 */ int a 10; int *p; p a; /* 赋同类型指针变量的地址 */ int a 10; int *p1 a, *p2; p2 p1; /* 赋绝对地址很少这样使用一般仅用于定义寄存器的地址 */ int *p 0x08000000;void * 指针void * 指针可以指向任意数据类型任意类型指针可以直接赋值给 void * 指针不需要强制类型转换。正是由于这种特性使得 void * 指针成为了 C 语言中的通用指针。需要注意的是虽然任意类型指针可以直接赋值给 void * 指针不需要强制类型转换但是将 void * 指针赋值给其他类型指针的时候还是需要进行强制类型转换的。任意类型的指针转换为 void *再转换为原来的类型时都不会发生数据丢失值也不会发生改变。主要用途void * 指针主要用途如下作为函数的参数使用表示函数的参数可以是任意指针类型。作为函数的返回值类型使用以便进行其他操作。比如 malloc 的返回值就是 void *因此在将malloc()函数返回的地址赋值给一个指针变量时一般要做强制类型转换。void * 作为一种指针类型除了修饰函数原型一般不参与具体的指针运算。我们不能使用间接访问运算符*访问void * 不能对void * 做下标运算。