指针
指针概述
指针:存储地址的特殊变量 问题:指针的内存属性 1.分配了一个指针,这个指针在内存中有多大? 在32bit的系统中,指针就4个字节 2.指针存放的地址所指向的内存的读取方法是什么? 指针指向的内存空间一定要保证合法性(段错误) 数据在栈中存储,是从高地址向低地址存放的
指针+修饰符 const ,volatile , typedef
更加明确定义地址的具体属性
const 常量 只读【不能变】 const char *p; 常量指针 【字符串】 存放的地址不属于main函数 char const *p; p的前面是*,它是一个指针,可以指向其他的位置;而它指向的值,是被const修饰的,不能被更改 char *const p;指针常量 【硬件资源,缓存地址】 char *p const; 指针指向的地址不能变,但指向的值可以变 const char *const p; 【ROM只读存储器】
volatile 防止优化 对指向的内容的一种修饰 如果对硬件层面的数据进行优化的话,会导致数据不准确,但在软件层面可以进行数据的优化
typedef 别名 什么类型 变量名称 char *name_t; name_t是一个指针,指向了一个char类型的内存 typedef char *name_t; name_t是一个指针类型的名称,指向了一个char类型的内存 name_t abc; abc是一个指针,指向了一个char类型的内存
指针+运算符 ++,--,+,- ,[],逻辑运算符
指针的加法/减法运算,实际上变化的是一个单位,单位的大小可以使用sizeof(p[0])来确定 int *p=xxx; 【0x12】 p+1; 【0x12+1*(sizeof(*p))】 p++,p-- p++等价于p=p+1 指针的自增,自减涉及到更新地址的作用
变量名[n] 跳跃式访问 n:ID标签 【地址内容的标签访问方式】取出标签里的内存值
利用内存越界,强制修改字符串常量 int main() { const int a=0x12345678;//栈中高地址 int b=0x11223344;//栈中低地址 a=100;//直接修改,段错误,编译不通过 int *p=&b; p[1]=0x100;//直接通过内存修改值 printf("the a is %x\n",a);//0x100 return 0; }
逻辑操作符==,!= 1.跟一个特殊值进行比较 0x0 -》地址的无效值,结束标志-》NULL 2.指针只能同类型相互比较
多级指针
将一堆非线性地址存放的数据,用【线性的指针】将它们的地址存储,然后再使用一个地址将这一段线性地址的首地址保存下来 int **p; 二维指针的结束标志:p[m]==NULL;
数组
内存分配的一种形式
数组的定义与初始化
定义一个空间 1.大小 2.读取方式 数据类型 数组名[m];//m的作用域只在申请的时候 数组名是一个常量符号,一定不能放到=的左边 char buf[100]; buf="hello world"; 一看到数组,就要想到【越界】问题 数组与指针的区别 都可以使用[],里面可以是任意值,编译器不会判别是否越界 指针存放的是地址,地址可以变 数组名是地址的常量标签,不可以变
数组空间的初始化
空间的赋值 --》按照标签逐一处理 数组空间的初始化 和 变量的初始化 本质不同,尤其在嵌入式的裸机开发中,空间的初始化往往需要库函数的辅助 对于char 类型数组 char buf[10]={a,b,c}; buf当成普通内存来看,没有问题 buf当做一个字符串来看,最后一定要加上‘\0’或 0 字符串的重要属性,结尾一定有一个'\0' char buf[10]={"abc"};//最简单方式,实际有四个字符 char buf[10]="abc";//是将”abc“这一个字符串常量拷到buf变量中 buf[2]='e'; //该语句正确,对变量的修改可以 注意与char *p="abc";区分开,该句表示,用指针指向常量区 p[2]='e'; //不正确,字符串所在的常量区只能读,不能写
第二次内存初始化,赋值 经典错误: char buf[10]="abc"; buf="hello world"; 正确方式:逐一处理 buf[0]='h' buf[1]='e' ........buf[n]='d' buf[n+1]=0 strcpy(); 一块空间,当成字符空间,编译器提供了一套字符拷贝函数 字符拷贝函数的原则: 内存空间和内存空间逐一赋值的功能的一个封装体 一旦空间中出现:0 函数将提前结束 当拷贝源地址大于目标地址空间,也会继续拷贝,直到 0 出现 char buf[10]="abc"; buf="hello world"; strcpy(buf,"hello world"); strcpy()存泄漏函数 ——》strncpy(); 指明拷贝的个数,最多只拷贝那么多
字符空间 用ASCII码来解码的空间--》给人看 %s '\0'作为结束标志 非字符空间 数据采集 0x00 - 0xff 长度:8bit 开辟一个存储这些数据(温度或湿度传感器采集到的数据)的盒子 char buf[10];--->string unsigned char buf[10];--->data 如何给非字符空间拷贝? 不能用strcpy()函数,因为这个函数适用于字符串的,当遇到'\0'时,才会停止拷贝,但是在数据中,不存在反斜线0作为标志 只能定义个数! 拷贝三要素: 1.src 2.dest 3.个数 memcpy()函数举例 int buf[10]; int sensor_buf[100]; memcpy(buf,sensor_buf,10*sizeof(int)); //在memcpy函数中,最后一个参数,是指拷贝的个数,它的字节类型是size_t ,所以要再乘上一个sizeof(数据类型),当然也可以直接写为sizeof(buf) unsigned char buf1[10]; unsigned char sensor_buf[100]; memcpy(buf,sensor_buf,10*sizeof(unsigned char));
指针与数组
指针数组,数组里存放了一堆地址 char *a[100]; sizeof(a)=100*4; 可以将指针数组与二维指针相互联系
多维数组
数组名的保存 定义一个指针,指向int a[10]的首地址 定义一个指针,指向int b[5][6]的首地址 int c[2][3][4] int *p1=a; int (*p2)[6]=b; int (*p)[3][4]=c; 错误:int **p2=b; 二维指针:将一堆非线性地址存放的数据,用【线性的指针】将它们的地址存储,然后再使用一个地址将这一段线性地址的首地址保存下来 二维数组:读取内存方式,每次读一行 数组:每次读一个基本数据类型长度的内存 int *p[5];有很多指针,每个指针都指向不同的地址 指针数组 int (*p)[5];相当于只有一个指针,可以一次性读5个块 数组指针
结构体,共用体
举例: struct abc{ char a; int b; }; 内存:1+4=5??? 字节对齐:希望牺牲一点空间换取时间上的效率 最终结构体的大小一定是4的倍数(看具体的系统) 4+4=8 列如4个字节大小的数据总线,只有将它填满时,效率最高,并且可以利用指针的自增 打包的顺序:结构体里成员变量的顺序不一致,也会影响内存开销 struct abc{ 1 2 4 char a; 4 4 8字节 short e; int b; }; struct my{ 1 4 2 char a; 4 4 4 12字节 int b; short e; };
内存分布图
概述
内存的属性:大小,位置 栈空间:运行时,函数内部使用的变量,函数一旦返回就释放,生存周期较函数内 堆空间:可以自由,自我管理的分配和释放的空间,生存周期由程序员来决定 只读空间:静态空间,整个程序结束时释放内存,生存周期最长 内存分布图: 高地址: 内核空间:应用程序不允许访问 ------------------- 栈空间:局部变量 ------------------- 运行时的堆空间:malloc ------------------- 全局的数据空间(初始化的,未初始化)static、 data bss 只读数据段:”hello world“,常量化 R text 代码段:code R text 低地址:
数据段
命令:size build 显示内存分段 命令:查看静态空间 nm
堆空间
可以自由,自我管理的分配和释放的空间,生存周期由程序员来决定 分配:malloc(),一旦成功,返回分配好的地址给我们,只需要接收,对于*地址的读法,由程序员决定,输入参数指定分配的大小,单位就是B。 char *p; p=(char *)malloc(100); if(p==NULL)//错误返回 { //error } *内存泄漏错误: void fun() { char *p; p=(char *)malloc(100); return ; } //当fun函数结束时,应该释放char *p,但是在fun函数中申请的堆空间并没有被释放掉,当想要操作这一段空间,是非常困难的 释放: free(p);//分配和释放一定要配对