




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
指针与内存管理静态/全局内存静态声明的变量分配在这里全局变量也使用这部分内存这些变量在程序开始运行时分配,直到程序终止才消失,所有函数都能访问全局变量,静态变量的作用域局限在定义他们的函数内部。自动内存这些变量在函数内声明,并且在函数被调用时才创建,它们的作用域局限于函数内部,而且生命周期限制在函数的执行时间内。动态内存内存分配在堆上,可以根据需要释放,而且直到释放才消失指针引用分配的内存,作用域局限于引用内存的指针。指针变量包含内存中别的变量、对象或函数的地址。指针本身并没有包含所引用数据的类型信息,只包含地址。示例1:同为打印指针数组第二个元素第二个字母,但数组方式表达更简洁。char*names[]={"MillerVJones","Anderson"};printf("%c\n",*(*(names+1)+2));//这里需把names看作二级指针,即names存放的是字符串数组的首地址,而*names内容为指针数组第一个元素的首地址。printf("%c\n”,names[1][2]);使用指针可能出现的问题:访问数组和其他数据结构时越界自动变量消失后被引用堆上的内存释放后被引用内存分配之前解引指针注:1.若指向未初始化的指针,指针内容可能并不是一个合法的地址,就算是合法地址,也可能没包含合法的数据,程序没有权限访问不合法地址。不初始化也可以使用指针,但尽快初始化指针是好习惯如何区分常量指针和指针常量:答案是倒过来读(理解),如constint*p;int*constp;//倒过来读很好区分注:1.整型不能赋值给指针,但可以赋值为0,如:int*p=0,原因是0被重载了虚拟内存和指针程序使用的是虚拟内存,操作系统会在需要时把虚拟地址映射到物理内存地址。Constint*const*p为双重指针,即指向“指向常量的常量指针”左值:指的是赋值操作符左边的操作数,所有的左值都必须可以修改,因为它们会被赋值。Null的概念:NULL的定义:#defineNULL((void*)0)Void指针:通用指针,用来存放任何数据类型的指针,但只能用作数据指针,不能用作函数指针。注:l.void指针具有与char指针相同的形式和内存对齐方式void指针和别的指针永远不会相等,不过两个赋值为NULL的void指针相等。sizeof可以用在void*上,不能用在void上全局和静态指针:指针被声明为全局或静态,就会在程序启动时被初始化为NULL。存放在堆中。指针的算术运算:指针加法:给指针加上一个整数,实际上加的数是这个整数和指针数据类型对应字节数的乘积示例2:intvector[]=(28,41,7};Int*pi=vector;//若pi=100Printf("%d\n”,*pi);输出28Pi+=1;//pi=104Printf("%d\n”,*pi);//输出41注:a.对于short类型的指针,加1时实际上地址值加2,char型则加1指针减法和加法运算一样两指针可以相减,也可以进行比较,但通常没什么用,可以用来判断数组中元素的顺序。Malloc动态内存分配:示例3:int*pi=(int*)malloc(sizeof(int));*pi=5;printf("%d\n”,*pi);free(pi);注:malloc和free必须成对使用,指针释放过后就不应该再去访问,通常将释放后的指针赋值为NULL若分配的内存没有释放则会造成内存泄漏静态、全局指针和malloc:初始化静态或全局变量时不能调用函数,如下面这句就会报错。Staticint*pi=malloc(sizeof(int));对于静态变量,可以通过在后面用一个单独的语句给变量分配内存来避免这个问题。如:Staticint*pi;Pi=malloc(sizeof(int));但是全局变量不能用单独的赋值语句,因为全局变量是在函数和可执行代码外部声明的,而赋值语句这类代码必须出现在函数中注:在编译器看来,作为初始化操作符的=和作为赋值操作符的=不一样。使用calloc函数:Calloc会在分配内存的同时清空内存。Int*pi=calloc(5,sizeof(int));//第一个参数为数量,第二个参数为类型占用字节数迷途指针:如果内存已经释放,而指针还在引用原始内存。这样的指针就叫做迷途指针。注:visualstudio会在释放内存后用0xCC、0xCD或者0xDD复写数据。在不抛出异常的情况下,如果在预期之外的地方看到这些值,可以认为程序可能在访问已经释放的内存。程序栈:是支持函数执行的内存区域,通常和堆共存,也就是说它们同享同一块内存区域,程序栈通常占据这块区域的下部而堆则占用上部。程序栈存放栈帧栈帧存放函数参数和局部变量,堆管理动态内存。调用函数时,会创建函数的栈帧并将其推到程序栈上,函数返回时,其栈帧从程序栈弹出。系统在创建栈帧时,将参数以跟声明相反的顺序推到帧上,最后推入局部变量。函数中的块语句被当作“微函数”,会在合适的时机(执行时)被推入栈和从栈中弹出。通过指针传递和返回数据:传递指针可以让多个函数访问指针所引用的对象,而不用把对象声明为全局可访问。用指针传递数据的一个主要原因是函数可以修改数据。通过传递一个指向常量的指针可以使用指针传递数据并禁止其被修改。而当数据是需要被修改的指针时就传递指针的指针(两级指针,注:用值传递数据时,不能改变实参的值,因为修改形参不会影响实参。返回指针可能存在的问题:返回未初始化的指针返回指向无效地址的指针返回局部变量的指针返回指针但没有释放内存(使用malloc在函数内部分配内存并返回地址,调用者负责释放返回的内存。)传递指针的指针:将指针传递给函数时,传递的是值。如果想要修改原指针而不是指针的副本,就需要传递指针的指针。同传递值一样,只传递一个指针是不能改变其值的,相当于改变了形参。示例4:voidallocArray(int**arr,intsize,intvalue)(*arr=(int*)malloc(size*sizeof(int));If(*arr!=NULL)(for(inti=0;i<size;i++)(*(*arr+1)=value;}}}Int*vector=NULL;allocArray(&vector,5,45);函数指针:指持有函数地址的指针,这为我们以编译时未确定的顺序执行函数提供了一种选择,而不需要使用条件语句。声明示例:void(*fo)(int);调用时直接将函数名传递给函数指针。传递函数指针示例:示例5:intadd(intnum1,intnum2)(returnnum1+num2;}Intsubstract(intnum1,intnum2)(returnnum1-num2;}Typedefint(*fptrOperation)(int,int);Intcompute(fptrOperationoperation,intnum1,intnum2)(returnoperation(num1,num2)}Printf("%d\n”,compute(add,5,6));Printf("%d\n”,compute(substract,5,6);返回函数指针:返回函数指针需要把函数的返回类型声明为函数指针。示例6:fptrOperationselect(charopcode)(switch(opcode)(case‘+’:returnadd;Case‘-‘:returnsubstract;}}Intevaluate(charopcode,intnum1,intnum2)(fptrOperationoperation=select(opcode);Returnoperation(num1,num2);Printf("%d\n”,evaluate(‘+’,5,6));Printf("%d\n,evaluate(‘-‘,5,6));指针和数组:数组和指针表示法紧密关联,在合适的上下文可以互换。尽管数组名字有时候可以当作指针来使用,但数组的名字不是指针。数组表示法和指针可以一起使用,但两者明显不同也不一定能互换,比如说,尽管数组使用自身的名字可以返回数组地址,但是名字本身不能作为赋值操作的目标。注:1.数组是能用索引访问的同质元素连续集合,连续是指数组中的元素在内存中是相邻的,中间不存在空隙。可以将二维数组当作数组的数组,即,如果只用一个下标访问数组,得到的是对应行的指针。使用sizeof得到的是整行的长度。将数组名作为地址赋值给指针,是把数组第一个元素的地址赋值给指针,即,指针指向数组的第一个元素而不是数组本身。Sizeof对数组和同一数组的指针操作是不同的,前者返回整个数组的大小,后者返回指针本身的大小。指针是一个左值,能修改,而数组名字不能被修改。如果从堆上(malloc)分配内存,并把地址赋给一个指针,那就可以对指针使用数组下标,并把这块内存当作一个数组。不过用完记得释放。指针数组和数组指针:Int*arr[5];和int(*arr)[5];是不同的,前者为指针数组,数组的每一个元素都为一个指针,存放的是地址值。后者为数组指针,arr为一个指向二维数组的指针,该二维数组的元素都是整数,且每行有5个元素,可以类比于函数指针,需要将二维数组的地址传给arr。示例7:intmatrix[2][5]={{1,2,3,4,5},{6,7,8,9,10}};Int(*ptrmatrix)[5]=matrix;注:这里matrix代表的是二维数组的首地址,matrix[0]也代表首地址,但matrix+1和matrix[0]+1代表的地址不同。前者表示加了20,后者表示加了4.传递多维数组:给函数传递多维数组时,要决定在函数原型声明中使用数组表示法还是指针表示法,还要考虑如何传递数组的形态,这里的形态指数组的维数及每一维的大小。示例8:voiddisplay2DArray(intarr[][5],introws){};或者voiddisplay2DArray(int(*arr)[5],introws){};调用时,display2DArray(matrix,2);注:函数不会为这个数组分配内存,因为传递的是地址。动态分配二维数组:像上面的matrix,其声明方式决定了其所分配的内存是连续的,但使用malloc这样的函数创建二维数组时,分配的内存可能不连续。示例9:introws=2;Intcols=5;Int**matrix=(int**)malloc(rows*sizeof(int*));For(inti=0;i<rows;i++)(matrix[i]=(int*)malloc(cols*sizeof(int));}示例9这种方式分配的内存就不连续。示例10:introws=2;Intcols=5;Int**matrix=(int**)malloc(rows*sizeof(int*));Matrix[0]=(int*)malloc(rows*cols*sizeof(int));For(inti=1;i<rows;i++)(matrix[i]=matrix[0]+cols*I;}示例10这种方式分配的就是连续的内存。指针与字符串字符串是以ASCII字符NUL(\0)结尾的字符序列,通常存储在数组或从堆上分配的内存中。字符串有两种类型,一种是由char数据类型组成的单字节字符串,一种是由wchar_t数据类型组成的宽字符串(16位或32位,主要用于支持非拉丁字符集),处理函数分别包含在string.h和wchar.h头文件中。注:1.字符串长度指的是字符串中除了NUL字符之外的字符数,而分配内存时也要为NUL字符分配。为了防止字符串被修改,可将其声明为常量指针,即constchar*p="sound”。指针与结构体结构体的定义使用struct关键字,通常结构体名的前面加上下划线。,访问结构体成员用点(•)操作符,当使用结构体指针时,使用箭头(T)操作符,当然也可以先将指针解引*)后使用点操作符。结构体的声明通常使用typedef关键字,如下:typedefstruct_person(char*fisrtName;char*lastName;char*title;unsignedintage;}Person;为结构体分配内存为结构体分配内存时,分配的内存大小至少是各个字段的长度和,不过实际长度通常会大于这个和,因为结构体各字段之间可能会有填充,某些数据类型需要对齐到特定边界就会产生填充。比如说,短整数通常对齐到能被2整除的地址上,而整数对齐到能被4整除的地址上。详解字节对齐:编译器默认会对结构体进行字节对齐处理,目的是加快计算机取数速度。三准则:结构体变量的首地址能够被其最宽基本类型(内置数据类型,如char,int,short,double,float)成员的大小所整除。结构体每个成员相对于结构体首地址偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间填充字节(internalpadding)结构体总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailingpadding)注:1.结构体某个成员相对于结构体首地址的偏移量可以通过offsetof()宏来获得,如offsetof(_person,firstName);由于结构体成员可以是复合类型,如另一个结构体,所以在寻找最宽基本类型成员时,应当包含复合数据类型的子成员,而不是看成一个整体。但在确定复合类型的偏移量是则是看作一个整体。示例11:struct_S1{inti;charc;}struct_S2{charc1;_S1s;charc2;}则sizeof(_S2)=4+(4+4)+4=16.//以4的整数倍对齐再如:struct_S3(inti;charc;shorts;}则sizeof(_S3)=4+2+2=8;〃由规则1和规则2可得出可以通过#pragmapack(n)指令调整结构体对齐方式,其中n为字节对齐数(取1,2,4,8,16)示例12:#pragmapack(push)//将当前pack值压栈保存#pragmapack(2)struct_S1(inti;charc;}struct_S2(charc1;_S1s;charc2;}#pragmapack(pop)//恢复之前的pack值此时sizeof(_S2)=2+(2+4)+2=10。空的结构体大小为1。结构体包含静态成员变量时,结构体大小不包括该静态变量。结构体位域的概念位域的主要目的是压缩存储规则如下:如果相邻位域字段的类型相同,且其位宽之和小于数据类型的sizeof大小,则后面的字段紧邻前一个字段存储,直到不能容纳为止。如果相邻位域字段的类型相同,但其位宽之和大于数据类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍。如果相邻的位域字段类型不同,则不压缩如过位域指端之间穿插非位域字段,则不进行压缩。整个结构体的总的宽度为最宽基本数据类型的整数倍如:示例13struct_t(chart1:3;chart2:4;chart3:5;shorts;chart4:3}则sizeof(_t)=1+1+2+2=6指针误用不恰当的指针声明,如int*ptr1,ptr2;实际上只有ptr1为指针,正确的声明方式应为int*ptr1,*ptr2。不过可以可以采用下面的类型定义方式:typedefint*pint;pintptr1,ptr2;注:这里要区分#define和typedef,前者为宏定义,在编译预处理时进行简单的替换,而后者不是简单的替换,而是采用如同定义变量的方法那样来声明一种类型。因此:#definePINTint*PINTptr1,ptr2;〃同样只有ptr1为指针另外,对于constPINTp;和constpintp;前者为指针常量(指向的内容不可变,后则为常量指针(指针本身不可多次赋值。因为pint作为数据类型把p锁定了。使用指针前为初始化,即野指针。使用malloc这类函数时,一定要检查返回值,否则可能导致程序非正常终止。示例14:float*vector=(float*)malloc(20*sizeof(float));if(vector==NULL)(}else(}一旦不再需要内存中的敏感苏剧,马上覆写是个好主意(使用memset)为了避免重复释放指针,在释放过后总是将其置为NULL。转换指针类型转换是一种基本操作,跟指针结合使用很有用,原因包括:访问有特殊目的的地址一般发生在嵌入式系统上,可以把某特殊地址赋值给一个指针,再把某个字符赋值给这个地址。如:#defineVIDEO_BASE0xB8000int*video=(int*)VIDEO_BASE;*video=’A’;分配一个地址来表示端口端口即是硬件概念,也是软件概念。服务器用软件端口指明它们要接收发给这台机器的某类消息,硬件端口就是物理端口,程序通过读写硬件端口可以处理信息和命令。示例15:#definePORT0xB0000000unsignedintvolatile*constport=(unsignedint*)PORT;注:a.这里的volatile关键字修饰符可以译为“直接存取原始内存地址”,表示可以在程序以外改变变量,比如说,外部设备可能会向端口写入数据,且可以独立于计算机的处理器执行这个写操作,也有可能在多线程程序中共享同一变量的其它线程中发生改变的情形。出于优化的目的,编译器有时候会临时使用缓存或者寄存器来持有内存的值,如果外部操作修改了这个内存的值,改动不能反映到缓存或者寄存器中。用volatile可以阻止运行时系统使用寄存器暂存端口值,每次访问端口都需要系统读写端口,而不是从寄存器中读取一个可能已经过期的值。但是不应该把所有变量都声明为volatile,这样会组织编译器进行所有类型的优化。示例16:intsquare(volatileint*ptr)(return*ptr**ptr;}上段代码的本意是计算ptr指向内容的平方,但由于ptr被volatile修饰,可能计算过程中发生改变,得到的就不是平方值了。又如volatilechara;a=0;while(!a)(//dosomethings;}doother();如果没有volatiledoother()不会被执行b.一个变量可以同时为const和volatile,它是volatile因为
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 企业员工健康管理中的感染防控措施实施
- 语文教学计划中的情境创设
- 煤炭行业品牌形象建设与传播考核试卷
- 一般约定合同当事人及其他相关方张孟博课
- 小学《道德与法治》二年级上册社会实践计划
- 旅游行业突发事件处理措施与应急方案
- 建筑工程进度安排及质量保证措施
- 呼叫中心客户服务团队建设与管理考核试卷
- 高二年级组下学期学生素质拓展计划
- 医疗器械安全性分析与改进措施
- HPE-DL380-Gen10-服务器用户手册
- Unit6Beautifullandscapes-Reading教学设计译林版七年级英语下册
- 金刚石行业分析报告
- 中间人协议书范本(2025年)
- 2024版家庭资产配置与财富传承规划合同3篇
- 2025届高考地理 二轮复习课件-专题35 生态脆弱区的综合治理
- 2024-2030年全球及中国石榴花提取物行业发展动态及供需前景预测报告
- 桥隧建筑物安全监控相关知79课件讲解
- 幼儿园体育游戏对幼儿社交能力的影响
- 九下 化学 科学 第七单元 跨学科实践活动:海洋资源的综合利用与制盐
- 《STP营销战略概述》课件
评论
0/150
提交评论