嵌入式学习 day11:指针 一、何为指针1. 地址计算机内存按字节编址每个字节都有一个唯一的编号这个编号就是地址。地址就像门牌号例如0x2000,0x2001......一个int占4个字节它的地址是这4个字节中第一个字节的地址首地址。2. 指针指针可以理解为就是地址地址也可以理解为指针。指针有类型且有指向空间大小的含义。3. 指针变量用来存放指针地址的变量就叫指针变量。cint num 10; int *p num; // p就是指针变量存的是num的地址4. 指针功能让程序更简洁高效——直接操作内存不用绕弯子能直接访问内存空间——这是C语言贴近硬件的体现二、指针相关运算符1. 取地址运算符用来获取变量的地址。cint num 10; printf(%p\n, num); // 输出num在内存中的地址注意事项只有左值变量才能取地址常量和表达式不行num的类型是int *int的指针类型2. * 取值运算符*用来获取指针指向地址里存放的值。cint num 10; int *p num; printf(%d\n, *p); // 输出10相当于把p指向的地址里的值取出来num→ 拿到num的地址*(num)→ 拿到num的地址里的值 → 回到num本身*在定义变量和操作变量时意义不同int *p里的*表示这是个指针变量而*p 20里的*表示取这个地址里的值。三、指针变量的定义1. 基本格式c数据类型 *指针变量名;cint *p; // 指向int的指针 char *q; // 指向char的指针 float *r; // 指向float的指针2. 指针变量的大小指针变量存的是地址而地址的大小由系统决定32位系统地址占4字节64位系统地址占8字节cprintf(%lu\n, sizeof(int *)); // 64位系统输出8 printf(%lu\n, sizeof(char *)); // 输出8无论什么类型的指针它本身占的大小是一样的。但指针的类型决定了它指向的空间大小例如int *类型指针变量为4字节char *类型指针变量为1字节。3. 指针变量初始化指针变量不初始化的话里面存的是一个随机地址这个地址可能指向系统的关键区域操作它可能导致程序崩溃段错误。空指针指向地址0x0的指针用NULL表示。cint *p; // ❌ p是随机值危险 int *p NULL; // ✅ 初始化为NULL表示不指向任何地方野指针未经初始化的指针或指向已释放空间的指针。cint *p; // 野指针p的值是随机的 *p 10; // 危险可能修改了系统关键数据指针定义时一定要初始化要么赋一个有效地址要么赋NULL。不要让它处于不确定的状态。4. 指针变量的赋值cint num 10; int *p num; // 定义时初始化 int *q; q num; // 赋值为num的地址5. 通过指针修改变量的值cint num 10; int *p num; *p 20; // 修改p指向的地址num里的值为20 printf(%d\n, num); // 输出20四、指针的算术运算指针的加减运算不是简单的数字加减而是以指向的数据类型大小为单位进行偏移。cint a[5] {10, 20, 30, 40, 50}; int *p a[0]; // p指向a[0] printf(%p\n, p); // 假设输出 0x100 printf(%p\n, p 1); // 输出 0x104加4字节因为int占4字节 printf(%d\n, *(p 1)); // 输出20运算含义p n向高地址偏移n × sizeof(指向类型)个字节p - n向低地址偏移n × sizeof(指向类型)个字节p指针向后移动一个元素p--指针向前移动一个元素p - q两个指针之间相差的元素个数指针的加减运算和数组下标天然对应*(p 1)等价于p[1]*(p n)等价于p[n]。a[0]就是*(a 0)也就是数组首地址本身。五、指针与一维数组1. 数组名就是地址数组名是指向数组第一个元素的指针常量。cint a[5] {1, 2, 3, 4, 5}; // 以下三种写法等价 a[0] // 1 *(a 0) // 1 *a // 1 // 以下三种写法也等价 a[2] // 3 *(a 2) // 3数组名可以赋值给指针cint *p a; // 等价于 int *p a[0];但数组名不能改变指向它是指针常量cint *p a; p; // ✅ 指针变量可以改 a; // ❌ 数组名不能改2. 两种特殊情况数组名≠指针虽然大多数情况下数组名可以当指针用但有两个例外情况说明sizeof(a)计算的是整个数组的大小不是指针大小a取的是整个数组的地址类型是int (*)[5]不是int *3. 数组元素访问的等价关系ca[n] *(a n) *(p n) p[n]C语言中[]可以搭配指针使用p[n]等价于指针n再取*六、指针作为函数参数1. 值传递 vs 地址传递传递方式特点能否修改外部变量值传递传的是值的副本❌ 不能地址传递传的是地址指针✅ 能c// 值传递 void swap_value(int a, int b) { int tmp a; a b; b tmp; } // 地址传递 void swap_pointer(int *a, int *b) { int tmp *a; *a *b; *b tmp; } int main(void) { int x 10, y 20; swap_value(x, y); // ❌ 没变化 swap_pointer(x, y); // ✅ x20, y10 return 0; }由于形参改变不影响实参需要使用传地址的方法修改变量的值。地址传递的本质是形参虽然还是副本但这个副本是地址它和实参的地址指向的是同一块内存。所以通过地址去修改就能动到实参。因此scanf里要用。2. 数组传参的本质数组传参时传的其实是数组首元素的地址指针所以函数内部能修改数组元素。c// 以下三种写法完全等价 void fun(int array[], int len); void fun(int array[5], int len); void fun(int *parray, int len);七、今日总结今天学了指针核心要点如下指针就是地址指针变量是存放地址的变量 取地址* 解引用定义时的 * 表示这是个指针变量指针类型决定步长p1偏移sizeof(指向类型)个字节指针初始化野指针是随机地址空指针是0x0指针必须初始化数组名即指针a指向数组首元素a[n] *(an)两种例外sizeof(a)和a时数组名不退化地址传递用指针做参数函数内可以修改外部变量数组传参传的是地址本质是int *parray三种写法等价今日感悟指针和数组的关系是C语言最精妙的设计之一是函数参数是实现函数返回多个值的唯一方式。数组名就是地址下标就是偏移。理解a[n] *(an)为后续指针的学习打下基础。