第五章3指针(11--33)
⼗⼀指针的运算
1、作为⼀种特殊的变量,指针可以进⾏⼀些运算,但并⾮所有的运算都是合法的,指针的运算主要局限在加减算术和其他⼀些为数不多的特殊运算。
2、把a的值5作为地址 0x00000005赋值给*p是发⽣访问冲突。整数与指针最好不要直接运算。
3、地址的赋值和指针的赋值。
num,p1,p2 他们中⼀个改变,其他的两个都会跟着改变
4、 a被编译器解析为数组的⾸地址
通过下标循环进⾏遍历
通过指针循环进⾏遍历
5、通过dll注⼊的⽅式修改另⼀个进程的数据
通过定义并初始化⼆级指针p,使其改变另⼀个程序的指针p的指向,从⽽改变它的值。
外挂⼯具:cheat engine
注意不会实时刷新
⼗⼆指针的算数运算
1、使⽤递增/递减运算符(++ 和 --)将指针递增或递减
指针++就是按照指针类型的⼤⼩,前进⼀个类型的⼤⼩,int,前进四个字节
指针 ++ 和 -- 只有在数组的内部才有意义。
2、指针++ 就是指针每次向前移动sizeof(指针类型)个字节
通过指针循环的⽅式初始化数组a的每⼀个元素
(从头到尾扫描数组)
(注:格式控制符“%p”中的p是pointer(指针)的缩写。指针的值是语⾔实现(编译程序)相关的,但⼏乎所有实现中,指针的值都是⼀个表⽰地址空间中某个存储器单元的整数。printf函数族中对于%p⼀般以⼗六进制整数⽅式输出指针的值,附加前缀0x。)
3、指针加上2,在数组内部等价于向后移动两个元素的⼤⼩
指针减去3,等价于数组内部,向前移动3个元素的⼤⼩
此时此刻,就会打印出 3 5 2
4、指针的加减法在⾮数组内部没有任何意义,⽽且很容易越界报错
⼀个exe不能读写其他exe进程的内存。
⼗三指针之间的⽐较
1、对两个毫⽆关联的指针⽐较⼤⼩是没有意义的,因为指针只代表了“位置”这么⼀个信息,但是,如果两个指针所指向的元素位于同⼀个数组(或同⼀块动态申请的内存中),指针的⼤⼩⽐较反映了元素在数组中的先后关系。
2、通过⽐较两个指针的地址,来判断哪个指针的位置考前还是靠后
地址的⽐较没有意义,只能判断谁的地址也就是内存的编号⽐较靠前
3、通过指针指向数组中的元素,来⽐较数组元素的地址哪个靠前
4、指针是否相等,可以判断是否指向同⼀地址
⼗四指针运算
1、若有p指向数组a,则:
2、 a是⼀个数组
p=p+1;
⼗五指针相减
1、指针变量所⽀持的另⼀种运算⽅式是两个同类型指针相减,返回值是个有符号整数
2、举例来说,指针p1指向sz[i],指针p2指向sz[j],那么p1-p2=i-j,两个指针的距离并不是其值简单做差,还要除以“指针所指类型占⽤的内存字节数”。
3、指针相减,如果值为正,p1在p2后⾯。值为负,p1在p2前⾯
具体之差就意味着指针之间相隔⼏个元素的⼤⼩
具体之差不是地址之差,⽽是地址之差除以指针指向元素的⼤⼩
指针存储的值与指针指向的数据没有任何关系
4、数组之间可以判断谁的编号在前,谁的编号在后,并且可以判断两个指针之间相隔多少个元素。具体之差不是地址之差,⽽是地址之差除以指针指向元素的⼤⼩。指针相减,可以判断下标。p1<p2,p1对应元素的下标⼩于p2对应元素的下标。
⼗六指针与数组
1、指针与数组
指向数组元素的指针变量
数组名是表⽰数组⾸地址的地址常量
注意:数组名a不代表整个数组,只代表数组⾸元素的地址。
“p=a;”的作⽤是“把a数组的⾸元素的地址赋给指针变量p”,
⽽不是“把数组a各元素的值赋给p”。
2、
数组名num就是数组第⼀个元素的⾸地址
num是⼀个指向int 类型的常量指针
num=0x123234492; 常量不可以赋值不可以放在赋值号的左边。
⼗七指向元素指针与指向数组的指针
1、指向元素的指针与指向数组的指针
运⾏结果:
⼗⼋指针引⽤多维数组
1、
2、对于⼆维数组
3、⼆维数组名是指向⾏的,它不能对如下说明的指针变量p直接赋值:
⼗九指针遍历输出⼆维数组的值
1、有⼀个3×4的⼆维数组,要求⽤指向元素的指针变量输出⼆维数组各元素的值。
⼆维数组的元素是整型的,它相当于整型变量,可以⽤int*型指针变量指向它
⼆维数组的元素在内存中是按⾏顺序存放的,即存放完序号为0的⾏中的全部元素后,接着存放序号为1的⾏中的全部元素,依此类推
因此可以⽤⼀个指向整型元素的指针变量,依次指向各个元素
线性的⽅式循环⼆维数组:
嵌套for循环的⽅式变量⼆维数组:
⼆⼗指针的⽅式输出⼆维数组任意元素
1、指针的⽅式输出⼆维数组任⼀⾏任⼀列元素的值
定义⼀个⼆维数组a[3][4]
⼆⼗⼀数组作为函数参数
1、⽤指向数组的指针作函数参数
⼀维数组名可以作为函数参数,多维数组名也可作函数参数。
⽤指针变量作形参,以接受实参数组名传递来的地址。
可以有两种⽅法:
①⼀维数组⽤指向变量的指针变量
②⼆维数组⽤指向⼀维数组的指针变量
(1)⼀维数组⽤指向变量的指针变量
int a[10] 数组作为函数参数,传递的是地址,地址就是指针占4个字节,
函数的参数对于数组没有副本机制,为了节约内存,拷贝数组浪费空间与CPU。
在main函数中调⽤test,将会打印出4
指针变量作为⼀维数组的参数
(2)⼆维数组⽤指向⼀维数组的指针变量
实参数组名是指针常量,但形参数组名是按指针变量处理
2、将数组a中n个整数按相反顺序存放
sizeof 指针数组作为函数参数,则数组没有副本机制,等价于直接操作原⽣数组
3、⽤指针⽅法对10个整数按由⼤到⼩顺序排序。
解题思路:
在主函数中定义数组a存放10个整数,定义int *型指针变量p指向a[0]
定义函数sort使数组a中的元素按由⼤到⼩的顺序排列
在主函数中调⽤sort函数,⽤指针p作实参
⽤冒泡进⾏排序
4、有⼀个班,3个学⽣,各学4门课,计算总平均分数以及第n个学⽣的成绩。
解题思路:这个题⽬是很简单的。本例⽤指向数组的指针作函数参数。⽤函数average求总平均成绩,⽤函数search出并输出第i个学⽣的成绩。
求平均值:
第⼀个⽤指向数组的指针存储⼆维数组名,传递多少个元素(每个元素⼜是⼀个⼀维数组)
显⽰出第n个学⽣的成绩
5、有⼀个班,3个学⽣,各学4门课,查有⼀门以上课程不及格的学⽣,输出他们的全部课程的成绩。
解题思路:在主函数中定义⼆维数组score,定义search函数实现输出有⼀门以上课程不及格的学⽣的全部课程的成绩,形参p的类型是float(*)[4]。在调⽤search函数时,⽤score作为实参,把score[0]的地址传给形参p。
变量所以元素,有不及格的就跳出循环,然后打印出来
检测到不及格的就打印出来
⼆⼗⼆函数指针概念
1、如果在程序中定义了⼀个函数,在编译时,编译系统为函数代码分配⼀段存储空间,这段存储空间的起始地址,称为这个函数的指针。
(1)
定义⼀个函数msg
定义函数的指针p,使他指向函数名msg,
直接调⽤p();就会执⾏函数msg。
(2)
定义函数的指针p,使他指向函数名add,
直接调⽤add(1,10);就会执⾏函数add。
⼆⼗三函数返回值是指针
1、⼀个函数可以返回⼀个整型值、字符值、实型值等,也可以返回指针型的数据,即地址。其概念与以前类似,只是返回的值的类型是指针类型⽽已
定义返回指针值的函数的⼀般形式为
类型名 *函数名(参数表列);
定义全局变量
2、⽤随机数⽣成⼀个数组,写⼀个函数查最⼩的数,并返回最⼩数的地址。在主函数中打印出来最⼩数。
函数返回⼀个地址,对于数组⽽⾔,函数参数调⽤没有副本机制,为了节约内存
⼆⼗四函数返回值是指针练习--⽤途
1、实现字符串的拷贝
返回拷贝好的字符串地址,进⾏拷贝字符串
2、⾃⼰实现字符串的拷贝
返回拷贝好的字符串地址
上⾯的while循环可以拆分成如下的代码
⼆⼗五指针左值指针与整数指针空指针以及指向为空的指针
1、左值的概念, “可放在赋值号左边的都可称为左值”
2、空指针
void *指针是⼀种特殊的指针,不指向任何类型的数据,如果需要取出,⽤此地址指向某类型的数据,应先对地址进⾏类型转换。
⼆⼗六 Void指针与空指针--详细讲解
1、空类型指针可以指向任何类型的数据,包含他们的地址
2、任何指针都可以赋值给空类型的指针,⽤于保存地址
3、memset函数
从数组str的⾸地址开始,前进 5个字节,进⾏赋值,赋值⼀个字符 ’ A ’ 。
memset(str, ’A’ ,5) ;
对20个字节全部赋值为0,对于数组清零(num是数组)
memset(num, 0 ,20);
遍历数组num的所以元素,元素值都为0。
4、malloc函数
malloc的全称是memory allocation,中⽂叫动态内存分配,当⽆法知道内存具体位置的时候,想要绑定真正的内存空间,就需要⽤到动态的分配内存。原型为extern void
5、空类型指针可以转换为任何类型的指针
⼀个类型的指针包含三个信息:
地址、步长、内容如何解析
⼆⼗七动态分配
1、数组只能处理⼩数据
例如:定义数组 int a[1024102410] ; 运⾏报错
2、动态分配,是指⽤户可以在程序运⾏期间根据需要申请或释放内存,⼤⼩也完全可控。动态分配不像数组内存那样需要预先分配空间,⽽是由系统根据程序需要动态分配,⼤⼩完全按照⽤户的要求来,当使⽤完毕后,⽤户还可释放所申请的动态内存,由系统回收,以备他⽤。
malloc和free是C标准库中提供的两个函数,⽤以动态申请和释放内存
malloc()函数的基本调⽤格式为:
void *malloc( unsigned int size );
参数size是个⽆符号整型数,⽤户由此控制申请内存的⼤⼩,执⾏成功时,系统会为程序开辟⼀块⼤⼩为size个内存字节的区域,并将该区域的⾸地址返回,⽤户可利⽤该地址管理并使⽤该块内存,如果申请失败(⽐如内存⼤⼩不够⽤),返回空指针NULL。
在栈区开辟⼀段内存,系统会⾃⼰回收。
在堆区开辟⼀段内存,需要⾃⼰⼿动⽤free释放内存
开辟内存后⽴即释放掉
free就是释放内存,例如free(p)
malloc()函数返回类型是void*,⽤其返回值对其他类型指针赋值时,必须进⾏显式转换。
⼆⼗⼋ free函数注意事项
1、malloc函数和free函数在stdlib.h头⽂件⾥⾯
2、蚕⾷法消耗内存
⼆⼗九 malloc_calloc_realloc
1、C语⾔标准库函数还提供了calloc函数⽤以动态申请内存,和malloc函数以字节为单位申请内存不同,calloc函数是以⽬标对象为单位分配的,⽬标对象可以是数组,也可以是后⾯会讲到的结构体等。
2、 calloc会⾃动将内存初始化为0,malloc就不会
int num;
scanf("%d",&num);
(1)malloc函数
//动态数组,输⼊19,就有19个元素,初始化
如果想对该数组进⾏扩充,就⽤到了realloc函数
addnum增加的个数
三⼗内存分配习题以及⼩结
1、建⽴动态数组,输⼊5个学⽣的成绩,另外⽤⼀个函放数检查其中有⽆低于60分的,输出不合格的成绩。
解题思路:
⽤malloc函数开辟⼀个动态⾃由区域,⽤来存5个学⽣的成绩,会得到这个动态域第⼀个字节的地址,它的基类型是void型。⽤⼀个基类型为int的指针变量p来指向动态数组的各元素,并输出它们的值。但必须先把malloc函数返回的void指针转换为整型指针,然后赋给p
通过malloc初始化5个学⽣的成绩
2、 free(p); //释放内存
printf(“p=%p\n”,p); //释放内存以后,指针的值并不发⽣变化
printf(“%d\n”,p[2]); //释放内存以后,如果再次引⽤指针,就会出现垃圾数值
因此,释放内存以后,指针应该赋值为空,(p=NULL)。
释放内存之后,不能再次释放内存
⼩总结:
内存不可以反复释放,内存释放之后不可以引⽤否则会出现垃圾数据,
内存释放以后,指针应该赋值为空,就可以规避再次引⽤,以及反复释放的问题。
3、内存泄露
如果没有释放内存,但记录该块内存的指针消亡了或者是指针的值发⽣了改变,这块内存将永远得不到回收,造成了内存泄漏
三⼗⼀课后习题植物⼤战僵⼫
1、游戏的阳光值为670.
三⼗⼆深⼊指针迷途指针
迷途指针
定义⼀个指针p,通过malloc函数为它分配内存
然后释放内存free(p);
//应该在此处让p指向NULL,也就是p=NULL.
p释放了以后,仍然指向这⽚内存,就是迷途指针
三⼗三指针代码实践
1、记事本能够通过如下两⾏代码编译,但是VS则不能通过编译,
因为指针变量p没有初始化。
2、通过指针的⽅式交换两个变量的值
3、把a作为⼀个实参传递给函数change,并不能改变实参a的值。
通过传值的⽅式传递给change函数的形参,将会在栈区中开辟⼀段内存,
(单向传递,只能接受)。
传递a的地址给changep函数,在函数中给形参重新赋值以后,实参的值发⽣改变。也不会开辟新的内存空间,(双向传递,既接受了地址,也能通过地址改变变量)。
4、定义⼀个函数没能实现交换两个变量的值(通过传值的⽅式,并没能实现交换的功能)
传⼊的是实参的地址和形参的地址并不相同,传⼊之后,仅仅是在栈区新开辟了两段内存,函数执⾏完后,并没能把值传递给实参,传值的⽅式是单向传递。
5、通过传递地址的⽅式交换两个实参变量的值。
这⾥的形参pa,pb分别和实参的两个参数⼀⼀对应指向同⼀块内存,
改变了实参的值。(双向传递)
6、如果创建指针没有进⾏初始化就会编译出错。
创建指针t ,并没有初始化t,⽽是通过间接访问的⽅式把pa所指向的值付给了*t

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。