C语言基础入门--指针
8.指针
8.1 指针的基本概念
什么是指针?
指针本质上是一个变量,但与其他变量不同,它存储的值是其他变量的内存地址
。就像char类型变量的值是字符,int类型变量的值是整数,指针变量的值是地址。
指针的大小
- 在32位平台上,指针占4个字节
- 在64位平台上,指针占8个字节
内存地址与指针的关系
指针本质上存储的是内存地址。每个内存字节都有一个唯一的地址编号,就像街道上的每个房子都有唯一的门牌号一样。
- 32位系统:内存地址用32位二进制数表示,寻址范围是0到2³²-1(约4GB)
- 64位系统:内存地址用64位二进制数表示,寻址范围是0到2⁶⁴-1(这个空间极其巨大)
8.2 指针的声明与初始化
指针的声明
数据类型 *指针变量名;
示例:
int *p; // 指向int类型的指针char *pc; // 指向char类型的指针float *pf; // 指向float类型的指针
指针的初始化
方法1:指向已存在的变量
int num = 10;int *p = # // p指向变量num的地址
方法2:动态内存分配
int *p = (int*)malloc(sizeof(int)); // 分配内存*p = 10; // 在分配的内存中存储值free(p); // 使用后释放内存
方法3:初始化为空指针
int *p = NULL; // 表示指针当前不指向任何有效的内存地址
8.3 指针的基本操作
取地址运算符(&)和解引用运算符(*)
- & :获取变量的地址
- * :获取指针指向的地址中存储的值
#include <stdio.h>int main() {int num = 10;int *p = # // p指向num的地址printf("num的值: %d\n", num); // 直接访问printf("num的地址: %p\n", &num); // 获取地址printf("通过p访问的值: %d\n", *p); // 间接访问(解引用)printf("p存储的地址: %p\n", p); // 指针的值(即地址)return 0;}
8.4 指针的类型重要性
指针的类型不仅决定了它指向的数据类型,还决定了:
① 访问权限(能操作几个字节)
char *pc; // 解引用只能访问1个字节int *pi; // 解引用能访问4个字节double *pd; // 解引用能访问8个字节
② 步长(指针加减的跨度)
int arr[5] = {1, 2, 3, 4, 5};int *pi = arr;char *pc = (char*)arr;printf("pi: %p, pi+1: %p\n", pi, pi+1); // 地址差4字节printf("pc: %p, pc+1: %p\n", pc, pc+1); // 地址差1字节
8.5 指针与数组的关系
数组名在大多数情况下可以看作是指向数组第一个元素的指针。
一维数组与指针
int arr[5] = {1, 2, 3, 4, 5};int *p = arr; // 等价于 int *p = &arr// 以下访问方式是等价的printf("%d\n", arr[2](@ref); // 数组下标方式printf("%d\n", *(arr+2)); // 指针算术方式printf("%d\n", p[2](@ref); // 指针下标方式printf("%d\n", *(p+2)); // 指针解引用方式
指针算术运算
int arr[5] = {10, 20, 30, 40, 50};int *p = arr;p++; // 指向下一个元素(arr[1])p += 2; // 指向arrp--; // 指向前一个元素
指针相减
int arr[10] = {0};int *p1 = &arr[2];int *p2 = &arr[6];int distance = p2 - p1; // 结果为4(元素个数)
8.6. 特殊类型的指针
指针数组 vs 数组指针
// 指针数组:每个元素都是指针int a=1, b=2, c=3;int *arr[3] = {&a, &b, &c};// 数组指针:指向整个数组的指针int matrix[4];int (*p)[4] = matrix; // 指向包含4个元素的数组
多级指针
int num = 10;int *p = # // 一级指针int **pp = &p; // 二级指针(指向指针的指针)printf("num = %d\n", num); // 直接访问printf("*p = %d\n", *p); // 一级解引用printf("**pp = %d\n", **pp); // 二级解引用
函数指针
#include <stdio.h>int add(int a, int b) {return a + b;}int main() {int (*fp)(int, int) = add; // 函数指针int result = fp(3, 5); // 通过指针调用函数printf("结果: %d\n", result);return 0;}
8.7 野指针与空指针
野指针的概念与危害
野指针是指指向不可知位置的指针,使用野指针会导致未定义行为,可能造成程序崩溃。
野指针的成因
- 指针未初始化
- 指针越界访问
- 指针指向的空间已释放
如何避免野指针
// 1. 初始化指针int *p = NULL;// 2. 使用前检查有效性if (p != NULL) {*p = 10;}// 3. 释放后置空free(p);p = NULL;
8.8. const与指针
const修饰指针时有三种情况:
① 指向常量的指针
const int *p; // 指针指向的内容不可变,但指针本身可以指向其他地址
② 常量指针
int num = 10;int *const p = # // 指针本身不可变(始终指向同一地址),但指向的内容可变
③ 指向常量的常量指针
cconst int *const p = # // 指针本身和指向的内容都不可变
8.9 指针的实际应用示例
函数参数传递(修改实参)
void swap(int *a, int *b) {int temp = *a;*a = *b;*b = temp;}int main() {int x = 10, y = 20;swap(&x, &y); // 传递地址,实参被修改printf("x=%d, y=%d\n", x, y); // 输出x=20, y=10return 0;}
动态数组创建
#include <stdio.h>#include <stdlib.h>int main() {int size = 5;int *arr = (int*)malloc(size * sizeof(int));if (arr == NULL) {printf("内存分配失败\n");return 1;}// 使用动态数组for (int i = 0; i < size; i++) {arr[i] = i * 10;}// 释放内存free(arr);arr = NULL;return 0;}
指针使用注意事项
- 始终初始化指针,避免野指针
- 检查指针是否为NULL后再使用
- 注意指针的边界,避免越界访问
- 动态分配的内存要及时释放,防止内存泄漏
- 理解指针类型,确保正确的字节操作和步