嵌入式Day12--指针
1.特点
- 让代码更加简洁
高效指针传参通过指针直接操作数据,避免不必要的数据拷贝,提高程序运行效率。 - 提供直接访问内存的操作
指针存储的是内存地址,可以直接读写该地址的数据,灵活控制内存。 - 利用指针可以直接操作硬件
指针可以直接访问硬件寄存器或内存映射的硬件设备。
2.概念
- 内存,存储运行时的数据。
- 内存地址: 计算机需要对物理内存进行管理。因为内存空间大,给每个存储单元(1byte)分配一个编号。这个编号就是内存地址,并且是线性连续的。
- 指针就是指针变量,是专门用来存储内存地址的变量。 在32bit操作系统下任意类型的指针大小为4byte(0-0xffffffff)。在64bit操作系统下,任意类型的指针大小为8byte(0-0xffffffffffffffff)
- 指针的类型是多种多样的。例如:int*(整形的),char* (字符指针),double*(浮点类型指针)任意类型的指针大小都是一致的,用指针可以表示任意的数据。
- 通过变量名访问内存的方式称为直接访问。 如果用指针方式访问称为间接访问。
- 当定义变量后,变量名会和对应的内存空间进行关联。变量名是内存的一个别名。
- 内存地址是一个编号,这个编号和int类型在c语言中是不同的两种类型,支持的运算符和操作是不同。
3.相关的运算符
3.1&(取地址)运算符
获得变量在内存空间中的地址编号(单目运算符,从右向左结合。)
只有变量(左值)在内存中有明确位置的值才能使用,常量和表达式不能进行&操作
int a =20; &a; // 获得a 变量在内存中的地址。 输入需要输出,通常使用%p来输出 取地址操作相当于升级操作 变量升级为对应类型的指针3.2 *(解引用)运算符
获得指针指向的空间或者对应空间中的值,通过指针间接访问它指向的数据。
*运算符连接的内容必须为指针类型,不能是普通变量类型
如果直接使用*对应的表达式,表达式值为:该指针指向空间中的值
int a =20; &a; 指针类型 *&a; // 20 int 解引用操作相当于降级操作 指针降级为变量类型。 解引用操作的过程 1. 找到指针中存储的地址 2. 根据指针的基类型(int * 的基类型是int) 确定占用内存大小 3. 取出:从地址开始的地方+基类型的大小取出数据。 取地址操作和解引用操作互为反操作。4.定义
不能使用未经初始化的指针
int *p; //指向空间的范围为4字节 char *p; //指向空间的范围为1字节 float *p; //指向空间的范围为4字节 double *p; //指向空间的范围为8字节 int *p, *q;5.初始化
指针变量定义好后一定要初始化。如果不初始化会存储随机地址。
5.1野指针
未经初始化的指针、指向已经被释放空间的指针、超出作用范围的指针,称为野指针
5.2空指针
不明确指向任何有效对象或内存地址的指针、指向内存地址为0x0的指针,称为空指针
用NULL来表示,它的值为 0
内存地址0x0空间为只读空间,不能赋值,NULL指针不能执行*p = value操作
int *p_i; // 定义了一个整形指针 int* .只能存储整形变量的地址 char* p_c = 0 ; // 都是8 个字节。 空指针 double* p_d ; // 野指针 short* p_s = NULL; //空指针 ,逻辑上先指向0地址稍后再给一个合理的地址6.指针的访问
变量的访问分为直接和间接访问
// 初始化 int i_a =20; p_i = &i_a; // 正常初始化 // &20; 只能是变量执行取址操作 // &(i_a +10); printf("a is addr %p\n",&i_a); printf("p_i is %p\n",p_i); printf("a is %d\n",i_a); // 直接访问 printf("指针访问,a is %d\n",*p_i); // 间接访问 i_a = 30;// 直接访问 printf("i_a = 30 ,a is %d\n",i_a); *p_i = 50;// 间接访问 printf("*p_i =50, a is %d\n",*p_i);内存存储如下:
7.指针的算术运算
算术运算(整形):+ - * / % ++ --
指针的算术运算:+ - ,++ -- (一般对数组进行访问的时候,会使用算术运算符)。
指针的加法 int a =20; // 0x2000 int int *p = &a; // 0x2000 &a-> int * printf("a is addr %p\n",&a); printf("p is addr %p\n",p); p=p+1; // p 中存储的地址值 变大。 p中原来的地址 + sizeof(基类型 ,4byte) 0xebbc 0xebc0 printf("p+1 is addr %p\n",p);#include <stdio.h> int main() { int a =20; // 0x2000 int int *p = &a; // 0x2000 &a-> int * printf("a is addr %p\n",&a); printf("p is addr %p\n",p); #if 0 // 减法 p=p-1; printf("p-1 is addr %p\n",p); 先获得p 指针中存储的地址,和sizeof(基类型)的大小进行相减操作。 // p = p*2; // 乘法没有意义 // p = p/2; // 没有意义 // p = p%4; //没有意义 // 加法 先获得p指针中存储的地址,和sizeof(基类型)的大小进行相加操作。 p++; // p = p+1; printf("p+1 is addr %p\n",p); #endif // 先获得p指针中存储的地址,和sizeof(基类型)的大小进行相减操作。 p--; printf("p-1 is addr %p\n",p); nt b = 20; int *pb = &b; //p+pb ; 两个指向不同变量的指针,进行相加没有意义。 // p - pb; 两个指向不同变量的指针,进行相减没有意义。 //如果这两个指针,指向数组的开头和数组的结尾。 int i_a[8]={0}; int * p_start = &i_a[0]; int *p_end = &i_a[7]; int ret= p_end - p_start ; //int* -int * 结果类型 int 有意义 相差了几个数据元素 。 (0x2000 -x201c) / sizeof(int) printf("ret is %d\n",ret); }不同类型的指针的解引用操作
如果执行 * 解引用操作话 。 假设指针存储的地址是0x2000 char* ,取0x2000 开始的1个字节空间的数据 int* ,取0x2000 开始的4个字节空间的数据 short* ,取0x2000 开始的2个字节空间的数据 float* ,取0x2000 开始的4个字节空间的数据 double* ,取0x2000 开始的8个字节空间的数据不同类型的指针的++ ,--
如果执行 ++ 。 假设指针存储的地址是0x2000 char* ,偏移量 1byte -> 0x2001 int* ,偏移量 4byte -> 0x2004 short* ,偏移量 2byte -> 0x2002 float* ,偏移量 4byte -> 0x2004 double* ,偏移量 8byte -> 0x20088.内存大小端
对于多字节(大于1字节以上的基本数据类型)的数据在内存中数据存储字节的顺序。
大小端存储方式是由CPU 决定。51单片机,网络设备,都使用大端方式存储数据。AMD,intel,
ARM都是采取小端存储。
小端存储,数据的低位在内存的低地址,数据的高位在内存的高地址
大端存储,数据的低位在内存的高地址,数据的高位在内存的低地址
#include <stdio.h> int main() { int a = 0x12345678; // 强制类型转换 char* p = (char*)&a; // char* = int * // *p 从p指向的首地址开始,偏移量+1(byte) ,取出数据 //printf("val: 0x%x\n",*p); if( 0x78 == *p) { printf("小端存储\n"); } else { printf("大端存储\n"); } return 0; }