1、指针概念
指针(pointer)是一个值为内存地址 的变量(或数据对象)。
1.1、指针的声明
c
int* pi; /* pi是指向int类型变量的指针,注意未经初始化的指针不准使用 */
声明指针的时候必须指定指针指向变量的类型,因为不同的变量类型占用不同的存储空间,此外一些指针操作要求知道操作对象的大小。
声明的意思pi是一个指针,*pi是int类型。
1.2、与指针相关的运算符
地址运算符(&) :后跟一个变量名时,&给出该变量的地址。
间接运算符/解引运算符(*):后跟一个指针名或地址时,8给出储存在指针指向地址上的值。
1.3、指针操作
对指针可以进行以下常见的操作。
- 赋值:可以把地址赋值给指针。但地址应该与指针类型兼容。
- 解引用:*运算符给出指针指向地址上存储的值。
- 取址:&运算符给出的是指针本身的地址。(指针也有自己的地址和值)
- 指针和整数相加:整数和指针都可以做第一个运算对象,指的是整数与指针所指向类型的大小(以字节为单位)相乘,然后把结果与初始地址相加。要注意越界情况,保证指针有效。
- 递增指针:用于指向数组元素,递增指向数组元素的指针可以让该指针移动至数组的下一个元素。
- 指针减去一个整数:指针必须是第一个运算对象,整数是第二个运算对象。该整数将乘以指针指向类型的大小,然后初始地址减去乘积。要注意越界情况,保证指针有效。
- 递减指针,用于指向数据元素,递减指向数组元素的指针可以让该指针移动至数组的上一个元素。
- 指针求差:可以计算两个指针的差值。比如,当两个指针指向同一个数组中不同元素时,可以由此求出两个元素之间的距离。
- 指针比较:当两个指针指向同类型的对象时,可以使用关系运算符比较两个指针的数值。
c
#include <stdio.h>
int main() {
// 初始化数组和指针
int arr[5] = {10, 20, 30, 40, 50};
int* ptr1 = &arr[0]; // 指针ptr1指向arr[0]
int* ptr2 = &arr[3]; // 指针ptr2指向arr[3]
// 1. 赋值操作
int x = 100;
int* ptr3 = &x; // 指针ptr3指向变量x
printf("ptr3的值: %p\n", (void*)ptr3); // 输出ptr3存储的地址(类型转换,跨平台兼容需要)
// 2. 解引用操作
printf("ptr1解引用: %d\n", *ptr1); // 输出: 10
*ptr1 = 100; // 修改ptr1指向的值
printf("修改后ptr1解引用: %d\n", *ptr1); // 输出: 100
// 3. 取址操作
printf("ptr1的地址: %p\n", (void*)&ptr1); // 输出ptr1自身的地址
// 4. 指针和整数相加
int* ptr_add = ptr1 + 2; // 指针后移2个元素
printf("ptr1 + 2: %d\n", *ptr_add); // 输出: 30 (原arr[2])
// 5. 递增指针
ptr1++; // 指针后移1个元素
printf("ptr1递增后: %d\n", *ptr1); // 输出: 20
// 6. 指针减去一个整数
int* ptr_sub = ptr2 - 1; // 指针前移1个元素
printf("ptr2 - 1: %d\n", *ptr_sub); // 输出: 30
// 7. 递减指针
ptr2--; // 指针前移1个元素
printf("ptr2递减后: %d\n", *ptr2); // 输出: 30
// 8. 指针求差
ptr1 = &arr[0]; // 重置ptr1指向arr[0]
ptr2 = &arr[4]; // 重置ptr2指向arr[4]
ptrdiff_t diff = ptr2 - ptr1; // 计算指针差值
printf("ptr2 - ptr1 = %td\n", diff); // 输出: 4 (元素间距)
// 9. 指针比较
printf("ptr1 < ptr2: %d\n", ptr1 < ptr2); // 输出: 1 (真)
return 0;
}
//输出
ptr3的值: 0x7ffee... // 实际地址会不同
ptr1解引用: 10
修改后ptr1解引用: 100
ptr1的地址: 0x7ffee... // 实际地址会不同
ptr1 + 2: 30
ptr1递增后: 20
ptr2 - 1: 30
ptr2递减后: 30
ptr2 - ptr1 = 4
ptr1 < ptr2: 1
2、常见使用场景
2.1、使用指针在函数间通信
使用和*运算符,函数可以访问存储在对应位置的数值,并改变它们,即修改实参。
c
/* 使用指针交换数值 */
void interchange(int* u,int* v){
int temp;
temp = *u;
*u = *v;
*v = temp;
}
2.2、动态内存分配
通过malloc、calloc、realloc分配内存,返回指针。
c
int* arr = (int*)malloc(5 * sizeof(int)); // 分配5个int的空间
if (arr != NULL) {
for (int i = 0; i < 5; i++) {
arr[i] = i + 1; // 通过指针访问数组元素
}
free(arr); // 释放内存
}
2.3、指针与字符串、数组
数组名可以隐式转换成指针,但不等于指针变量
c
char* str = "Hello"; // 字符串字面量存储在常量区,str指向其首地址
printf("%c\n", *(str + 2)); // 输出:'l'(等价于str[2])
// 指针遍历字符串
while (*str != '\0') {
printf("%c ", *str);
str++;
}
2.4、指针与数据结构
指针是构造复杂数据接口的基础
c
// 链表节点定义
struct Node {
int data;
struct Node* next; // 指向下一个节点的指针
};
// 创建节点
struct Node* createNode(int data) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
}
2.5、函数指针
将函数作为参数传递(回调函数)
c
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
// 函数指针作为参数
int calculate(int (*func)(int, int), int a, int b) {
return func(a, b);
}
int main() {
int result = calculate(add, 5, 3); // 等价于add(5, 3)
printf("%d\n", result); // 输出:8
return 0;
}
2.6、指针访问硬件地址
直接操作内存控制寄存器
c
// 访问GPIO寄存器(示例)
volatile unsigned int* gpio_output = (unsigned int*)0x40020014;
*gpio_output = 0xFF; // 向指定地址写入数据
3、使用注意事项
3.1、空指针(Null Pointer)
未初始化的指针可能指向随机地址,使用前必须赋值。
c
int* ptr = NULL; // 初始化为空指针
if (ptr != NULL) {
*ptr = 10; // 错误:空指针解引用
}
正确的使用方法。
c
#include <stdio.h>
#include <stdlib.h> // 包含malloc和free的头文件
int main() {
int* ptr = NULL; // 初始化为空指针(好习惯)
// 方法1:指向已存在的变量
int num = 5;
ptr = # // 指向变量num的地址
if (ptr != NULL) {
*ptr = 10; // 正确:ptr指向有效内存
}
printf("num = %d\n", num); // 输出:num = 10
// 方法2:动态分配内存
ptr = (int*)malloc(sizeof(int)); // 分配4字节内存
if (ptr != NULL) { // 检查分配是否成功
*ptr = 20; // 正确:ptr指向有效内存
printf("*ptr = %d\n", *ptr); // 输出:*ptr = 20
free(ptr); // 必须释放内存,防止泄漏
ptr = NULL; // 释放后设为NULL,避免野指针
}
return 0;
}
3.2、内存泄漏
动态分配的内存必须通过free()释放。
c
int* ptr = (int*)malloc(sizeof(int));
// ... 使用ptr ...
free(ptr); // 必须释放,否则内存泄漏
3.3、野指针(Dangling Pointer)
指针指向已经释放的内存。
c
int* ptr = (int*)malloc(sizeof(int));
free(ptr);
*ptr = 10; // 错误:ptr成为野指针
3.4、指针越界
访问数组范围外的指针。
c
int arr[5];
int* p = arr;
p[10] = 100; // 错误:越界访问,可能导致程序崩溃
3.5、指针与数组名有区别
数组名是常量指针,不可修改。
c
int arr[5];
// arr = NULL; // 错误:数组名不可赋值
int* ptr = arr;
ptr = NULL; // 正确:指针可修改