━━指针与函数、指针与堆内存.ppt_第1页
━━指针与函数、指针与堆内存.ppt_第2页
━━指针与函数、指针与堆内存.ppt_第3页
━━指针与函数、指针与堆内存.ppt_第4页
━━指针与函数、指针与堆内存.ppt_第5页
已阅读5页,还剩27页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

C+程序设计,第4章(3) 指针与函数、 指针与堆内存,主要内容,指针作为函数参数 指针作为函数返回值 指向函数的指针 C+的四个内存区域 动态存储分配 堆内存 new 与 delete 运算符 动态存储分配的几点说明 使用 new 和 delete 的几点说明 常指针 指向常量的指针 类型标识符的定义,指针作为函数参数,作用:指针作为函数参数,可以使主调函数与被调函数之间共享变量或对象,以实现函数之间数据的双向传递。 地址传递: 若某函数的一个形参定义为指针类型,则调用该函数时其对应的实参可以是: 相同类型的变量(或对象)的地址 相同类型的指针变量 相同类型的数组名 由于实参是一个地址,将该地址传递给了对应的形参指针变量,此时形参指针变量与实参指针指向的是同一个内存区域的变量(或对象),因此若在被调函数中修改了形参指针变量所指向的数据,也就修改了主调函数中实参指针所指向的数据。,【例】( 值传递、引用传递、地址传递 函数的参数三种传递方式的比较 ) #include void swap1 ( int p1 , int p2 ) int temp = p1 ; p1 = p2 ; p2 = temp ; void swap2 ( int ,【例】(形参为指针类型,实参可以是相同类型的数组名) #include void sort1 ( int *p , int n ) /对p指针所指向的n个元素的数组按升序排序 int i , j , t ; for ( i=0 ; i *( p+j ) ) t = *( p+i ) ; *( p+i ) = *( p+j ) ; *( p+j ) = t ; void sort2 ( int *p , int n ) /对p指针所指向的n个元素的数组按升序排序 int i , j , t ; for ( i=0 ; i p j ) t = p i ; p i = p j ; p j = t ; void sort3 ( int p , int n ) /对n个元素的p数组按升序排序 int i , j , t ; for ( i=0 ; i p j ) t = p i ; p i = p j ; p j = t ; ,void main ( ) int a 6 = 43 , 81 , 32 , 11 , 63 , 10 ; int b 6 = 43 , 81 , 32 , 11 , 63 , 10 ; int c 6 = 43 , 81 , 32 , 11 , 63 , 10 ; int d 6 = 43 , 81 , 32 , 11 , 63 , 10 ; int i ; sort1( a , 6 ) ; for ( i=0 ; i6 ; i+ ) cout a i “t” ; cout endl ; sort2( b , 6 ) ; for ( i=0 ; i6 ; i+ ) cout b i “t” ; cout endl ; sort3( c , 6 ) ; for ( i=0 ; i6 ; i+ ) cout c i “t” ; cout endl ; int *point = d ; sort2( point , 6 ) ; for ( i=0 ; i6 ; i+ ) cout d i “t” ; cout endl ; ,运行: 11 32 43 63 81 10 11 32 43 63 81 10 11 32 43 63 81 10 11 32 43 63 81,【例】(设计通用的两矩阵相乘的函数) 分析:a为mn矩阵,b为np矩阵,a乘以b得到c矩阵,则c为mp的矩阵。 k=n-1 矩阵相乘公式: c ij = a ik b kj k=0 #include void matrixmul ( float *pa , float *pb , float *pc , int m , int n , int p ) int i , j , k ; float t ; for ( i=0 ; im ; i+ ) for ( j=0 ; jp ; j+ ) t = 0 ; for ( k=0 ; kn ; k+ ) t += ( *( pa + i*n + k ) ) * ( *( pb + k*p + j ) ) ; ( *( pc + i*p + j ) ) = t ; void main ( ) float a 2 3 = 1, 2, 3 , 4, 5, 6 ; float b 3 5 = 1, 2, 3, 4, 5 , 6, 7, 8, 9, 6 , 5, 4, 3, 2, 1 ; float c 2 5 ; matrixmul ( *a , *b , *c , 2 , 3 , 5 ) ; int i , j ; for ( i=0 ; i2 ; i+ ) for ( j=0 ; j5 ; j+ ) cout c i j “t” ; cout endl ; ,运行: 28 28 28 28 20 64 67 70 73 56,【例】(求二维数组的平均值) #include float average1 ( float *p , int n ) /形参p为元素指针 float sum=0 ; int i ; for ( i=0 ; in ; i+ ) sum += *p+ ; return sum / n ; float average2 ( float p 5 , int n ) /形参p为二维数组 float sum=0 ; int i , j ; for ( i=0 ; in ; i+ ) for ( j=0 ; j5 ; j+ ) sum += p i j ; return sum / (n*5) ; float average3 ( float (*p)5 , int n ) /形参p为行指针 float sum=0 ; int i , j ; for ( i=0 ; in ; i+ ) for ( j=0 ; j5 ; j+ ) sum += (*p) j ; p+ ; return sum / (n*5) ; void main ( ) float a 3 5 = 1, 2, 3, 4, 5 , 6, 7, 8, 9, 10 , 11, 12, 13, 14, 15 ; cout “平均值=” average1( *a , 15 ) endl ; cout “平均值=” average2( a , 3 ) endl ; cout “平均值=” average3( a , 3 ) endl ; ,运行: 平均值=8 平均值=8 平均值=8,指针作为函数返回值,作用:任一函数,利用return语句可以返回一个值,当函数的返回值为指针时,返回的必须是一个指向已定义数据的指针,且只能返回全局变量或静态变量的指针,不能返回局部变量的指针。 返回值为指针的函数的定义: 类型 * 函数名 ( 形参列表 ) 函数体 【例】(实现字符串逆序的函数) #include char * flip ( char *p ) char *p1 , *p2 , temp ; p1= p2 = p ; while ( *p2+ ) ; p2 -= 2 ; while ( p1 p2 ) temp = *p2 ; *p2- = *p1 ; *p1+ = temp ; return p ; void main ( ) char s100 ; cout “请输入一行字符:” ; cin.getline( s , 100 ) ; cout “逆序后:” flip( s ) endl ; cout “逆序后:” s endl ; ,运行: 请输入一行字符:123456ABCDEFGH 逆序后:HGFEDCBA654321 逆序后:HGFEDCBA654321,【例】(用递归函数实现字符串的逆序) #include /用递归函数实现字符串的逆序 char * flip1 ( char *p , int n ) /对从p所指的字符开始的后面 n 个字符实现逆序 char t ; if ( n 1 ) t = p 0 ; p 0 = p n-1 ; p n-1 = t ; flip1 ( p+1 , n-2 ) ; return p ; /用递归函数实现字符串的逆序输出 void flip2 ( char *p , int i ) /对p所指的字符串从第i个字符开始的字符串逆序输出 if ( p i != 0 ) flip2 ( p , i+1 ) ; cout p i ; void main ( ) char s 100 , *p = s ; int n = 0 ; cout “请输入一行字符:” ; cin.getline( s , 100 ) ; while ( *p+ ) n+ ; flip1( s , n ) ; cout “逆序后s=” s endl ; cout “还原后s=” flip1( s , n ) endl ; cout “逆序输出:” ; flip2( s , 0 ) ; cout endl ; ,运行: 请输入一行字符:123456ABCDEFGH 逆序后s = HGFEDCBA654321 还原后s = 123456ABCDEFGH 逆序输出:HGFEDCBA654321,【例】(实现字符串拷贝的函数) #include char * copy ( char * to , char * from ) char *p = to ; while ( * to + = * from + ) ; return p ; void main ( ) char s1 20 , s2 20 ; cout “请输入一行字符:” ; cin.getline( s1 , 20 ) ; cout “拷贝前 s2 = ” s2 endl ; copy( s2 , s1 ) ; cout “第1次拷贝后:” s2 endl ; copy( s2 , “ABCDEFG” ) ; cout “第2次拷贝后:” s2 endl ; ,运行: 请输入一行字符:123456 拷贝前 s2 = 烫烫烫烫烫烫烫烫烫烫123456 第1次拷贝后: 123456 第2次拷贝后: ABCDEFG,【例】(实现字符串拼接的函数) #include char * stringcat ( char * to , char * from ) char *p = to ; while ( * to + ) ; to - ; while ( * to + = * from + ) ; return p ; void main ( ) char s1 20 , s2 20 ; cout “请输入第一个字符串:” ; cin.getline( s1 , 20 ) ; cout “请输入第二个字符串:” ; cin.getline( s2 , 20 ) ; cout “拼接前: s1 = ” s1 “ts2 = ” s2 endl ; stringcat( s1 , s2 ) ; cout “拼接后: s1 = ” s1 “ts2 = ” s2 endl ; ,运行: 请输入第一个字符串:123456 请输入第二个字符串:ABCDEFG 拼接前: s1=123456 s2=ABCDEFG 拼接后: s1=123456ABCDEFG s2=ABCDEFG,指向函数的指针,函数的指针:函数的代码存储在内存中,其起始地址称为函数的入口地址,其函数名代表该函数的入口地址,可以定义一个指向函数的指针变量来指向它,即将函数名赋给该指针变量,这样就可以通过该指针变量来调用该函数。 指向函数的指针变量的定义: 返回值类型 ( * 指针变量名 ) ( 形参列表表 ); 对指向函数的指针变量的赋值: 指针变量 = 函数名 注意:指向函数的指针变量只能指向与其具有相同返回值类型且相同参数(参数类型、个数、顺序均一致)的这一类函数。 通过指向函数的指针变量来调用函数: 格式1: ( * 指针变量 ) ( 实参列表 ) 格式2: 指针变量 ( 实参列表 ),指向函数的指针,【例】 float s1 ( int x , int y ) ; float s2 ( int a , int b ) ; int s3 ( int a , int b , float c ) ; float ( * f1 ) ( int , int ) ; int ( * f2 ) ( int , int , float ) ; f1 = s1 ; 这样 f1( 3 , 5 ) ; 就等价于 s1( 3 , 5 ) ; f1 = s2 ; 这样 f1( 3 , 5 ) ; 就等价于 s2( 3 , 5 ) ; f1 = s3 ; f2 = s3 ; 这样 f2( 3 , 5 , 7 ) ; 就等价于 s3( 3 , 5 , 7 ) ; 指向函数的指针常用来作为函数的参数:那么在调用该函数时,其对应的实参只要是与该形参指针变量同类型的函数名即可,这样在多次调用该函数的过程中,可实现再调用不同功能的函数(由对应的实参确定),从而大大增加程序的灵活性。,【例】(分别实现两个操作数的加、减、乘、除运算的函数) #include float add ( float x , float y ) cout a c b ; switch ( c ) case + : p = add ; break ; case - : p = sub ; break ; case * : p = mul ; break ; case / : p = div ; break ; default : cout “运算式输入错误!n” ; return ; cout p ( a , b ) endl ; / 也可写成: (*p) ( a , b ) ,【例】(分别实现求一维数组的各元素和、最大元素、各元素平均值的函数) #include float sum ( float *p , int n ) float s = 0 ; for ( int i=0 ; i m ) m = *p ; p+ ; return m ; float ave ( float *p , int n ) return sum( p , n ) / n ; void process ( float *p , int n , float ( *fp ) ( float * , int ) ) cout fp( p , n ) endl ; void main ( ) float x 8 = 2 , 8 , 5 , 7, 6 , 3 , 9 , 4 ; cout “元素和=” ; process( x , 8 , sum ) ; cout “最大值=” ; process( x , 8 , max ) ; cout “平均值=” ; process( x , 8 , ave ) ; ,运行: 元素和=44 最大值=9 平均值=5.5,【例】(用梯形法求下列三个定积分的近似值) area1 = 42 ( 1 + x2 ) dx area2 = 2.51 x / ( 1 + x2 ) dx area3 = 31 ( x + x2 ) / ( 1 + sinx + x2 ) dx 分析:用梯形法求定积分 area = ba f ( x ) dx 的近似值的通用公式为: i=n-1 area = ( f ( a ) + f ( b ) ) / 2 + f ( a + i * h ) h i=1 其中:a 和 b 分别为积分的下限和上限;n 为积分区间的分隔数; h = ( b-a ) / n ,h为积分步长;f ( x ) 为被积函数。 #include #include float f1 ( float x ) return ( 1 + x * x ) ; float f2 ( float x ) return x / ( 1 + x * x ) ; float f3 ( float x ) return ( x + x * x ) / ( 1 + sin(x) + x * x ) ; ,float jifen ( float ( *f ) ( float ) , float a , float b , int n ) float y , h ; y = ( f( a ) + f( b ) ) / 2 ; h = ( b - a ) / n ; for ( int i=1 ; in ; i+ ) y += f( a + i * h ) ; return ( y * h ) ; void main ( ) cout “第一个积分值=” ; cout jifen ( f1 , 2 , 4 , 1000 ) endl ; cout “第二个积分值=” ; cout jifen ( f2 , 1 , 2.5 , 1000 ) endl ; cout “第三个积分值=” ; cout jifen ( f3 , 1 , 3 , 1000 ) endl ; ,运行: 第一个积分值 = 20.6667 第二个积分值 = 0.643927 第三个积分值 = 1.98498,C+的四个内存区域,C+的四个内存区域: 代码区存放程序代码。 静态数据区存放全局变量或对象、存放static局部变量或对象。全局变量或对象在程序开始运行时在该区分配; static局部变量或对象在程序运行过程中第一次进入其作用域时在该区分配。该区的变量或对象直到程序运行结束才被释放。 局部数据区(栈区) 存放auto局部变量或对象 。 auto局部变量或对象在程序运行到其作用域时在栈区分配,但怎样分配在编译时就已经确定。auto局部变量或对象在离开其作用域时即被释放。 动态存储区(自由存储区、堆区) 存放程序运行过程中由 new 运算符动态创建的变量或对象 。动态创建的变量或对象在编译时无法为其预定存储空间,系统根据运行时的具体要求在该区分配。该区的变量或对象需要使用 delete 运算符才能将其释放。,动态存储分配,静态存储分配: 通常程序中定义的变量或对象,编译器在编译时就可根据该变量或对象的类型知道其所需内存空间的大小,即怎样分配在编译时就已经能够确定,从而在适当的时候为它们分配确定的内存空间,这种内存分配称为静态存储分配。 动态存储分配: 有些变量或对象只有在程序运行过程中才能确定其大小,这样编译器在编译时就无法为它们预定内存空间,只能在程序运行过程中,系统根据运行时的具体要求进行内存分配,称为动态存储分配。动态存储分配是在自由存储区(堆区)中进行。,堆内存,关于堆内存: 堆区是由操作系统直接管理的内存区域,它是“公共的区域”,而且“面积”比较大,它的“生命周期”也不是由编译系统控制的,而是由编程的人来控制的。在许多的应用中,堆内存的使用都是必不可少的。必须通过指针,才可以使用堆内存。 堆内存的分配与释放: 在程序运行过程中,遇到一个需要动态分配的变量或对象时,必须向系统申请取得堆内存中的一块所需大小的内存空间,用于存放该变量或对象。当不再使用该变量或对象时,必须结束它的生命期,需要显式释放其所占用的内存空间。C+中,取得和释放堆内存中的空间,分别通过 new 和 delete 运算符来完成。new 用来动态分配内存空间, delete 用来将动态分配得到的内存空间归还给系统(释放)。,new 与 delete 运算符,new 运算符动态申请堆空间 格式1: 指针变量 = new 类型名 ; 格式2: 指针变量 = new 类型名 ( 初始值 ) ; 功能:动态创建一个指定类型的变量或对象。若创建成功(堆空间申请成功),new 运算返回新分配堆空间的首地址;若创建失败(堆空间申请失败), new 运算返回 0 或 NULL(空指针)。 delete 运算符动态释放堆空间 格式: delete 指针 ; 功能:释放由 new 运算动态创建的变量或对象。指针的值必须是 new 运算所分配堆空间的首地址,即 new 运算的返回值。,new 与 delete 运算符,new 运算符动态创建一维数组 格式: 指针变量 = new 类型名 下标表达式 ; 功能:动态创建一个指定类型的一维数组,数组元素的个数为下标表达式的值。若创建成功(堆空间申请成功),new 运算返回新分配一维数组空间的首地址;若创建失败(堆空间申请失败), new 运算返回 0 或 NULL(空指针)。 注意:动态为数组分配堆空间时,不能同时进行初始化。 delete 运算符动态释放一维数组 格式: delete 指针 ; 功能:释放由 new 运算动态创建的一维数组。指针的值必须是 new 运算所分配一维数组空间的首地址,且前面必须加上一对方括号 “ ”。 注意:若 delete 中的方括号“ ”不写,只是释放一维数组中一个元素的空间。,【例】(动态创建变量) #include void main ( ) int * p = new int ( 100 ) ; cout void main ( ) int * p = new int 6 ; cout p i ; for ( i=0 ; i6 ; i+ ) cout *( p + i ) t ; cout endl ; delete p ; ,运行: 请输入六个整数: 5 6 3 4 9 1 5 6 3 4 9 1,运行: 100 200,new 与 delete 运算符,new 运算符动态创建二维数组 格式: 指针变量 = new 类型名 下标表达式1 下标表达式2 ; 功能:动态创建一个指定类型的二维数组,数组元素的个数为各维下标表达式值的乘积。若创建成功(堆空间申请成功),new 运算返回新分配二维数组空间的首地址;若创建失败(堆空间申请失败), new 运算返回 0 或 NULL(空指针)。 注意:new 运算创建二维数组时,必须将返回的地址赋给行指针变量;若要赋给元素指针变量,必须进行类型强制转换。 delete 运算符动态释放二维数组 格式: delete 指针 ; 功能:释放由 new 运算动态创建的二维数组。指针的值必须是 new 运算所分配二维数组空间的首地址,且前面必须加上一对方括号 “ ”。 注意:若 delete 中的方括号“ ”不写,只是释放二维数组中的一行元素的空间。,【例】(动态创建二维数组) #include void main ( ) float ( *p1 ) 3 = new float 2 3 ; int i , j ; cout p1 i j ; for ( i=0 ; i p2 i ; for ( i=0 ; i6 ; i+ ) cout * ( p2+i ) t ; if ( ( i+1 ) %3 =0 ) cout endl ; delete p1 ; delete p2 ; ,运行: 请输入六个整数:5 6 3 4 9 1 5 6 3 4 9 1 请输入六个整数:5 6 3 4 9 1 5 6 3 4 9 1,动态存储分配的几点说明,动态分配失败: 动态分配是在堆内存中进行,但堆区资源有限,动态分配可能失败,这时 new 运算返回一个空指针(0或NULL),表示发生异常,堆区资源不足,分配失败。 指针删除与堆空间释放: delete 指针p ;其实际含义是删除指针p所指向的变量或对象,即释放p所指向的堆空间,而不是删除指针p本身,释放该堆空间后,指针p就成了空悬指针(即指针p未指向一个有效的地址,而空悬指针是程序错误的一个根源),此时最好将指针p置空(0或NULL)。 动态数组的撤消: delete 指针p ;注意“ ”不可缺少,否则回收不彻底! 若释放由 new 创建的一维数组,而 delete 中又少了“ ”,由于指针p指向的是一维数组的首元素,是元素地址,此时只回收了首元素所占的堆空间。 若释放由 new 创建的二维数组,而 delete 中又少了“ ”,由于指针p指向的是二维数组的首行,是行地址,此时只回收了首行元素所占的堆空间。,动态存储分配的几点说明,内存泄漏与重复释放: new 和 delete 是配对使用的,若 new 返回的指针值丢失,其所分配的堆空间将无法回收,称为内存泄漏。程序执行结束后,这部分空间将从系统中丢失,必须重新启动计算机才能找回。 对同一个堆空间重复释放也是危险的,因为可能在第一次释放后,该空间已经分配给其它变量或对象,再次释放会使新的变量或对象丢失。 动态分配的变量或对象的生存期: 非动态分配的变量或对象,在定义时都要用标识符命名,称命名变量或对象。auto局部的命名变量或对象其生存期在作用域内;全局和static局部的命名变量或对象在程序运行结束才被自动释放。 动态分配的变量或对象没有名字,对其访问只能通过指针来间接进行,称无名变量或对象。其生存期不依赖于建立它的作用域,例如在被调函数中动态分配的变量或对象,只要不被 delete 释放,在返回主调函数后仍可使用,但前提是其起始地址必须从被调函数中带出,否则将无法访问。,使用 new 和 delete 的几点说明,使用 new 动态分配的存储空间,若不赋初值,其初值不确定。 若 new 动态分配的空间较大,通常需要判断其返回的指针值是否为0或NULL,若为0或NULL ,表示动态分配失败,此时应终止程序执行或进行出错处理。 【例】 int ( *p ) 200 = new int 100 200 ; if ( p = 0 ) exit ( 0 ) ; 或 if ( p = NULL ) exit ( 0 ) ; 用 new 为数组动态分配堆空间,不能同时初始化。 【例】 int *p = new int 5 1 , 3 , 5 , 7 , 9 ; 必须妥善保存 new 动态分配空间的指针值,以便其后用 delet

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论