高级语言程序设计 C++程序设计教程(下) 2006年春季学期 与一些教材的区别 偏重理论,不去讨论某个系统的具体使用方法,但会涉及实现技术 我们的优势 虽然枯燥,但适应DBA的需要 不过时
主要内容安排 第八章:指针 第十三章:堆与拷贝构造函数 第九章:引用 第十四章:静态成员与友元 第十章:结构 第十五章:继承 第八章:指针 第十三章:堆与拷贝构造函数 第九章:引用 第十四章:静态成员与友元 第十章:结构 第十五章:继承 第十一章:类 第十六章:运算符重载 第十二章:构造函数 第十七章:模板 与一些教材的区别 偏重理论,不去讨论某个系统的具体使用方法,但会涉及实现技术 我们的优势 虽然枯燥,但适应DBA的需要 不过时
第八章 指针 8.1 指针概念 8.6 指针与函数 8.2 指针运算 8.7 字符指针 8.3 指针与数组 8.8 指针数组 8.1 指针概念 8.2 指针运算 8.3 指针与数组 8.4 堆内存分配 8.5 const指针 8.6 指针与函数 8.7 字符指针 8.8 指针数组
引言 C++语言拥有在运行时获得变量的地址和操纵地址的能力。这种用来操纵地址的特殊类型变量就是指针。指针用于数组,作为函数参数,用于内存访问和堆内存操作。
8.1 指针概念 主要内容: 1.指针类型 2. 定义指针变量 3. 建立指针 4. 间接引用指针 5. 指针变量的地址 6. 指针与整型数据的区别 7. 指针的初始化 8. 指针类型与实际存储的匹配
1.指针类型 我们学过基本数据类型,如int,float,char,double等,其中每一种基本数据类型都有相应的指针类型。
2.定义指针变量 指针变量的定义语句,由数据类型后跟星号,再跟随指针变量名组成。 格式:基类型 * 指针变量名; 说明: (1)指针定义都分配了空间,但是都没有指向任何内容。 (2)在指针定义中,一个*号只能表示一个指针。 例:char *cptr;
3.建立指针 建立指针包括定义指针和给指针赋初值。 用&操作符可以获取变量的地址,指针变量用于存放地址。 例: int *iptr; int icount=18; iptr= &icount;
3.建立指针(续1) 0000:F822 iCount 18 iPtr 图8-1 指针的工作方式
4.间接引用指针 “*”的作用: (1)乘法; (2)定义指针; (3)指针的间接引用。 间接引用指针时,可获得由该指针指向的变量内容。
例1:间接引用指针 iPtr,输出iCount的内容。 // ch8_1.cpp #include<iostream.h> void main() { int * iPtr; int iCount=18; iPtr= &iCount; cout <<*iPtr<<endl; //间接引用指针 } 结果:18
4.间接引用指针(续1) 说明: (1)*放在可执行语句中的指针之前,称为间接引用操作符,*放在指针定义中时,称指针定义符。 (2)非指针变量是不能用间接引用操作符的,因为*只能作用于地址。 (3)间接引用的指针既可用于右值,也可用于左值。
5.指针变量的地址 指针是变量,是变量就具有内存地址。所以指针也有地址。
例2:输出iCount变量值,以及iPtr和iCount的地址值。//ch8_2.cpp #include<iostream.h> void main() { int iCount=18; int * iPtr= &iCount; iPtr=58; cout<<iCount<<endl; cout<<iPtr<<endl; cout<<&iCount<<endl; //与iPtr值相同 cout<<*iPtr<<endl; //与iCount值相同 cout<<&iPtr<<endl; //指针本身的地址 }
结果:58 0x067fe00 0x0067fe00 58 0x0067fdfc 注意:*iPtr的类型是整型,指针iPtr指向该整数,所以iPtr的类型是整型指针,而iPtr的地址(即&iPtr)的类型是整型指针的地址,即指向整型指针的指针。三者都不相同。指针的地址就是二级指针。
6.指针与整型数的区别 指针在使用中必须类型匹配。 例:int iCount=26; 注意:指针值不是整型数。强制转换是合法的。 int * iPtr=&iCount; //定义语句:*在此处作定义指针变量用,而非间接引用 *iPtr=&iCount; //error:不能将整型地址转换成整型数 *iPtr=50; //执行语句:*在此处作间接引用 注意:指针值不是整型数。强制转换是合法的。
7.指针的初始化 指针变量初始化的值是该指针类型的地址值。 例: int iCount=26; int * iPtr=&iCount; //初始化为整型地址 *iPtr=&iCount; //error
注意:不要将int. iPtr=&iCount;与. iPtr=&iCount;混淆。前者是定义语句, 注意:不要将int *iPtr=&iCount;与*iPtr=&iCount;混淆。前者是定义语句,*是指针定义符,C++为iPtr指针分配一个指针空间,并用iCount的地址值初始化,后者是执行语句,左右两边类型不匹配。 说明: (1)*操作符在指针上的两种用途要区分开:定义或声明时,建立一指针;执行时,间接引用一指针。 (2)指针在使用前,要进行初始化。 (3)指针忘了赋值比整型变量忘了赋值危险得多。
8.指针类型与实际存储的匹配 指针是有类型的,给指针赋值,必须是是一个与该指针类型相符的变量或常量的地址。 例3:下面的代码错将浮点类型的变量地址赋给整型指针。 //ch8_3.cpp(P149)
第八章 指针 8.1 指针概念 8.6 指针与函数 8.2 指针运算 8.7 字符指针 8.3 指针与数组 8.8 指针数组 8.1 指针概念 8.2 指针运算 8.3 指针与数组 8.4 堆内存分配 8.5 const指针 8.6 指针与函数 8.7 字符指针 8.8 指针数组
8.2 指针运算 指针可以进行加减运算。 说明: (1)数组名本身,没有方括号和下标,它实际上是地址,表示数组起始地址。 (2)可以把数组起始地址赋给一指针,通过移动指针(加减指针)来对数组元素进行操作。
例1:用指针运算来计算数组元素的和。 //ch8_4.cpp #include <iostream.h> void main() {int iArray[10]; int sum=0; int * iPtr=iArray; //用数组名iArray给指针初始化 for(int i=0;i<10;i++) iArray[i]=i*2; for(int index=0;index<10;index++) //计算数组元素之和 { sum+= *iPtr; iPtr++; } cout <<”sum is” <<sum<<endl; }
8.2 指针运算(续1) 例1:续 结果:sum is 90 注意: 其中,“iPtr=iArray;” 还可以改写成: iPtr=&iArray[0];
例2:显示指针移动时其地址的变化和指向的内容。 // ch8-5.cpp #include<iostream.h > void main () {int iArray[ 10 ]; int * iPtr=iArray; for(int i=0; i<10 ; i++ ) iArray [i]=i*2 ; for(int index = 0; index <10; index + + ) { cout<<“&Array[“<<index <<”]:”<<iPtr <<”=>”<<*iPtr<<end1; iPtr++; }
8.2 指针运算(续2) 注意: sum + = * iPtr ; iPtr + + ; 可压缩成一条语句:
第八章 指针 8.1 指针概念 8.6 指针与函数 8.2 指针运算 8.7 字符指针 8.3 指针与数组 8.8 指针数组 8.1 指针概念 8.2 指针运算 8.3 指针与数组 8.4 堆内存分配 8.5 const指针 8.6 指针与函数 8.7 字符指针 8.8 指针数组
8.3 指针与数组 联系:数组名可以拿来初始化指针,数组名就是数组第一个元素地址。 区别: (1)数组名是指针常量,区别于指针变量,所以,给数组名赋值是错误的。 (2)数组名表示内存中分配了数组的固定位置,修改了这个数组名,就会丢失数组空间,所以数组名所代表的地址不能被修改。
8.3 指针与数组(续1) 例:对于数组a,有:a等于&a[0]。 int a [100]; int * iPtr=a; a[i]、*(a+i)、iPtr[i]、*(iPtr+i) 访问第i个元素的地址有: &a[i] 、a+i 、iPtr+i 、&iPtr[i]
例:用5种方法求数组元素的和。 //ch8-6.cpp #include <iostream.h > int sum1,sum2,sum3,sum4,sum5;//存放每种方法的结果 int iArray[]={1,4,2,7,13,32,21,48,16,30};//全局量 int * iPtr; void main() {int size ,n; size=sizeof(iArray)/sizeof(*iArray); //元素个数 for (n=0;n<size;n++) { sum1 += iArray[n]; } //方法1:下标法
例:用5种方法求数组元素的和。(续1) iPtr=iArray; for(n=0;n<size;n++) { sum2+=*iPtr++; } //方法2:指针变量 iPtr=iArray; //此句不能省略,因为方法2修改了iPtr { sum3+=*(iPtr+n); } //方法3:指针变量 //此句可以省略,因为方法3没有修改iPtr { sum4+=iPtr[n]; } //方法4:指针数组
例:用5种方法求数组元素的和。(续2) for (n=0;n<size;n++) { sum5+=*(iArray+n); } //方法5:常值指针 cout<<sum1<<endl<<sum2<<endl<<sum3<<endl <<sum4<<endl<<sum5<<endl; } 结果:174 174
第八章 指针 8.1 指针概念 8.6 指针与函数 8.2 指针运算 8.7 字符指针 8.3 指针与数组 8.8 指针数组 8.1 指针概念 8.2 指针运算 8.3 指针与数组 8.4 堆内存分配 8.5 const指针 8.6 指针与函数 8.7 字符指针 8.8 指针数组
8.4 堆内存分配 主要内容: 1.堆内存 2. 获得堆内存 3. 释放堆内存 4. new与delete
1.堆内存 堆(head)是内存空间。堆是区别于栈区、全局数据区和代码区的另一个内存区域。允许程序在运行时(而不是在编译时),申请某个大小的内存空间。 堆内存也称动态内存。
2.获得堆内存 C程序用malloc()函数获得堆内存,它在malloc.h头文件中声明。malloc()函数的原型为: void* malloc(size_t size); 说明:size_t 即 unsigned long。该函数从堆内存中“切下”一块size大小的内存,将指向该内存的地址返回。该内存中的内容是未知的。
例1:从堆中获取一个整数数组,赋值并打印。 //ch8-7.cpp #include<iostream.h> #include<malloc.h> void main() {int arraysize; //元素个数 int * array; cout<<”pleaseinput a number of array elements:\n”; cin>>arraysize; array=(int*)malloc(arraysize * sizeof(int)); //堆内存分配 for(int count=0;count<arraysize; count++) array[count]=count*2;
例1: (续) for(int count=0;count<arraysize;count++) cout<<array[count] << “ ”; cout<<endl; } 结果:Please input a number of array elements: 10 0 2 4 6 8 10 12 14 16 18
说明: (1)一个拥有内存的指针完全可以被看作为一个数组,而且位于堆中的数组和位于栈中的数组结构是一样的。表达式“array[count]=count*2;”正是这样应用的。 (2)系统能提供的堆空间不够分配,这时系统会返回一个空指针NULL。这时所有对该指针的访问都是破坏性的,因此调用malloc()函数更完善的代码应该如下: if((array=(int *)malloc(arraysize * sizeof(int))) ==NULL) {cout<<”can’t allocate more memory,terminating.\n”; exit(1) }
3.释放堆内存 C程序用free()函数返还由malloc()函数分配的堆内存,其函数原型为: void free(void * );
例2:完善程序ch8_7.cpp: //ch8-8.cpp #include<iostream.h> #include<malloc.h> void main() {int arraysize; //元素个数 int * array; cout<<”please input a number of array elements:\n”; cin>>arraysize; if((array=(int *)malloc(arraysize * sizeof(int))) ==NULL) {cout<<”can’t allocate more memory, terminating.\n”; exit(1); }
例2:(续) for(int count=0;count<arraysize; count++) array[count]=count*2; for(int count=0;count<arraysize;count++) cout<<array[count]<<” ”; cout<<endl; free(array); //释放堆内存 }
4.new与delete new和delete是C++专有操作符,不用头文件声明。 (2)delete:释放堆内存,操作数是new返回的指针,当返回的是new分配的数组时,应该带[]。
例3:下面的程序是程序ch8_8.cpp的新版: #include<iostream.h> void main() {int arraysize; int * array; cout<<”please input a number of array elements:\n”; cin>>arraysize; if((array=new int[arraysize])==NULL)//分配堆内存 { cout<<”can’t allocate more memory, terminating.\n”; exit(1); }
例3: (续) for(int count=0;count<arraysize;count++) array[count]=count*2; cout<<array[count]<<” ”; cout<<endl; delete[]array; //释放堆内存 }
第八章 指针 8.1 指针概念 8.6 指针与函数 8.2 指针运算 8.7 字符指针 8.3 指针与数组 8.8 指针数组 8.1 指针概念 8.2 指针运算 8.3 指针与数组 8.4 堆内存分配 8.5 const指针 8.6 指针与函数 8.7 字符指针 8.8 指针数组
8.5 const指针 主要内容: 1.指向常量的指针(常量指针) 2. 指针常量 3. 指向常量的指针常量(常量指针常量)
1.指向常量的指针(常量指针) 在指针定义语句的类型前加const,表示指向的对象是常量。 格式:const 基类型 * 指针变量名;
例1: const int a =78; const int b =28; int c =18; const int *pi=&a; //指针类型前加const *pi=58; //error:不能修改指针指向的常量 pi=&b; //ok: 指针值可以修改 *pi=68; //error: 同上 pi=&c; *pi=88; //error:同上 c=98; //ok 说明:定义指向常量的指针只限制指针的间接访问操作,而不能规定指针指向的值本身的操作规定性(即指针值可以修改,被指向的常量不能修改)。
例2:将两个一样大小的数组传递给一个函数,让其完成复制字符串的工作,为了防止作为源数据的数组遭到破坏,声明该形参为常量字符串。 // ch8-10.cpp #include<iostream.h> void mystrcpy(char*dest,const char*source) { while( *dest++=*source++);} void main() { char a[20]=“how are you!”; char b[20]; mystrcpy(b,a); cout<<b<<endl; }
例2:续 结果:How are you! 注意:“const int * pi=&a;”告诉编译,*pi是常量,不能将 *pi 作为左值进行操作。
2.指针常量 在指针定义语句的指针名前加const,表示指针本身是常量。在定义指针常量时必须初始化。
例3: char * const pc=“asdf ”; //定义指针常量 pc=“dfgh”;//error:指针常量不能改变其指针值 *pc=‘b’; //ok:pc内容为“bsdf” *(pc+1)=‘c’; //ok:pc内容为“bcdf” *pc++=‘y’; //error:指针常量不能改变其指针值 const int b=28; int * const pi=&b; //error: 不能将const int* 转化成int* 说明:“int * const pc=&b;”告诉编译,pc是常量,不能作为左值进行操作,但是允许修改间接访问值,即*pc可以修改。
3.指向常量的指针常量 (常量指针常量) 可以定义一个指向常量的指针常量,它必须在定义时进行初始化。 格式: const 基类型 * const 指针变量名=常值表;
第八章 指针 8.1 指针概念 8.6 指针与函数 8.2 指针运算 8.7 字符指针 8.3 指针与数组 8.8 指针数组 8.1 指针概念 8.2 指针运算 8.3 指针与数组 8.4 堆内存分配 8.5 const指针 8.6 指针与函数 8.7 字符指针 8.8 指针数组
8.6 指针与函数 主要内容: 1.传递数组的指针性质 2. 使用指针修改函数参数 3. 指针函数 4. void指针
1.传递数组的指针性质 一旦把数组作为参数传递到函数中,则在栈上定义了指针,可以对该指针进行递增、递减操作。
例1:传递一个数组,并对其进行求和运算。 //ch8-11.cpp #include<iostream.h> void sum(int array[],int n) {int sum=0; for(int i=0; i<n; i++) { sum += *array; array++; //ok:array是一个指针,可以作为左值 } cout<<sum<<endl; void main () {int a[10]={1,2,3,4,5,6,7,8,9,10}; sum(a,10);
例1: (续1) 结果: 55 说明: 在主函数中,对sum()函数进行了调用。传递的数组参数在Sum( )中,实质是一个指针,所以声明Sum(int array[ ],int n)与Sum(int *array,int n)是等价的。
例1: (续2) 数组作为参数的函数调用 栈 Sum main sum array 0067:F010 数组a 0067:F010 返回地址 0067:F010 返回地址 1 2 3 4 10 Sum main 0067:F010 数组a sum array 栈
2.使用指针修改函数参数 问题:通过对函数的调用,函数返回一值。然而,在很多情形下,希望函数返回不止一个值。
例2:程序是想通过swap()函数调用来交换两个整数值。 //ch8-12.cpp #include<iostream.h> void swap(int,int); void main() { int a=3,b=8; swap(a,b); cout<<”after swapping…\n”; cout<<”a=”<<a<<”,b=”<<b<<endl; } void swap(int x,int y) { int temp=x; //交换两个形参 x=y; y=temp;
例2: (续1) 结果: a=3,b=8 after swapping… 说明:该swap()函数无法返回更多的值,而且也无法改变a和b的值。由于函数参数值的传递是实参到形参的复制,被调函数内部对形参的修改并不反映到上层函数的实参中,致使swap()中x和y作了交换,而main()中a和b没有交换。
例2: (续2) 3 8 返回地址 temp y x b a swap main 栈
2.使用指针修改函数参数 改进:传递指针可以使函数“返回”更多的值。这里的“返回”不是函数返回类型描述返回值的返回,而是反映了上层函数中的变量给被调函数修改了。
例3:通过传递指针来实现整数值的交换。 //ch8-13.cpp #include<iostream.h> void swap(int *,int * ); void main() {int a=3,b=8; cout<<“a=”<<a<<”,b=”<<b<<endl; swap(&a,&b); cout<<“after swapping…\ n”; } void swap(int * x,int * y) {int temp=*x; *x=*y; *y=temp;
例3: (续) 结果: a=3,b=8 after swapping… a=8,b=3 传递指针的函数调用实现过程为: (1)函数声明中指明指针参数; (2)函数调用中传递以变量的地址; (3)函数定义中对形参进行间接访问。 对*x和*y的操作,实际上即是访问上层函数的变量a和b。
存在的问题: (1)可读性问题:因为间接访问比直接访问相对难理解,传递地址比传递值的直观性要差,函数声明与定义也相对比较复杂。 (2)重用性问题:函数调用依赖于上层函数或整个外部内存空间的环境,丧失了黑盒的特性,所以无法作为公共的函数模块来使用。 (3)调试复杂性问题:跟踪错误的区域从函数的局部栈空间扩大到整个内存空间。不但要跟踪变量,还要跟踪地址。错误现象从简单的不能得到相应的返回结果,扩展到系统环境遭到破坏甚至死机。
3.指针函数 定义:返回指针的函数称为指针函数。 注意: (1)指针函数不能把在它内部说明的具有局部作用域的数据地址作为返回值。 (2)可以返回堆地址、全局或静态的地址,但不要返回局部变量的地址。
例4:打印一个用整型指针指向的整数值。 //ch8-14.cpp #include<iostream.h> int * getInt(char * str) //指针函数 {int value=20; cout<<str<<endl; return & value; //warning:将局部变量的地址返回是不妥的 } void somefn(char * str) {int a=40;
例4: (续1) void main() {int * pr=getInt(“input a value :”); //赋值取自返回的指针值 cout<<*pr<<endl; //第一次输出*pr somefn(“It is uncertain.”); cout<<*pr<<endl; //第二次输出*pr } 结果: input a value: 20 It is uncertain. 4435500
4.void指针 void指针:是空类型指针,不指向任何类型,仅仅只是一个地址。 说明: (1)空类型指针不能进行指针运算,不能进行间接引用,因为指针运算和间接引用都需要指针的类型信息。 (2)由于其他指针都包含有地址信息,所以将其他指针的值赋给空类型指针是合法的,反之,将空类型指针赋给其他指针则不被允许,除非进行显示转换。
4.void指针(续1) 例: int a=20; int * pr=&a; void * p=pr; //ok:将整型指针值赋给空类型指针pr=p; //error: 不能将空类型指针赋给其他指针pr=(int *)p; //ok:显示转换被允许
例5:空类型指针的应用。函数memcpy()在头文件mem.h中声明,它的功能为从源src中拷贝n个字节到目标dest中。 //ch8-15.cpp #include<iostream.h> #include<mem.h> //vc为memory.h void main() {char src[10]=“* * * * * * * * *”; char dest[10]; char * pc=(char *)memcpy(dest,src,10); cout<<pc<<endl; } 结果:* * * * * * * * *
4.void指针(续1) 说明: 函数 memepy()的原型为: void * memcpy(void *d,const void *s,sizet n); 其中:sizet为 unsigned int。该函数返回空类型指针dest的值,d形参值是主函数中的实参dest的数组首地址。
第八章 指针 8.1 指针概念 8.6 指针与函数 8.2 指针运算 8.7 字符指针 8.3 指针与数组 8.8 指针数组 8.1 指针概念 8.2 指针运算 8.3 指针与数组 8.4 堆内存分配 8.5 const指针 8.6 指针与函数 8.7 字符指针 8.8 指针数组
8.7 字符指针 主要内容: 1.字符数组和字符串常量 2.字符串常量的格式和特点 3.字符指针 4.字符串比较 5.字符串赋值
1.字符数组和字符串常量 在C++中,有两类字符串: 一类是字符数组,一类是字符串常量。 例: char buffer[]=“hello”;//字符数组 cout<<“good”<<endl; //字符串常量
1.字符数组和字符串常量(续1) 说明: (1)由引号(“”)标识,但不是用来初始化数组的字符串,是字符串常量。 上例中,“hello”用来给数组初始化,所以不是字符串常量。“good”是字符串常量。 (2)字符串常量的类型是指向字符的指针(字符指针char*),它与字符数组名同属于一种类型。字符串常量在内存中以’\0’结尾。这种类型的字符串称为C字符串,或ASCIIZ字符串。
2.字符串常量的格式和特点 (1)字符串常量通常存放在内存data区中const区。而字符数组是根据其数组存储的特点存放在相应的位置上。如果字符数组是全局变量,就存放在内存的data 区中全局或静态区,如果字符数组是局部变量,就存放在内存的栈区等。
2.字符串常量的格式和特点(续1) (2)当编译器遇到一字符串常量时,就把它放到字符串池中,以’\0’作结束符,记下其起始地址,在所构成的代码中使用该地址。这样,字符串常量就“变成”了地址。 (3)由于字符串常量的地址属性,所以两个同样字符组成的字符串常量的地址是不相等的。
Heap Stack data Const Common Data Code 字符串常量存放处 字符串常量的内存位置
例1:下面的程序不会输出”equal”字符串。 //ch8_16.cpp #include<iostream.h> void main( ) {if(”join”==”join”)cout<<”equal\n”; else cout<<”not equal\n”; } 结果:not equal 注意:程序中两个字符串的比较实质上是两个地址的比较。要使两个字符串真正从字面上进行比较,可以用库函数strcmp( )。
3.字符指针 字符串常量,字符数组名,字符指针均属于同一种数据类型。
例2:字符指针的操作。 //ch8_17.cpp #include<iostream.h> void main() {char buffer[10]=”ABC”; char * pc; pc=”hello”; //ok:将字符串常量的首地址赋给指针 cout<<pc<<endl; pc++; cout<<*pc<<endl; pc=buffer; cout<<pc; }
例2: (续) 结果: hello ello e ABC 注意: (1)输出字符指针就是输出字符串。所以输出pc时,便从’e’字符的地址开始,直到遇到’\0’结束。 (2)输出字符指针的间接引用,就是输出单个字符。当输出*pc时,便是输出pc所指向的字符。
4.字符串比较 两个字符串常量的比较是地址的比较。同理,两个数组名的比较也是地址的比较。
例3:下面的代码不会输出”equal”。 //ch8_18.cpp #include<iostream.h> void main() {char buffer1[10]=”hello”; char buffer2[10]=”hello”; if (buffer1==buffer2) cout<<”equal\n”; else cout<<”not equal\n”; } 结果:not equal
4.字符串比较(续1) 字符串比较应该是逐个字符一一比较,应使用标准库函数strcmp(),它在string.h头文件中声明,其原型为:int strcmp(const char * strl,const char * str2); 说明:其返回值如下: (1)当strl 串等于str2串时,返回值0; (2)当strl 串大于str2串时,返回一个正值; (3)当strl 串小于str2串时,返回一个负值。
例4:改进的程序ch8_18.cpp,使之成功地进行字符串比较。 #include<iostream.h> #include<string.h> void main() {char buffer1[10]=”hello”; char buffer2[10]=”hello”; if(strcmp(buffer1,buffer2)==0) cout<<”equal\n”; else cout<<”not equal\n”; } 结果:equal
5.字符串赋值 C++中不能直接对字符数组赋予一个字符串,原因是数组名是常量指针,不是左值。 说明:应使用标准函数strcpy( )来对字符数组进行赋值。strcpy()的声名在头文件string.h中,它的原型为: char * strcpy(char * dest,const char * src); 其中:strcpy()函数的返回值为dest值,一般都舍弃该值。
5.字符串赋值(续1) 例:下面的代码对字符数组进行赋值。 char buffer1[10]; char buffer2[10]; strcpy(buffer1,”hello”); strcpy(buffer2,buffer1);
5.字符串赋值(续2) 说明:函数strcpy( )仅能对‘\0’作结束符的字符数组进行操作。若要对其他类型的数组赋可调用函数memcpy( )。 例: int intarray1[5]={1,3,5,7,9}; int intarray2[5]; memcpy(intarray2,intarray1,5*sizeof(int));
第八章 指针 8.1 指针概念 8.6 指针与函数 8.2 指针运算 8.7 字符指针 8.3 指针与数组 8.8 指针数组 8.1 指针概念 8.2 指针运算 8.3 指针与数组 8.4 堆内存分配 8.5 const指针 8.6 指针与函数 8.7 字符指针 8.8 指针数组
8.8 指针数组 主要内容: 1.定义指针数组 2.指针数组与二维数组 3.指向指针的指针 4.NULL指针值
1.定义指针数组 定义:一个数组中若每个元素都是一个指针,则为指针数组。 例:定义一个字符指针数组,并对其初始化。 char * proname[] ={“Fortran”,“C”,“C++”}; 说明:指向这些字符串的指针是连续排列在指针数组中的。
2.指针数组与二维数组 指针数组与二维数组是有区别的。 指针所指向的字符串是不规则长度的; 二维数组中,每一列的大小必须一样,因此只能将数组中所要存储的字符串中最长列的大小作为数组列的大小。
分析上例: (1)指针数组: proname数组本身含有6个字节(假设每个指针占2个字节); proname[0]指向的字符串长8个字节(包括’\0’); proname[1]指向的字符串长2个字节; proname[2]指向的字符串长4个字节; proname所占内存总数为20个字节。 (2)二维数组: char name[3][8] ={“Fortran”,“C”,“C++”}; 共需3*8=24字节。
3.指向指针的指针 指针数组名是指向指针的指针(即二级指针)。 例:下面的代码描述了指向字符指针的指针。 char * pc[]={“a”,”b”,”c”}; char **ppc; ppc=pc; //ok 说明:这里的初始化值“a”,”b”,”c”必须为双引号,如果单引号则表示字符而不是字符串,字符没有’\0’的结束标记。字符总是占一个字节。
传递指针数组给函数就是传递二级指针给函数。 例1:把一个字符指针数组传递给函数。 //ch8_20.cpp #include<iostream.h> void Print(char *[],int); void main() { char * pn[]={”Fred”,”Barney”,”Wilma”,”Betty”}; int num=sizeof(pn)/sizeof(char *); Print(pn,num); } void Print (char * arr[],int len) { for(int i=0;i<len;i++) //输出各字符串 cout<<(int) arr[i]<<“ ” //输出字符串地址 <<arr[i]<<endl; //输出字符串
例1: (续) 结果: 4202628 Fred 4202633 Barney 4202640 Wilma 4202646 Betty 注意: (1)arr是指向字符指针的指针,所以 *arr是字符指针,*(arr+i)是arr开始的第i个字符指针,*(arr+i)就是arr[i]。 (2)arr是指针而不是数组。 (3)输出字符指针就是输出字符串。
4.NULL指针值 NULL是空指针值,它不指向任何地方。 注意:NULL与void* 是不同的概念,NULL是一个值,一个指针值,任何类型的指针都可赋予该值。而void * 是一种类型,是一种无任何类型的指针。
例2:通过对ch8_20.cpp中的指针数组设置NULL的方法进行处理。 #include<iostream.h> void Print(char * []); void main() {char*pn[]={“Fred”,”Barney”,”Wilma”,”Betty”,NULL}; Print(pn); } void Print(char * arr[]) {while(*arr!=NULL) //输出各字符串 { cout<<(int) *arr<<” ”<<*arr<<endl; arr++;}