嵌入式学习第十一天:深入理解C语言指针
📚 学习目标
今天是嵌入式学习的第十一天,我们深入探讨了C语言中最重要的概念之一——指针。指针是C语言的精髓,也是嵌入式开发中直接操作硬件、管理内存的核心工具。
🎯 核心知识点总结
1. 指针的基本概念
1.1 地址与指针
- 地址:内存中每个字节都有一个唯一的编号,称为地址
- 指针:存储地址的变量,指向内存中的某个位置
- 指针变量:专门用来存放地址的变量
intnum=100;// 定义一个整型变量int*p=#// 定义指针变量p,指向num的地址1.2 指针的两个关键运算符
- &(取地址运算符):获取变量的内存地址
- *(取值运算符):获取指针指向的内存内容
inta=10;int*ptr=&a;// ptr保存a的地址printf("a的值:%d\n",a);// 输出:10printf("a的地址:%p\n",&a);// 输出:a的地址printf("ptr指向的值:%d\n",*ptr);// 输出:102. 指针变量的定义与使用
2.1 指针变量的定义格式
数据类型*指针变量名;// 示例int*p_int;// 指向int类型的指针char*p_char;// 指向char类型的指针float*p_float;// 指向float类型的指针double*p_double;// 指向double类型的指针2.2 重要特性
- 所有指针变量在64位系统中都占8个字节
- 指针类型决定了指针运算时移动的步长
- 指针必须初始化后才能使用,避免野指针
3. 指针的初始化与注意事项
3.1 正确的初始化方式
intnum=100;int*p1=#// 正确:指向已存在变量的地址int*p2=NULL;// 正确:初始化为空指针3.2 需要避免的情况
int*p1;// 错误:未初始化,野指针*p1=100;// 危险:操作未知内存int*p2=0x123456;// 错误:直接赋地址值(除非是硬件寄存器地址)4. 指针的算术运算
4.1 指针的加减运算
intarr[5]={10,20,30,40,50};int*p=arr;// p指向arr[0]printf("%d\n",*p);// 输出:10p++;// p指向arr[1]printf("%d\n",*p);//输出:20p+=2;// p指向arr[3]printf("%d\n",*p);// 输出:404.2 指针运算的规则
p + n:向高地址移动 n * sizeof(数据类型) 字节p - n:向低地址移动 n * sizeof(数据类型) 字节- 两个指针相减:得到它们之间相差的元素个数
5. 指针与数组的关系
5.1 数组名就是指针常量
intarr[5]={1,2,3,4,5};// 以下四种访问方式等价arr[2]=100;*(arr+2)=100;int*p=arr;p[2]=100;*(p+2)=100;5.2 数组传参的本质
// 函数声明(三种等价形式)voidfunc1(intarr[],intlen);voidfunc2(intarr[10],intlen);voidfunc3(int*arr,intlen);// 函数调用intmain(){intarray[10]={0};func1(array,10);// 传递的是数组首地址return0;}6. 指针作为函数参数
6.1 值传递 vs 地址传递
// 值传递:无法修改实参voidswap_value(inta,intb){inttemp=a;a=b;b=temp;}// 地址传递:可以修改实参voidswap_pointer(int*a,int*b){inttemp=*a;*a=*b;*b=temp;}intmain(){intx=10,y=20;swap_value(x,y);// x,y的值不变swap_pointer(&x,&y);// x,y的值交换return0;}6.2 通过指针返回多个值
voidget_min_max(intarr[],intlen,int*min,int*max){*min=arr[0];*max=arr[0];for(inti=1;i<len;i++){if(arr[i]<*min)*min=arr[i];if(arr[i]>*max)*max=arr[i];}}intmain(){intnums[5]={3,1,4,1,5};intmin_val,max_val;get_min_max(nums,5,&min_val,&max_val);printf("最小值:%d,最大值:%d\n",min_val,max_val);return0;}7. 指针的常见应用场景
7.1 动态内存管理(后续学习)
// malloc动态分配内存int*p=(int*)malloc(10*sizeof(int));if(p!=NULL){// 使用分配的内存free(p);// 使用完毕后释放}7.2 字符串操作
charstr[]="Hello";char*p=str;while(*p!='\0'){printf("%c",*p);p++;}7.3 函数指针(后续学习)
intadd(inta,intb){returna+b;}intsub(inta,intb){returna-b;}intmain(){int(*func_ptr)(int,int);// 函数指针声明func_ptr=add;printf("5+3=%d\n",func_ptr(5,3));func_ptr=sub;printf("5-3=%d\n",func_ptr(5,3));return0;}💡 重点难点解析
1. 指针的类型重要性
intnum=0x12345678;char*p_char=(char*)#int*p_int=#printf("通过char指针:0x%x\n",*p_char);// 输出:0x78(小端序)printf("通过int指针:0x%x\n",*p_int);// 输出:0x123456782. 指针的const修饰
constint*p1;// 指向常量的指针:不能通过p1修改指向的值int*constp2;// 常量指针:p2的指向不能改变constint*constp3;// 指向常量的常量指针:既不能改值也不能改指向3. 多级指针
intnum=100;int*p=#// 一级指针int**pp=&p;// 二级指针printf("num = %d\n",num);// 100printf("*p = %d\n",*p);// 100printf("**pp = %d\n",**pp);// 100🛠️ 实践练习
练习1:指针基础操作
#include<stdio.h>intmain(){inta=10,b=20;int*p1=&a,*p2=&b;printf("交换前:a=%d, b=%d\n",a,b);// 通过指针交换值inttemp=*p1;*p1=*p2;*p2=temp;printf("交换后:a=%d, b=%d\n",a,b);return0;}练习2:指针与数组
#include<stdio.h>voidreverse_array(int*arr,intlen){int*start=arr;int*end=arr+len-1;while(start<end){inttemp=*start;*start=*end;*end=temp;start++;end--;}}intmain(){intnums[5]={1,2,3,4,5};printf("原数组:");for(inti=0;i<5;i++){printf("%d ",nums[i]);}reverse_array(nums,5);printf("\n反转后:");for(inti=0;i<5;i++){printf("%d ",nums[i]);}return0;}📝 常见错误与调试技巧
1. 野指针问题
// 错误示例int*p;// 未初始化,野指针*p=100;// 可能引发段错误// 正确做法int*p=NULL;// 初始化为空指针if(p!=NULL){// 使用前检查*p=100;}2. 指针越界访问
intarr[5]={0};int*p=arr;for(inti=0;i<=5;i++){// 错误:i=5时越界p[i]=i*10;}3. 使用gdb调试指针
# 编译时加入调试信息gcc-gprogram.c-oprogram# 启动gdbgdb ./program# 常用命令(gdb)breakmain# 在main函数设置断点(gdb)run# 运行程序(gdb)print&var# 打印变量地址(gdb)print *ptr# 打印指针指向的值(gdb)x/4x ptr# 以16进制查看ptr指向的4个字节🔍 学习建议
- 多画内存图:理解指针时,在纸上画出内存布局图
- 循序渐进:从一级指针开始,逐步理解多级指针
- 实践为主:多写代码,通过调试观察指针的变化
- 理解本质:指针就是地址,所有操作都是对内存地址的操作
- 安全第一:始终检查指针是否为NULL,避免野指针
🎯 明日预告
明天我们将学习:
- 动态内存管理(malloc/free)
- 结构体与指针
- 链表的基本概念
📖 总结
指针是C语言中最强大也最容易出错的功能。今天的课程让我们理解了指针的本质、基本操作和常见应用。掌握指针需要时间和实践,建议大家在理解概念的基础上,多写代码、多调试,逐步建立对内存操作的直观感受。
记住:指针就是地址,操作指针就是操作内存。在嵌入式开发中,这种直接操作内存的能力正是我们控制硬件的关键!
