1 / 105

第 5 章 指针与引用

第 5 章 指针与引用. 5.4 指针与函数 5.4.1 指针作为函数的参数 5.4.2 返回指针的函数 5.4.3 函数指针 5.4.4 带参数的主函数 main() 5.5 字符串处理函数 5.6 引用 5.7 引用与函数 5.7.1 把引用用作函数参数 5.7.2 返回引用的函数 5.7.3 拷贝构造函数与对象的 引用调用 5.8 指针与引用. 5.1 指针 5.1.1 数据存储 5.1.2 指针的声明及使用 5.1.3 指针运算

yoshe
Download Presentation

第 5 章 指针与引用

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第 5 章指针与引用 5.4 指针与函数 5.4.1 指针作为函数的参数 5.4.2 返回指针的函数 5.4.3 函数指针 5.4.4 带参数的主函数main() 5.5 字符串处理函数 5.6 引用 5.7 引用与函数 5.7.1 把引用用作函数参数 5.7.2 返回引用的函数 5.7.3 拷贝构造函数与对象的 引用调用 5.8 指针与引用 5.1 指针 5.1.1 数据存储 5.1.2 指针的声明及使用 5.1.3 指针运算 5.1.4 与对象有关的指针 5.1.5 void和const指针 5.2 动态内存分配 5.2.1 运算符new 5.2.2 运算符delete 5.3 指针与数组 5.3.1 用指针访问数组元素 5.3.2指针数组与数组指针

  2. 【5.1 指针】 本章介绍指针和引用基本知识:指针的声明、指针的运算、与对象有关的指针、void和const指针、引用的声明及使用;讨论指针与数组、指针与函数、引用与函数的关系;并详细讲解动态内存分配、字符串处理函数等。 数组元素在内存中是顺序存贮的,可以用数组表示逻辑上具有顺序关系的线性数据结构。但对于更复杂的数据结构,就必须采用其他方法。 指针是保存内存地址的变量。通过指针,程序员可以直接管理计算机内存。正确地使用指针,可以有效地表示更加复杂的数据结构。此外,C++指针还可以用在函数的传址调用等场合,使得C++的功能非常强大。 指针是C++语言从C语言中继承来的重要概念,是C++语言最重要的特色之一。指针的正确使用是C++语言的学习重点之一。 计算机内存划分为内存单元,这些内存单元按其顺序进行编址。程序运行时,系统将利用内存存储相关的数据。 【5.1.1 数据存储】

  3. 例 5-1 数据在内存中的存储方式是:按其所属的数据类型,占据一定数量的连续内存单元。 C++程序运行时,系统会自动地根据变量数据类型为该变量分配相应的内存。必要时可以使用取地址运算符“&”来获取变量的地址。 变量地址及取地址运算符 #include<iostream.h> int main() { int i=1; cout<<″i=\t″<<i<<″\tThe address of it:\t″<<&i<<endl; return 0; } i= 1 The address of it: 0x0066FDF4 程序中的每个变量在其生存期内存储的位置是不变的,具有固定的存储地址。

  4. 【5.1.2 指针的声明及使用】 注 由于不同计算机的编址方式不尽相同,且在同一台计算机上每次运行这个程序时,系统为变量i分配的地址也不一定相同。 指针是用于存放内存地址的变量。可以将变量的地址保存在指针中 标识符给出的是指针变量名。 数据类型可以是C++语言中任一合法的类型。 “*”号说明其后的变量是一个指针变量。 列出的数据类型并不是指针变量本身的类型,而是指针用于保存的地址值中存储的变量的数据类型,即指针所指向变量的数据类型。 指针是一个变量,必须先声明,后使用。其声明的一般形式如下: 数据类型 *标识符;

  5. 任一指针变量本身数据值的类型都是unsigned long int 说明:声明了p1、p2两个指针。其中p1指向int型变量,p2指向double型变量。 指针也可以和其他变量同时声明 说明:声明了一个int型变量i和指针p1。 注 使用指针前,必须首先给它赋一个合法的值。否则,程序中对指针的访问有可能导致系统崩溃。 与其他变量一样,指针可以通过初始化来赋值。 说明:第1个声明就将指针p1的值初始化为变量i的内存地址;第2个声明将指针p2的值初始化为0。值为0的指针叫空指针。 比如: int *p1; double *p2; 比如: int i,*p1; 比如: int i,*p1=&i; int *p2=0;

  6. 【5.1.2 指针的声明及使用】 如果在声明时,指针初始化为0或根本没有初始化,使用前,就必须首先给它赋有意义的值。 比如: int i,*p1; p1=&i; int *p1=0; int i; p1=&i; 或 数组名表示的是该数组的首地址值,如果声明指针指向一个数组。 把指针px初始化为指向数组int x[5]的指针,即指针px指向数组的第一个元素。不需要使用取地址运算符“&”。 等价 int x[5],px=&x[0]; 如果需要声明多个指针指向同一变量的地址,我们可以利用这个变量给 就可以使用下述赋值方法: int x[5],*px=x;

  7. 这些指针逐一赋值,也可以采用下述方法。 使得指针p1和p2指向同一变量的内存地址。 确实需要用不同类型变量的地址给指针赋值,就应该采用强制类型转换。 每个变量在内存中都有一个固定的地址,而指针中保存的就是变量的地址值。如果声明一个指针,并使其值为某个变量的地址,就可以通过这个指针间接地访问该变量。 比如: int i,*p1=&i; int *p2= p1; 对指针的错误赋值的举例 int i,*p1=i; //不能给指针赋变量本身的值,应将变量的地址值赋给它 double *p2=&i; //不能用不同类型变量的地址来给指针赋值 例如: double *p2=(double*)&i

  8. 利用指针来访问变量需要使用间接访问运算符 “*”。 将输出变量i 的值1 输出2 说明:没有明确地给指针赋一个地址值,因而其值是随机的。 通过“*”运算访问目的数据的过程也称为“间接寻址”过程。 注 使用指针前,必须首先给它赋一个合法的值。 比如: int i=1,*p=&i; cout<<*p; *p=2; cout<<*p; 例如 int *p; *p=2; //错误,指针p没有赋值

  9. 例 5-2 指针的声明与使用 #include<iostream.h> int main() { int i,j; //声明变量 int *p1=&i,*p2=0; //声明指针并初始化,其中p2初始化为空指针 i=1; p2=&j; //将变量j的地址值赋给指针p2 *p2=2; //间接给变量j赋值 cout<<″p1=\t&i\t″<<″p2=\t&j″<<endl; cout<<″i=\t1\t″<<″*p2=\t2″<<endl; cout<<″i=\t″<<i<<″\tj=\t″<<j<<endl; cout<<″*p1=\t″<<*p1<<″\t*p2=\t″<<*p2<<endl; cout<<endl; *p1=10; //给变量i赋值 j=20; cout<<″*p1=\t10\t″<<″j=\t20″<<endl; cout<<″i=\t″<<i<<″\tj=\t″<<j<<endl; cout<<″*p1=\t″<<*p1<<″\t*p2=\t″<<*p2<<endl; cout<<endl;

  10. (续) p1= &i p2= &j i= 1 *p2= 2 i= 1 j= 2 *p1= 1 *p2= 2 *p1= 10 j= 20 i= 10 j= 20 *p1= 10 *p2= 20 p1= &j p2= &i i= 10 j= 20 *p1= 20 *p2= 10 p1=&j; //将变量j的地址值赋给指针p1 p2=&i; //将变量i的地址值赋给指针p2 cout<<″p1=\t&j\t″<<″p2=\t&i″<<endl; cout<<″i=\t″<<i<<″\tj=\t″<<j<<endl; cout<<″*p1=\t″<<*p1<<″\t*p2=\t″<<*p2<<endl; return 0; }

  11. 例 5-3 指针是一个变量,在内存中也需占据一定的空间,具有一个地址,这个地址也可以利用指针来保存。 声明一个指针来指向它,这个指针称为指向指针的指针,也即二级指针。 标识符为指针名。 指通过两次间接寻址后所访问的变量的类型。 两个“*”号表示二级指针。 声明了一个二级指针pp,它指向指针p。 二级指针 #include<iostream.h> int main() { 声明二级指针的形式如下: 数据类型 **标识符 比如: int i,*p=&i int **pp=&p;

  12. (续) i= 1 *p= 1 p= 0x0065FDF4 *pp= 0x0065FDF4 **p= 1 int i; int *p=&i,**pp=&p; //声明指针p指向变量i,并声明指针pp指向指针p i=1; cout<<″i=\t″<<i<<endl; cout<<″*p=\t″<<*p<<endl; //输出指针p所指向的变量i的值 cout<<″p=\t″<<p<<endl; //输出指针p的值,也即变量i的地址值 cout<<″*pp=\t″<<*pp<<endl; //输出指针pp所指向的指针变量p的值 cout<<″**pp=\t″<<**pp<<endl; //输出指针pp所指向的指针变量p所指向//的变量i的值 return 0; }

  13. 【5.1.3 指针运算】 注 注 指针的算术运算与一般的数值计算不同,它不是简单地将指针的值加上或减去给定的整数值,而是与指针所指向变量的类型有关。 通过二级指针访问目的单元中的数据值必须使用两个“*”号。 指针是一个变量,它的值是一个地址。它只能参与赋值、算术及关系运算。 1. 算术运算 指针可以和整数进行加减运算,包括增1和减1运算。 指针加n的实际操作是使指针中的地址值加上sizeof(数据类型)*n 指针减n的实际操作是使指针中的地址值减去sizeof(数据类型)*n

  14. 指针加减运算一般用在对数组元素进行操作的场合。指针加减运算一般用在对数组元素进行操作的场合。 运算结果是一个整数值,它的绝对值就是两个指针所指向的地址之间相隔数据的个数。 数组是连续存储的,通过对指向数组的指针进行加减运算,使指针指向数组中不同的元素。对指向普通变量的指针进行加减运算后,指针所指向的地址的用途是不确定。 p=p+5已经使指针p指向的地址在数组之外,*p=10表达式在这个地址中写入值有可能造成严重的后果。 如果两个指针所指向的变量类型相同,也可以对它们进行如下减法运算。 利用指针对数组进行操作时,也必须注意越界问题。 比如: int x[5]; int *p=x; //指针p指向数组x的首地址 p= p+5; *p=10; 比如: p1-p2

  15. 变量i的值为数组元素x[1]和x[4]之间相隔元素的个数。变量i的值为数组元素x[1]和x[4]之间相隔元素的个数。 2. 关系运算 指针的关系运算一般是在指向相同类型变量的指针之间进行。 说明:声明的两个指针作p1==p2运算,其结果为1(true)。也即指针p1、p2指向同一个变量。 其他关系运算比较的也是两个指针所指向的地址值的大小。一般用在对数组进行操作的场合。 指针可以指向任一类型的变量,也可以指向对象。 比如: int x[5],i; int *p1=&x[1], *p2=&x[4]; i=p2-p1; 【5.1.4 与对象有关的指针】 比如: int a; int *p1=&a; *p2=p1;

  16. 对象的指针的声明方法与普通变量的指针相同。对象的指针的声明方法与普通变量的指针相同。 通过对象的指针间接访问对象成员的方式相应地表示为: 1. 对象的指针 注 因为间接访问运算符“*”的优先级低于成员选择运算符“.”,所以表达式中对象的指针名两边的圆括号不能省略。 声明一个指针来保存对象的地址,这个指针就是指向对象的指针,简称对象的指针。 形式如下: 类名 *对象的指针名 (*对象的指针名).数据成员名 //访问数据成员 (*对象的指针名).成员函数名(参数表)//访问成员函数

  17. “->”也叫做成员选择运算符,该运算符用于通过对象的指针访问对象成员。“->”也叫做成员选择运算符,该运算符用于通过对象的指针访问对象成员。 例如: Cuboid cuboid1(10.0,20.0,30.0); Cuboid *pcuboid=&cuboid1; double area=(*pcuboid).getSurfaceArea(); C++语言提供了另一个更常用的方法: 表述形式如下: 对象的指针名->数据成员名; //访问数据成员 对象的指针名->成员函数名(参数表);//访问成员函数 因此,上述语句可改写为: Cuboid cuboid1(10.0,20.0,30.0); Cuboid *pcuboid=&cuboid1; double area=pcuboid ->getSurfaceArea();

  18. 使用对象的指针前,也一定要给指针赋一个合法的值。使用对象的指针前,也一定要给指针赋一个合法的值。 对象的指针 例 5-4 //EXAMPLE5_4.H #include<iostream.h> class Date //声明类 { public: Date(int y=2001,int m=1,int d=1); //带默认值的构造函数 ~Date(){} //析构函数 void setDate(int y,int m,int d); //用于改变日期的函数 void showDate() const; //用于输出日期的函数 private: int year,month,day; }; //EXAMPLE5_4.CPP #include″EXAMPLE5_4.H″ Date::Date(int y,int m,int d) //实现构造函数 (续)

  19. { year=y; month=m; day=d; } void Date::setDate(int y,int m,int d) //实现改变日期的函数 { year=y; month=m; day=d; } void Date::showDate() const //实现输出日期的函数 { cout<<year<<″年″<<month<<″月″<<day<<″日″<<endl; } int main() { Date date; //声明对象 Date *pdate=&date; //声明对象的指针 (续)

  20. 2001年1月1日 2001年1月1日 2001年6月30日 2001年6月30日 2001年12月31日 2001年12月31日 date.showDate(); //通过对象名访问公有成员函数 pdate->showDate(); //通过对象的指针访问公有成员函数 date.setDate(2001,6,30); //改变日期值 date.showDate(); pdate->showDate(); pdate->setDate(2001,12,31); date.showDate(); pdate->showDate(); return 0; } (续)

  21. 调用一个对象的成员函数时,系统会首先将这个对象的地址赋给被调用成员函数中的this指针。成员函数访问数据成员时,就隐含地使用this指针来确保访问到的数据成员属于这个对象。 说明:显式使用了this指针,明确地指出了成员函数当前所操作的数据成员所属的对象。 2. this指针 由于this指针的隐含性,在一般情况下并不需要显式标明。 this指针也是一个指向对象的指针,它隐含在类成员函数中,用来指向成员函数所属类的正在被操作的对象。 比如: Date::Date(int y,int m,int d) { this->year=y; this->month=m; this->day=d; }

  22. this指针 //EXAMPLE5_5.H #include<iostream.h> class Date //声明类 { public: Date(int y=2001,int m=1,int d=1); //带缺省值的构造函数 ~Date(){} //析构函数 void setDate(int y,int m,int d); //用于改变日期的函数 void showDate() const; //用于输出日期的函数 private: int year,month,day; }; //EXAMPLE5_5.CPP #include″EXAMPLE5_5.H″ Date::Date(int y,int m,int d) //实现构造函数 { this->year=y; //显式使用this指针 this->month=m; //this指针指向正在被操作的对象 例 5-5

  23. this->day=d; } void Date::setDate(int y,int m,int d) //实现改变日期的函数 { this->year=y; //显式使用this指针 this->month=m; // this指针指向正在操作的对象 this->day=d; } void Date::showDate() const //实现输出日期的函数 { cout<<this->year<<″年″<<this->month<<″月″<<this->day <<″日″<<endl; } int main() { Date date(2001,1,1); //声明对象 Date *pdate=&date; //声明对象的指针 date.showDate(); //通过对象名访问公有成员函数 pdate->showDate(); //通过对象的指针访问公有成员函数 date.setDate(2001,6,30); //改变日期 (续)

  24. 说明:显式使用this指针所完成的任 务都可以由系统隐含完成。 注 静态成员函数没有this指针 3. 指向类成员的指针 指向成员函数的指针(简称成员函数指针) 指向数据成员的指针(简称数据成员指针) 指向类成员的指针 类实例化后,类中的数据成员和成员函数在内存中也有一定的存储地址,可以声明指向类成员的指针。通过这些指针,可以访问对象的成员。 date.showDate(); pdate->showDate(); pdate->setDate(2001,12,31); date.showDate(); pdate->showDate(); return 0; } (续)

  25. 使用前必须声明并赋值 数据成员指针的声明形式如下: 数据类型 类名::*数据成员指针名; 数据类型是指数据成员指针所指向的数据成员的类型。 类名是数据成员所属类的名称。 在这里,赋值也就是确定指针指向类中的哪一个成员。 对于数据成员指针,赋值的形式如下: 数据成员指针名=&类名::数据成员名 例如: class A { public: ... int a; ... };

  26. 等价 声明指向类成员的指针后,就可以通过它访问属于该类对象的这个成员。 int A::*p; p=&A::a; 注 访问表达式中,必须指定需要访问的对象名或指向这个对象的指针。 int A::*p=&A::a; 表述形式如下: 对象名.*类成员指针名 //通过对象名访问 对象指针名->*类成员指针名 //通过对象的指针访问

  27. 数据成员指针 //EXAMPLE5_6.H #include<iostream.h> class A { public: A(double x1=0.0,double y1=0.0); ~A(){} void setxy(double x1,double y1); void outputxy() const; double x; private: double y; }; //EXAMPLE5_6.CPP #include″EXAMPLE5_6.H″ A::A(double x1,double y1):x(x1),y(y1) { } 例 5-6

  28. void A::setxy(double x1,double y1) { x=x1; y=y1; } void A::outputxy() const { cout<<″x=\t″<<x<<″\ty=\t″<<y<<endl; } int main() { A a(1.5,2.6),*pa=&a; //声明类A的对象a及指针pa,并让对象pa 指向a double A::*px=&A::x; //声明指向类A的double型数据成员的指针 //并使其指向公有成员x a.outputxy(); //调用成员函数输出 cout<<″a.x=\t″<<a.x<<endl; //利用对象名直接访问成员x cout<<″a.*px=\t″<<a.*px<<endl; //利用对象名和数据成员指针间接访问x cout<<″pa->*px=\t″<<pa->*px<<endl; //利用对象的指针和成员指 // 针间接访问x return 0; } (续)

  29. 【5.1.5 void和const指针】 x= 1.5 y=2.6 a.x= 1.5 a.*px= 1.5 pa->*px= 1.5 指向void类型的指针称为void指针。声明指针时,可以用关键字const进行修饰,用关键字const修饰的指针称为const指针。 1. void指针 一般来说,只能用指向相同类型的指针给另一个指针赋值。 比如: int a,b; int *p1=&a,*p2=p1;

  30. 不同类型指针间的赋值是错误的。 语句中的两个指针指向的类型不同,因此,它们之间不能相互赋值。除非进行强制类型转换。 void指针是一个特例,它可以指向任何类型的C++数据。也即可以用任一类型的指针直接给void指针赋值。如果需要将void指针的值赋给其他类型的指针,则需要进行强制类型转换。 比如: int a; int *p1=&a; double *p2=p1; //错误 比如: int a; // 声明int型变量 int *p1=&a; // 给指针p1赋值 void *p2=p1; // 用int型指针直接给void指针赋值,正确 int *p3=p2; // 用void指针直接给int型指针赋值,错误 int *p4=(int *)p2; // void指针通过强制类型转换给int型指针赋值,// 正确

  31. void指针 #include<iostream.h> int main() { int a(1); int *pa1=&a,*pa2=0; void *pv=pa1; //用int型指针直接给void指针赋值 cout<<″a=\t″<<a<<endl; cout<<″*(int*)pv=\t″<<*(int*)pv<<endl; //输出结果时,void指针必须 //强制类型转换 pa2=(int *)pv; //给int型指针赋值时,void指针必须转换 cout<<″*pa2=\t″<<*pa2<<endl; return 0; } a= 1 *(int*)pv= 1 *pa2= 1 例 5-7

  32. 关键字const放在不同的位置表示的意义也不相同。关键字const放在不同的位置表示的意义也不相同。 2. const指针 (1) 关键字const放在指针类型前,是声明一个指向常量的指针。程序中不能通过指针来改变它所指向的值,但指针本身的值可以改变,即指针可以指向其他数据。 (2)关键字const放在“*”和指针名之间,是声明一个指针常量(也称常指针)。指针本身的值不可改变,即不能再指向其他数据,但它指向的数据的值可以改变。 在用关键字const修饰指针时,关键字const可以放在指针类型前或“*”号与指针名之间,甚至可以在上述两个地方都加。 比如: int a=1,b=2; const int *p1=0; //声明指向int型常量的指针p1 p1=&a; //将int型变量a的地址赋给指针 *p1=2; //错误,不能通过指向常量的指针来改变它所指的值a=2 //正确,变量的值可通过变量名改变 p1=&b; //正确,指向常量的指针本身的值可以改变

  33. 在const指针指向一个对象时,必须注意,如果这个指针是一个指向常量的指针,那么通过这个指针就只能调用对象的const成员函数。在const指针指向一个对象时,必须注意,如果这个指针是一个指向常量的指针,那么通过这个指针就只能调用对象的const成员函数。 注 在声明常指针时必须初始化(除非它是函数的形参)。 (3)关键字const在上述两个地方都加,则是声明一个指向常量的指针常量,指针本身的值不可改变,它所指向的数据的值也不能通过指针改变。 声明对象为常对象时,实际上间接声明了这个对象的成员函数this指针是指向常对象的指针,即const this指针。通过这个this指针也就只能调用const成员函数。 注 对于能说明为常量的情形,应该尽量用关键字const修饰。 比如: int a=1,b=2; int *const p1=&a; //声明指向int型常量的指针p1 int *const p2; //错误,在声明指针常量时,必须初始化 *p1=2; //正确,指针常量指向的值可以改变 p1=&b; //错误,指针常量本身的值不可改变

  34. 【 5.2 动态内存分配】 程序运行开始时分配的存储空间。 静态存储区 所占内存分类 局部变量在栈中分配存储空间。 栈 堆 也称为自由存储单元,动态内存分配就是在堆中进行的。 运算符new用于申请所需的内存单元。 动态内存分配是指在程序运行期间根据实际需要随时申请内存,并在不需要时释放。 动态内存分配时需要使用运算符new和运算符delete,相应地我们把申请和释放内存的过程称作创建和删除。 指针指向的数据类型应与关键字new后给定的类型相同。 【5.2.1 运算符new】 使用形式如下: 指针=new 数据类型

  35. 【5.2.1 运算符new】 关键字new的作用是在堆中为程序申请指定数据类型所需的内存单元,也即在堆中创建该类型的变量。若创建成功,则返回返回一个指向所分配内存首地址的指针;否则,返回一个空指针。new运算的返回值必须赋给程序中声明的指针 。 创建一个int型变量,返回值保存在指针p中 创建成功后,可以使用这个指针。比如: *p=1; 创建失败,返回空指针。 把值1赋给指针p所指向的int型内存单元。 注 实际编程时,应该首先检查new运算的返回值是否为0,以判断创建是否成功。 动态创建变量时,也可以对创建的变量初始化。 比如: int *p; p=new int; 比如: int *p=new int(1);

  36. 说明:为创建的int型变量指定了初值1,若变量创建成功,其初值就为1。圆括号中的内容可以是任意与变量类型相同的表达式。说明:为创建的int型变量指定了初值1,若变量创建成功,其初值就为1。圆括号中的内容可以是任意与变量类型相同的表达式。 可以用动态内存分配在堆中创建对象。 说明:在堆中创建对象时,系统也会自动调用相应的构造函数。圆括号中的内容是调用构造函数的实参,构造函数用它们来初始化对象。 在堆中也可以创建数组。 指针指向的数据类型应与关键字new后给出的类型相同。 下标表达式给出的是数组元素的个数。 比如: Date *pDate1=new Date; Date *pDate2=new Date(2001,6,30); 创建一维数组的表述形式如下: 指针=new 数据类型[下标表达式]

  37. 在堆中创建数组时,不能对数组中的元素初始化。因此,如果需要动态创建对象数组,相应的类声明中必须有无需实参调用的构造函数。在堆中创建数组时,不能对数组中的元素初始化。因此,如果需要动态创建对象数组,相应的类声明中必须有无需实参调用的构造函数。 创建成功 运算符new将返回一个指向分配的内存首地址的指针,该指针的类型与上述表达式中给定的相同。 创建失败 返回空指针。 说明:分别在堆中创建了一个整数数组和Date类的对象数组。 对于多维数组,情况就要复杂一些: 除下标表达式1外,其余的下标表达式必须是结果为正整数的常量表达式。 比如: int *p=new int[5]; Date *pDate=new Date[5]; int *p=new int[5]={1,2,3,4,5}; //错误,不可初始化 其一般表述形式如下: 指针=new 数据类型[下标表达式1][下标表达式2][下标表达式3]…

  38. 【5.2.2 运算符delete】 创建成功,new运算将返回一个指向所分内存首地址的指针,不过,该指针的类型不是运算符new后给出的数据类型,而是一个该类型的数组,即该指针是一个数组指针(本章第3节中将具体介绍)。 与一维数组相同,也不能初始化这个创建的数组。 说明:创建了一个三维数组 当程序中不再需要使用运算符new申请到的某个内存时单元时,就必须用运算符delete来释放它。 指指向需要释放的内存单元的指针的名字 对应于上面的表达式,该指针的声明形式如下: 数据类型 (*指针)[下标表达式2][下标表达式3]… 比如: int (*p)[3][4]; p=new int[2][3][4]; 表述形式如下: delete 指针名; // 释放非数组内存单元 delete[] 指针名; // 释放数组内存单元

  39. 【5.2.2 运算符delete】 在这一操作中,指针本身并不被删除,必要时可以重新赋值。 注 在释放数组内存单元时,运算符后别忘了“[]”号。如果未加“[]”号,就只是释放数组的第一个元素占据的内存单元。 动态分配的内存除非程序显式地释放它,否则直到程序运行结束,一直都被占据着。 比如: int *p1=new int[5] ; Date *p2=new Date(2001,6,30); delete[] p1; delete p2;

  40. 【5.2.2 运算符delete】 如果某一指针指向动态分配的内存,在指针退出作用域前,没有释放它指向的内存,也会产生内存遗漏问题。 int *p=new int; *p=2; delete p; p=new int; 改正 说明:指针在原来指向的内存单元没有释放前被重新赋 值,从而造成了原指向的内存遗漏。 注 对应于每次使用运算符new,一般都应该相应地使用运算符delete来释放申请的内存,对应于每个运算符new,只能调用一次delete来释放内存。 利用指针释放其指向的内存单元后,没有对这个指针再次调用new来使之指向其他有效内存,而又进行delete操作,有可能导致程序崩溃。对空指针进行delete操作是安全的,每次delete操作后,给相应的指针赋0。 比如:int *p=new int; *p=2; p=new int //错误,产生内存遗漏

  41. 例 5-8 注 每次执行delete操作后,给相应的指针赋0(空指针)可以避免丢失指针。程序中对某一指针调用delete后,该指针指向的内存单元就被释放,但指针的值(也即指针指向的地址)并没改变,如果没有给它重新赋值就使用该指针,该指针就成了丢失指针。 同一个指针在没有重新赋值的情况下多次调用delete是丢失指针的情形之一。 动态内存分配 #include<iostream.h> int main() { int *p=new int; //动态申请内存 if(p==0) //检查内存分配是否成功 { cout<<"内存申请失败!"<<endl; return 1; }

  42. (续) *p= 10 *p= 20 *p=10; cout<<″*p=\t″<<*p<<endl; delete p; //删除内存 p=new int; //重新申请内存,并将指针指向它 if(p==0) { cout<<"内存申请失败!"<<endl; return 1; } *p=20; cout<<″*p=\t″<<*p<<endl; delete p; //删除内存 return 0; }

  43. 例 5-9 指针作为类的数据成员 声明类时,类中的数据成员也可以是指向堆中某一内存单元的指针,内存单元可以由构造函数分配,析构函数释放。 //EXAMPLE5_9.H #include<iostream.h> class A //声明类 { public: A(int x1=0,int y1=0); ~A(); void setxy(int x1,int y1); void outputxy() const; private: int *x,*y; //两个数据成员都是指针 }; //EXAMPLE5_9.CPP #include″EXAMPLE5_9.H″

  44. (续) A::A(int x1,int y1) //构造函数 { x=new int(x1); //申请内存并初始化 y=new int(y1); //申请内存并初始化 } A::~A() //析构函数 { delete x; //释放内存 delete y; //释放内存 } void A::setxy(int x1,int y1) { *x=x1; *y=y1; } void A::outputxy() const { cout<<″*x=\t″<<*x<<″\t*y=\t″<<*y<<endl; }

  45. (续) *x= 1 *y= 1 *x= 2 *y= 5 注 保持与C语言的兼容性,保留了C语言中用于动态内存分配的两个库函数:malloc()(与运算符new功能类似)和free()(与运算符delete功能类似)。 int main() { A *p=new A(1,1); //在堆中创建对象并初始化 p->outputxy(); p->setxy(2,5); p->outputxy(); delete p; //删除对象 return 0; }

  46. 【 5.3 指针与数组】 数组名x是指向第一个元素x[0]的指针,*x表示的就是元素x[0]的值;*(x+2)则是访问x[2]中的值的有效方法。 数组名表示的是该数组的首地址值,是指向数组第一个元素的指针常量。 数组在内存中是顺序存储的,声明某个指针指向数组中的一个元素,就可以通过指针和整数的加减运算(包括增1和减1运算)来访问这个数组的每个元素。由于数组名本身就是指向数组第一个元素的指针常量,因此,可以通过它来访问其中的元素。 例 5-10 通过数组名访问数组元素 #include<iostream.h> int main() 【5.3.1 用指针访问数组元素】 例如: int x[5];

  47. 例 5-10 利用数组名访问数组元素与利用数组下标的效果相同。 通过数组名访问数组元素 1 2 3 4 5 1 2 3 4 5 { int x[5]={1,2,3,4,5}; //声明数组并初始化 for(int i=0;i<5;i++) { cout<<x[i]<<′\t′; //利用数组下标输出数组元素值 } cout<<endl; for(i=0;i<5;i++) { cout<<*(x+i)<<′\t′; //利用数组名输出数组元素值 } cout<<endl; return 0; }

  48. 例 5-11 程序中,也声明一个与数组元素类型相同的指针,然后使之指向数组的某个元素。通过这个指针同样能访问到数组中的每个元素。 声明的指针指向数组int x[5]的第一个元素。因此,通过它就可以访问到这个数组中的每一个元素,如*(p+2)表示的就是元素x[2]的值。 利用指针访问数组元素 #include<iostream.h> int main() { int x[5],i; int *px=x; //声明指针,指向数组的首元素 for(i=0;i<5;i++) { x[i]=i; //利用数组下标的方法给数组元素赋值 } 例如: int x[5]; int *p=x;

  49. (续) 0 1 2 3 4 0 2 4 6 8 for(i=0;i<5;i++) { cout<<*px++<<′\t′; //利用指向数组的指针输出数组的元素值 } cout<<endl; px=x; for(i=0;i<5;i++) { *px++=2*i; //利用指向数组的指针给数组元素赋值 } px=x; for(i=0;i<5;i++) { cout<<* px++<<′\t′; } cout<<endl; return 0; }

  50. 数组名是一个指针常量,其值不可改变。在使用增1或减1运算访问数组元素时,不能直接利用数组名,必须另外声明一个指向数组某个元素的指针。数组名是一个指针常量,其值不可改变。在使用增1或减1运算访问数组元素时,不能直接利用数组名,必须另外声明一个指向数组某个元素的指针。 例如: 假设一个二维数组:int x[2][3]; 则数组中的元素x[i][j]有多种访问方法,以下是其中的一部分: *(*(x+i)+j) *(x[i]+j) (*(x+i))[j] *(x[0]+3*i+j) 如果是另外声明指针指向它的某个元素,处理的方法也有许多种。 例如: 假设p是指向上述二维数组首元素的指针, 下面列出部分通过这个指针访问数组元素x[i][j]的方法: *(*(p+i)+j) *(p[i]+j) (*(p+i))[j] *(p[0]+3*i+j) p[i][j]

More Related