而必须将数组中的元素逐个地输出到屏幕。
6.3.3数组作为函数的参数
要想将数组传递给函数,函数形式参数中的数组必须给出数组的基类型以及左右方括号“[]”,这样编译程序才能理解这个参数是一个数组类型的变量,而不是其他数据类型。
由于在数组声明时已确定了这个名字的数组长度,所以在形式参数中没有必要再次给出数组的大小。
C++语言也允许在方括号中写上数组的长度,但在方括号中写任何数字或没有数字都可以正常工作,这些数字仅起一个注释作用。
在C++语言中,编译程序对数组进行处理时,自动将数组名转化为指向这个数组首元素的指针(数组首元素的地址)。
所以,在函数调用时,只须给出数组的名字作为实际参数。
所有数组都是按引用被自动传递过去(实际上是它们起始地址的一个副本),因此如果给一个函数传递一个数组作为实际参数,并且在这个函数中改变了数组元素的值,那么,函数调用返回时这些改变仍然保持着。
按值调用的参数传递方式的最大好处是函数调用没有副作用,因为被调用函数的执行结果不影响调用程序中的数据。
但数组作为函数参数时是按引用调用的,如果不想再函数体中改变数组元素的值,如何保证函数调用没有副作用呢?
可以在函数原型中数组参数前加上const修饰符,以显式指明调用该函数对数组是没有副作用的,即数组中的任何元素对不能再改函数体中改变。
6.3.4一维数据应用举例
冒泡排序法:
将相邻的两个数据做比较,把较小的数据交换到前面
6.3.5二维数组的声明
如果一个数组的基类型是一维数组,则称这种类型为二维数组。
二维数组需要两个参数方可确定数组内的一个数组元素。
声明二维数组的一般形式为:
类型数组名[常量表达式][常量表达式];
intscores[50,3];//语法错误“50,3”是逗号表达式,相当于intscores[3];
为二维数组分配存储空间可以有以行为主和以列为主两种。
以行为主(以行优先)是一行一行地存,各行按列次序连续存储,一行存完后再存下一行。
以列为主(以列优先)是一列一列地存,各列按行次序连续存储,一列存完后再存下一列。
在C++中,一律采用”以行为主”的存储形式。
6.3.6二维数组元素的引用与初始化
intmatrix[3][4]={{1,2,3,4},{1,3,5,7},{2,4,6,8}};
intmatrix[3][4]={1,2,3,4,1,3,5,7,2,4,6,8};
intmatrix[3][4]={{1,2},{2,3,5}};
与一维数组相似,在C++语言中,编译程序对二维数组进行处理时,自动将数组名转化为指向这个数组首元素的指针(数组首元素的地址)
6.3.7二维数组应用举例
符号常量的值是不可修改的,所以使用全局符号常量并无什么副作用。
一维数据作为参数时,可以不给出数组的大小;但二维数组作为形式参数则必须给出数组的大小(或只给出第二维的长度)
6.3.8指针与数组
指针类型变量可以当做数组使用,数组类型变量也可当做指针使用。
C++语言的数组名字只是一个指针而已。
虽然数组名字也是一种指针类型,但它是一种常量指针类型,其值是不可改变的。
C++语言允许以指针形式访问数组元素正是需要利用指针是变量这一特点来弥补数组名字作为常量指针的不足,以提高处理数据(特别是一组连续数据)的能力和简化对数据的操作方式。
6.3.9指针数组与数组指针
指针数组是指基类型为指针类型的一个数组,而数组指针是指基类型为数组类型的一个指针。
如果需要保留许多指针的值,可以使用指针数组。
如:
int*ptr_array[10];
[]的优先级高于*,所以ptr_arrary先于[10结合,组成长度为10的数组形式,然后才决定这个数组中的每一个元素师指针类型(int*).。
如果写成int(*array_ptr)[10];
那么声明的将是一个指针类型的变量array_ptr,它指向一个长度为10的整数类型数组。
6.4字符串
字符串实际上是基类型为字符的一个特殊数组,它与普通字符数组的区别在于字符串最后有一个0值字节表示串结束。
6.4.1字符串常量与变量
字符串”A”与字符’A’的区别,编译程序为字符’A’只分配一个字节的存储空间,而为字符串”A”分配两个字节,其中一个字节存放A的ASCII编码,第二个字节存放串结束标志”\0”。
C++语言在处理字符串时,全靠这个结束标志才知道字符串的终止位置,因为括住字符串的双引号并没有存储起来,不能用它们为字符串确定边界。
字符串的长度是指字符串中所有字符的总和,包括其中的空格以及其他转义字符在内,尽管转义字符在字符串中占了几个位置,但只当做一个字符计算长度。
一个字符串常量占用的存储空间是其长度加1个字节,因为最后还要存储一个串结束标志。
字符串变量是一个基类型为字符类型的数组变量,同样遵循“先声明、后使用“的原则。
字符串定义的一般形式为:
Char数组名[元素个数];
在声明字符串常量时,可以字符串常量进行初始化,如:
charname[30]=”ZhongshanUniversity”;
这时编译程序会为字符串变量name分配30个字节的存储空间,并将”ZhongshanUniversity”中20个字符的ASCII码存储在前20个字节中,然后在第21个字节中放一个”\0”表示串结束,至于第22个至第30个字节是什么内容是不关心的。
字符串变量占用的存储空间大小与其中存放的字符串的长度之间没有相等关系,尽管name中存放的字符串长度为20,实际使用了21个字节,但它仍然占用着30个字节存储空间。
鉴于上述存储空间组织方式,在初始化字符串变量时一定要注意字符串长度不要超过其存储空间大小,如:
charname[20]=”ZhongshanUniversity”;
可能会引起严重问题,因为字符串的结束标志”\0”超出了为它分配的存储空间变价,可能破坏了其他数据。
C++语言为避免上述字符串越界错误,也帮助程序员提供了一条捷径,即不必声明字符数组的长度,由编译程序根据初始化的字符串常量的长度自动决定。
如:
charname[]=“ZhongshanUniversity”;
对于这个声明,编译程序会根据字符串常量的长度为name分配21个字节,相当于
charname[]=“ZhongshanUniversity”;
由于字符串是一个字符类型的数组,所以既可以用数组下标方式访问字符串中的各个字符,也可用指针方式访问。
逐个访问字符串中的字符时,一般使用循环语句for或while并且以判断字符是否为’\0’作为循环结束条件。
6.4.2字符串数组
constintmax_subjects=24;
char*subjects[max_subjects];
6.4.3关于字符串操作的库函数
与其他类型的数组一样,只可在声明字符串变量时用赋值运算作初始化,在其他地方不允许直接使用赋值运算,如:
charname[30];
name=“ZhongshanUniversity”;//语法错误
因为name实际上是一个常量指针,不可用赋值语句改变其值,即name不允许作为一个”左值”。
可以每次使用循环语句逐个字符赋值一个字符串,或比较两个字符串的每一个字符是否相等,但这一类常用操作已由C++系统提供,只要使用以下语句即可完成字符串的复制:
strcpy(name,”ZhongshanUniversity”);
这些常用函数组织成标准库的形式供程序员重用,该标准库的名字为string。
#include
(1)unsignedintstrlen(constchar*str);
返回字符串str中字符个数,包括其中的空格与转义字符,但不包括结束标记’\0’
(2)char*strcpy(char*str1,constchar*str2);
将str2指向的字符串复制到str1指向的位置并返回str1。
注意为str1分配的存储空间必须能放下str2指向的字符串。
以下是初学者常犯的错误:
char*str;
strcpy(str,”Becareful”);//可能引起严重的问题
因为str所指向的存储空间地址是不确定的,我们还没有让这个指针指向一块已分配到的存储空间。
char*str;
str=“Becareful.”;
因为字符串常量已经分配了存储空间,赋值后指针str指向这一空间。
charstr[20];
strcpy(str,”Becareful”);
因为str已经静态地分配了20个字节的空间,所以可以将字符串常量复制到这一存储空间。
注意为str静态或动态申请的存储空间大小不可小于12个字节,否则会出现越界写入错误。
(3)char*strcat(char*str1,constchar*str2);
将字符串str2连接到字符串str1后,形成一个新字符串,原先str1的结束标记’\0’被取消。
函数返回值为str1。
注意为字符串常量str1分配的存储空间一定要足够大,能够容纳两个字符串连接后的新字符串。
(4)intstrcmp(constchar*str1,constchar*str2);
比较两个字符串str1和str2的内容是否相同(按字典排序方法)。
如果str1小于str2则返回负数,str1等于str2则返回零,str1大于str2则返回整数。
(5)char*strchr(constchar*str,intch);
寻找字符串str中第一出现字符ch的位置。
如果找到ch,则返回指向该位置的指针,否则返回空指针。
(6)char*strstr(constchar*str1,constchar*str2);
寻找字符串str2在字符串str1中第一次出现的位置,不包括str2的结束标记’\0’。
如果找到str2,则返回指向该位置的指针;否则返回空指针。
6.4.4字符串与指针数组应用的例子(主函数带参数)
程序员写出源程序之后,要利用编译程序吧源程序转换为机器语言程序,再利用连接程序把编译所得的机器语言程序组织处理成为可装入内存并执行的可执行机器语言程序(或称可执行文件),最后才能执行这个程序。
得到了可执行目标程序之后,在操作系统环境下,直接输入可执行文件名便可以执行这个程序,不必每次都从源程序编译、连接后才去执行。
主函数带参数
stdlib.h
(1)把指针s所指向的数字字符串转换为整数(int):
intatoi(constchar*s);
(2)把指针s所指向的数字字符串转换为实数(double):
Doubleatof(constchar*s);
(3)把指针s所指向的数字字符串转换为长整数(long):
longatoll(constchar*s);
主函数带参数主要有以下两种方式:
intmain(intargc,char*argv[]);
intmain(intargc,char*argv[],char*env[]);
其中argc是一个整数,表示传给主函数的命令行参数个数,其实际值是实际参数个数加1(把命令本身也当做一个参数);
argv是一个指针数组,argv[0]指向命令(包括路径);argv[1]指向执行程序名后的第一个参数,argv[2]为执行程序名后的第二个参数,……,依此类推。
env也是一个指针函数,env[]的每一个元素将指示相关的环境值(作为一般用户实际没有必要去详细了解它)。
6.5指向对象的指针
对象一经声明就为对象的成员分配踩踩空间,并调用其构造函数进行初始化,对象生存期结束时自动调用其析构函数,并释放对象占用的存储空间。
一种较好的解决途径是先声明这些对象,但并不立即分配存储空间和调用构造函数,在需要这些对象时才这样做,使用完这些对象后立即调用其西沟函数并释放其占用的存储空间,而不是留待对象生存期接受才有C++系统自动回收存储空间。
这一途径是使用C++语言指向对象的指针。
由于使用对象指针比直接使用对象更加灵活、方便,所以应尽量使用对象指针代替对象。
特别是作为函数参数或返回值时,使用对象指针比使用对像本身具有更清晰的语义。
6.5.1对象指针
如果声明的一个指针的基类型是一个类类型,那么这个指针称为对象指针,它指向一个对象。
如:
CIRCULAR_NUMBERS*angle_ptr;
注意这时C++仅仅为这个指针分配了存放指针值的存储空间,并没有分配存放一个属于CIRCULAR_NUMBERS类型的对象空间。
用运算符“->”访问这个对象的公有数据成员或成员函数。
如:
angle_ptr->increment();
相当于:
angle.increment();
在C++语言一个类的成员函数体中,都隐含提供了一个对象指针,这个对象指针用保留字this命名,它指向该成员函数正在操作的对象。
保留字this不常用,只是在需要返回当前对象或指向当前对象的指针时才用到this.
6.5.2对象的动态创建与撤销
C++语言使用new和delete运算符完成对象的动态创建与撤销。
运算符new的一般形式为:
指针=new类型名(初始化表);
其中,初始化表及其括号是可选内容,类型既可以是基本数据类型,也可以是类类型。
如果是类类型,则初始化表相当于将实际参数传递给该类的构造函数。
例如:
int*int_ptr;
int_ptr=newint;
又如:
char*string;
string=newchar[40];char*string=newchar[40]l
注意这里使用方括号与静态声明一个数组时使用方括号的区别:
声明数组时方括号内只允许出现常量表达式,而new则允许在表达式在使用变量。
分配了存储空间后才能使用strcpy()函数,如:
strcpy(string,”ZhongshanUniversity”);
以上动态分配的string与以下静态分配的string2:
charstring2[40];
均占用40个字节的存储空间,都可以使用strcpy函数从另一个字符串复制过来。
但string是一个指针变量,而string2是一个常量指针,它不能指向其他的字符串,例如:
string=string2;
是合法的,而
string2=string;//错误
则是不合法的。
对于类类型也可以用new分配存储空间,如:
CIRCULAR_NUMBERS*angle_ptr;
angle_ptr=newCIRCULAR_NUMBERS(0,359,250);
在类型名字后用圆括号括住构造函数的实际参数,在new操作是angle_ptr所指向对象的构造函数才被调用,并且完成存储空间的分配。
如果我们设计的类没有构造函数,那么new后只需要接着类的名字,圆括号与初始化表均不出现。
由new分配存储空间并不保证每一次都成功。
如果new分配存储空间失败,则返回给指针的值是一个空指针’\0’,即一些标准库中定义的名字NULL。
如果这时不考虑new操作是否成功,继续使用这个指针是很危险的。
由经验的程序员在每次new操作后,都会判断内存分配是否成功,然后再决定下一步的操作,其模式如下:
CIRCULAR_NUMBERS*angle_ptr;
…
angle_ptr=newCIRCULAR_NUMBERS(0,359,250);
if(angle_ptr==NULL)//内存分配失败后的处理if(ngle_ptr)
{
….
}else{//内存分配成功后的处理
../
}
如果不再需要所分配的存储空间,可以使用delete运算符释放它们。
如:
deleteint_ptr;
deletestring;
第一条语句释放了指针int_ptr占用的存储空间,*int_ptr变得没有意义的了。
第二条语句释放了string所指向的40个字节,string将不再存放字符串内容。
注意用delete释放空间后,指针的值仍是原来指向的地址,而不是空值NULL。
不能通