[C语言]指针知识系统总结
1.指针的基础定义和使用
指针是C语言中存储变量内存地址(0x0000 0000-0xffff ffff---x86)的变量.通过指针可以直接访问和操作内存中的数据,提高程序的灵活性和效率
1.1核心定义语法
一级指针:数据类型* 指针名 = &目标变量
char val = 'a';
char* p = &val;
//p存储val的地址,通过*p = 'b'可修改val的值
二级指针:数据类型** 指针名 = &一级指针
char** q = &p;
//通过**q = 'c'可间接修改val的值
1.2基本操作
#include <stdio.h>
int main() {
int num = 10;
int *p = # // &取地址
printf("num的值: %d\n", num); // 10
printf("num的地址: %p\n", &num); // 地址
printf("p的值: %p\n", p); // 地址
printf("p指向的值: %d\n", *p); // *解引用
*p = 20; // 通过指针修改变量值
printf("修改后num: %d\n", num); // 20
return 0;
}
1.3基础概念
&(取地址符):获取变量的内存地址
*(解引用符):获取指针所指向地址中存储的值
指针的大小只取决于系统架构x86(win32)--4B x64(win64)--8B
1.4注意事项
1)单个声明中,* 仅绑定左侧变量:char* pa, pb; 中 pa 是指针,pb 是普通字符变量;
若需多个指针,需显式加 *(char* pa, *pb;)或用 typedef 简化:typedef char* PChar; PChar pa, pb;(两者均为指针)
2)避免用 #define 定义指针类型:#define PCHAR char*; PCHAR pa, pb; 会被替换为 char* pa, pb;,仅 pa 是指针,易出错
2.const和指针结合
const 与指针结合使用,可以定义不同的“常量”概念,增强程序的健壮性
2.1指向常量的指针
可以改变指向,不能修改值
const int* ptr; //指向常量的指针
int const* str; //等价写法
2.2常量指针
可以修改值,不能改变指向
int* const ptr = &var;
2.3指向常量的常量指针
既不能改变指向,也不能修改值
const int* const ptr = &var;
2.4记忆技巧
从右向左读
2.5记忆口诀
const 在 * 左边,修饰的是数据;const 在 * 右边,修饰的是指针本身
const int *p1; // p1是指向const int的指针
int *const p2; // p2是const指针指向int
3.野指针和空指针
野指针和空指针均会导致程序崩溃,核心是避免访问无权限或不确定的内存地址
3.1野指针(悬挂指针)
指向一个无效内存地址的指针。对野指针进行解引用会导致未定义行为(程序崩溃、数据损坏)
常见的场景有:
1)未初始化的指针:int* p;(p 存储随机地址),直接解引用 *p = 1; 会触发内存访问错误
2)指向已销毁的局部变量:函数返回局部数组地址(int* func(){ int arr[2]; return arr; }),函数结束后 arr 内存释放,返回的指针变为野指针
int* ptr; // 未初始化,野指针
free(ptr); // 释放后未置空,野指针
3.2空指针
用 NULL(宏定义,值为 0)初始化的指针,int* p = NULL;。规则:0 号地址(内存前 128MB)是系统保留区域,禁止访问,*p = 1; 会崩溃;NULL 的作用是明确指针 “暂时无指向”,避免野指针的随机性
int* ptr = NULL;
3.3注意事项
1)指针初始化:要么指向有效变量(int* p = &val;),要么设为 NULL。
2)避免强行转换固定数值为指针:int* p = (int*)200000;
4.指针运算
指针运算本质是地址的偏移,偏移量由指针指向的类型决定,仅在连续内存(如数组)中有实际意义
4.1指针 ± 整型:按类型偏移
1)核心规则:指针 + n 等价于 指针地址 + n * sizeof(指针指向的类型)。例:int arr[] = {1,2,3,4,5}; int* p = arr;(arr 是数组首元素地址),p+1 偏移 1*4=4 字节(int 占 4 字节),指向 arr[1];char* pn = #(char 占 1 字节),pn+1 偏移 1 字节。
2)数组访问的本质:arr[i] 等价于 *(arr + i),arr 是首元素地址,arr + i 指向第 i 个元素,解引用后得到值
4.2指针 - 指针:计算元素个数
仅当两个指针指向同一连续内存(如同一数组)时,结果为 “两指针间的元素数量”,而非地址差值。例:int arr[] = {1,2,3}; int* p1 = &arr[0], *p2 = &arr[2];,p2 - p1 = 2(间隔 2 个元素)
4.3面试高频:内存大小端判断
1)大端模式:高位字节存低地址(如 int val = 0x12345678,内存中存为 0x12 0x34 0x56 0x78)
2)小端模式:低位字节存低地址(内存中存为 0x78 0x56 0x34 0x12,主流 CPU(x86)采用)
int val = 0x12345678;
char* p = (char*)&val;
if (*p == 0x78) printf("小端");
else printf("大端");
//利用 char 指针仅访问 1 字节的特性
4.4基本要点
递增/递减: p++,p--
加上/减去整数: p + n,p - n
指针相减: 得到的是两个指针之间相隔的元素个数
int arr[] = {10, 20, 30, 40};
int *p = arr; // p指向数组第一个元素
printf("%d\n", *p); // 输出:10
p++; // 移动到下一个int元素
printf("%d\n", *p); // 输出:20
int *q = &arr[3];
printf("q - p = %td\n", q - p); // 输出:3 (相隔3个int)
5.指针数组和数组指针
5.1指针数组
一个数组,里面的每个元素都是指针
int a = 1, b = 2, c = 3;
int *arr[3] = {&a, &b, &c}; // arr是一个包含3个int*的数组
5.2数组指针
一个指针,它指向一个数组
int arr[2][3] = {{1,2,3}, {4,5,6}};
int (*p)[3] = arr; // p是一个指针,指向一个包含3个int元素的数组
// 通过p访问arr[1][2]
printf("%d\n", *(*(p + 1) + 2)); // 输出:6
// p + 1 跳过一行(3个int),解引用后得到第2行首地址,再+2得到元素地址,最后解引用。
5.3区分技巧
用 “优先级” 判断
1)int *arr[3]:arr先与[3]结合,是数组,其元素是int*
2)int (*p)[3]:p先与*结合,是指针,指向int [3]的数组
6.指针与函数
6.1指针作为函数参数
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int x = 5, y = 10;
swap(&x, &y); // x和y的值被交换
6.2指针作为函数返回值
int* getMax(int *a, int *b) {
return (*a > *b) ? a : b;
}
切勿返回指向局部变量的指针,因为函数结束后局部变量会被销毁。可以返回全局变量、静态变量或动态分配内存的指针
6.3函数指针
int add(int a, int b) { return a + b; }
int (*funcPtr)(int, int) = &add; // 定义函数指针并指向add函数
int result = funcPtr(3, 4); // 通过函数指针调用函数,result = 7
// 等价于:int result = (*funcPtr)(3, 4);
7.void*
void* 是一种通用指针类型,可以指向任何类型的数据,但不可以直接进行解引用和算术运算
7.1核心特性
1)可接收任意类型地址:int val = 10; void* p = &val; double num = 3.14; p = #(无需强制转换)
2)禁止直接操作:不能解引用(*p 报错,无法确定访问字节数),不能算术运算(p+1 报错,无法确定偏移量)
int n = 100;
float f = 3.14;
void *vp;
vp = &n; // 指向int
printf("%d\n", *(int*)vp); // 必须先进行强制类型转换
vp = &f; // 指向float
printf("%f\n", *(float*)vp);
7.2应用:实现通用函数
1)原理:将 void* 强转为 char*(1 字节),通过逐字节操作适配任意类型,需传入 “数据大小”(size_t)
void myCopy(void* dest, const void* src, size_t size) {
char* d = (char*)dest;
const char* s = (char*)src;
while (size--) { *d++ = *s++; } // 逐字节拷贝
}
2)使用场景:拷贝 int 数组、double 数组等任意类型数据,如 int arr1[3] = {1,2,3}; int arr2[3]; myCopy(arr2, arr1, 3*sizeof(int));
8.内存 - men 系列函数
mem 系列函数是标准库中操作内存的工具,核心是 “直接操作字节”,需区分 memcpy 和 memmove 的差异
8.1memset
将内存块的前 size 字节设为指定值(int c),如 memset(arr, 0, sizeof(arr));(数组初始化)
8.2memcpy
从 src 指向的内存,拷贝 size 字节到 dest 指向的内存,返回 dest.若 dest 和 src 内存重叠,拷贝结果会出错
8.3memcmp
比较两个内存块的前 size 字节,返回 0(相等)、正数(前者大)、负数(后者大)
更多推荐


所有评论(0)