指针类型:解引用与运算的关键
指针变量的大小与类型无关,在同一平台下所有指针变量的大小通常相同(例如在32位系统中为4字节,64位系统中为8字节)。那么为什么还需要各种各样的指针类型呢?实际上,指针类型具有重要的特殊意义,主要体现在以下几个方面:
目录
一、指针的解引用
代码1:
代码2:
调试观察可以发现:
结论:
二、指针的算术运算
三、void* 指针
示例1:类型不兼容警告
示例2:void 的使用*
VS编译代码的结果:
*void 的主要用途**:
四、指针运算
1、指针 ± 整数
1、指针运算原理
2、整型指针(int*)示例
3、字符指针(char*)示例
4、核心规律
5、记忆技巧
2、指针 - 指针
3、指针的关系运算
一、指针的解引用
对比下面两段代码,观察调试时内存的变化:
代码1:
#include <stdio.h>
int main()
{int n = 0x11223344;int *pi = &n; *pi = 0; return 0;
}
代码2:
#include <stdio.h>
int main()
{int n = 0x11223344;char *pc = (char *)&n;*pc = 0;return 0;
}
调试观察可以发现:
-
代码1会将n的4个字节全部改为0
-
代码2仅将n的第一个字节改为0
结论:
指针的类型决定了指针解引用时能操作的内存大小(权限)。例如:
-
char*
指针解引用只能访问1个字节 -
int*
指针解引用能访问4个字节(在32位系统中)
二、指针的算术运算
观察以下代码中指针加减整数时的地址变化:
#include <stdio.h>
int main()
{int n = 10;char *pc = (char*)&n;int *pi = &n;printf("%p\n", &n);printf("%p\n", pc);printf("%p\n", pc+1);printf("%p\n", pi);printf("%p\n", pi+1);return 0;
}
代码运行的结果如下:
运行结果分析:
-
char*
类型指针+1跳过1个字节 -
int*
类型指针+1跳过4个字节(假设int为4字节)
结论:指针的类型决定了指针加减整数时的步进大小。指针+1实际上是跳过1个指向类型的元素大小。
三、void* 指针
void*
是一种特殊的指针类型,称为"无类型指针"或"泛型指针",可以接收任意类型的地址。但有其局限性:
-
优点:可以接收任何类型指针的赋值而无需强制类型转换
-
限制:
-
不能直接进行指针算术运算(加减整数)
-
不能直接解引用
-
示例1:类型不兼容警告
#include <stdio.h>
int main()
{int a = 10;int* pa = &a;char* pc = &a; // 编译器警告:类型不兼容return 0;
}
在上面的代码中,我们将一个int
类型变量的地址赋给了char*
指针变量。由于类型不兼容,编译器会发出警告(如下图所示)。而使用void*
作为中间类型可以避免这个问题。
示例2:void 的使用*
#include <stdio.h>
int main()
{int a = 10;void* pa = &a; // 正确void* pc = &a; // 正确// *pa = 10; // 错误:void* 不能直接解引用// *pc = 0; // 错误:void* 不能直接解引用return 0;
}
VS编译代码的结果:
*void 的主要用途**:
-
常用于函数参数,实现泛型编程
-
用于处理未知类型的数据
-
在内存操作函数(如memcpy、memset)中使用
四、指针运算
指针支持三种基本运算:没有指针加指针,因为这个操作没有意义,跟日期加日期一个性质!!!
1、指针 ± 整数
利用数组在内存中的连续存储特性,通过指针算术运算遍历数组:
#include <stdio.h>
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};int *p = &arr[0];int sz = sizeof(arr)/sizeof(arr[0]);for(int i=0; i<sz; i++){printf("%d ", *(p+i)); // p+i 是指针+整数运算}return 0;
}
以下是关于指针运算核心知识点的整理:
1、指针运算原理
- 加减整数n时,地址偏移量取决于数据类型大小
- 计算公式:新地址 = 原地址 ± (n × sizeof(数据类型))
2、整型指针(int*)示例
- 声明:int* pa
- pa + 1:地址增加4字节(32/64位系统)
- pa + n:地址增加(4 × n)字节
- 典型步长:4字节
3、字符指针(char*)示例
- 声明:char* pc
- pc + 1:地址增加1字节
- pc + n:地址增加n字节
- 固定步长:1字节
4、核心规律
- 运算步长由指针声明类型决定
- 相同偏移量在不同类型指针中产生不同地址变化
- 通用公式:地址偏移量 = n × sizeof(基类型)
5、记忆技巧
- 本质是地址的移动操作
- sizeof(类型)决定指针的单步步长
- 该特性确保数组元素的正确遍历
2、指针 - 指针
计算两个指针之间的距离(元素个数),常用于字符串长度计算等场景:
#include <stdio.h>
int my_strlen(const char* s)
{const char* p = s;while (*p != '\0')p++;return p - s; // 指针相减得到字符个数
}int main()
{printf("%d\n", my_strlen("abc")); // 输出3return 0;
}
注意:指针相减要求两个指针指向同一内存区域(如同一个数组)。
3、指针的关系运算
比较指针的大小(内存地址高低),常用于数组遍历:
#include <stdio.h>
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};int *p = &arr[0];int sz = sizeof(arr)/sizeof(arr[0]);while(p < arr + sz) // 指针比较{printf("%d ", *p);p++;}return 0;
}
注意:指针关系运算也要求指针指向同一内存区域才有意义。