




已阅读5页,还剩67页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第4章 数组、字符串和指针4第 章 数组、字符串和指针迄今为止,我们已经学习了所有重要的基本数据类型,对于如何在程序中执行计算和进行判断也有了基本的了解。本章将扩展前面所学的基本编程技术的应用范围,从此前使用单独的数据项扩展到处理数据项的整个集合。本章将讲述以下内容: 数组及其使用方法 如何声明和初始化不同类型的数组 如何声明和使用多维数组 指针及其使用方法 如何声明和初始化不同类型的指针 数组和指针之间的关系 引用的概念及声明方法,关于使用引用的几点初步建议 如何在本地C+程序中给变量动态分配内存 如何在CLR程序中动态分配内存 跟踪句柄和跟踪引用的概念,CLR程序中需要它们的原因 如何在C+/CLI程序中处理字符串和数组 内部指针的概念,创建和使用内部指针的方法本章将更多地使用对象,我们还没有详细分析如何创建对象,因此即使对此一无所知也不必担心。我们将从第7章开始详细学习类和对象。4.1 处理多个相同类型的数据值我们已经知道如何声明和初始化那些仅容纳单项信息的各种类型的变量 本书称之为数据元素。我们知道如何在char类型的变量中创建一个字符,如何在short、int或long类型的变量中创建一个整数,如何在float或double类型的变量中创建一个浮点数。最容易想到的对这些技术的扩展是用单个变量名引用特定类型的多个数据元素,这样我们将能够处理更宽范围的应用问题。在下面这个例子中,我们就需要这种技术。假设需要编写工资计算程序。为每个人的工资、应缴税款等信息使用单独命名的变量,这是一项艰巨的任务。处理此类问题的简便方法是使用某种类属名(比如employeeName)来引用员工,用其他类属名来引用与各个员工有关的数据,比如工资、应缴税款等。当然,我们还需要一种从全体员工中挑选出个别员工的方法,以及从相关的同类变量中挑选出数据的方法。这种需求随着程序中出现要处理的相似实体的集合而出现,这些实体可能是棒球运动员,也可能是战舰。自然,C+给我们提供了处理集合的方法。4.1.1 数组在ISO/ANSI C+中,数组是所有此类问题解决方案的基础。数组就是一组名为数组元素或简称元素的存储单元,各个存储单元可以存储相同数据类型的数据项,而我们可以用相同的变量名引用所有存储单元。工资计算程序中员工姓名就可以存储在一个数组中,各员工的工资可以存储在另一个数组中,而应缴税款可存储在第三个数组中。数组中各个数据项用索引值进行标识;索引值就是表示数组元素编号的整数。第一个元素的编号是0,第二个是1,依此类推。我们也可以将数组元素的索引值视为相对于数组中第一个元素的偏移量。第一个元素的偏移量是0,因此其索引值是0,索引值3指的是第四个数组元素。对工资计算程序来说,我们应该这样安排那3个数组:如果某个员工的姓名存储在employeeName数组中特定索引值标识的单元,则pay和tax数组应该在相同索引值引用的数组位置存储该员工的工资和应缴税款数据。数组的基本结构如图4-1所示。图 4-1图4-1是一个数组的结构图。数组height有6个元素,各元素存储不同的数值。这些数值是某个家庭中所有成员的身高(精确到英寸)。因为有6个元素,所以索引值为05。如果要引用某个元素,则应该先写出数组名称,然后在方括号内写上该元素的索引值。例如,height2将引用第三个元素。如果将索引值看作相对于第一个元素的偏移量,则很容易理解第四个元素的索引值是3。存储各个元素所需存储单元的数量取决于元素的类型,数组的所有元素都存储在连续的内存区域中。4.1.2 声明数组数组的声明方法基本上与此前所看到的变量声明的方法相同,唯一区别是应该在紧跟数组名的方括号内指出数组元素的数量。例如,我们可以用下面这条声明语句,声明图4-1中的整数数组height:long height6;因为每个long数值在内存中要占用4个字节,所以整个数组共需24个字节。数组长度只受运行该程序的计算机上内存总量的限制。数组可以被声明成任意类型。例如,我们可以用下面这条语句,声明两个用来存储一组发动机的体积和功率的数组:double cubic_inches10; / Engine sizedouble horsepower10; / Engine power output这两个数组可存储10台发动机的体积和功率,其索引值为09。正如前面在其他变量的声明语句中所看到的那样,我们可以用一条语句声明多个同类型的数组,但实践中在分开的语句中声明变量往往更合适。试一试:使用数组为练习如何使用数组,假设我们需要记录每次给汽车购买的汽油量和相应的里程表读数。我们可以编写程序来分析这些数据,以了解不同时间段的汽油消耗情况。/ Ex4_01.cpp/ Calculating gas mileage#include #include using std:cin;using std:cout;using std:endl;using std:setw;int main()const int MAX = 20; / Maximum number of valuesdouble gas MAX ; / Gas quantity in gallonslong miles MAX ; / Odometer readingsint count = 0; / Loop counterchar indicator = y; / Input indicatorwhile( (indicator = y | indicator = Y) & count MAX )cout endl gascount; / Read gas quantitycout milescount; / Read odometer value+count;cout indicator;if(count = 1) / count = 1 after 1 entry completed / . we need at least 2cout endl Sorry - at least two readings are necessary.;return 0;/ Output results from 2nd entry to last entryfor(int i = 1; i count; i+)cout endl setw(2) i . / Output sequence number Gas purchased = gasi gallons / Output gas resulted in / Output miles per gallon (milesi - milesi - 1)/gasi miles per gallon.;cout endl;return 0;程序假设每次都给油箱加满汽油,因此购买的汽油量就是行驶的里程所需的汽油消耗量。下面是本程序产生的输出:Enter gas quantity: 12.8Enter odometer reading: 25832Do you want to enter another(y or n)? yEnter gas quantity: 14.9Enter odometer reading: 26337Do you want to enter another(y or n)? yEnter gas quantity: 11.8Enter odometer reading: 26598Do you want to enter another(y or n)? n1.Gas purchased = 14.9 gallons resulted in 33.8926 miles per gallon.2.Gas purchased = 11.8 gallons resulted in 22.1186 miles per gallon.示例说明因为必须得到两次里程表读数的差值,才能计算用掉的汽油所能行驶的里程,所以我们只使用第一对输入值的里程表读数,而忽略第一次购买的汽油量,这些汽油是在以前行驶的路程中消耗掉的。在输出中显示的第二段时间内,交通状况必定相当糟糕,或者是经常踩刹车。用来存储输入数据的两个数组gas和miles的大小取决于常量MAX。通过改变MAX的值,我们即可使该程序适应最大数量不同的输入值集合。这种技术经常用来使程序灵活地适应要处理的信息量。当然,编写程序代码时必须考虑到const变量指定的数组大小或任何其他参数。不过,上述要求增加的难度不算大,因此没有任何理由不采用这种技术。我们稍后还将学习如何在程序执行时动态分配内存,从而不必再预先给数据分配固定数量的内存。输入数据while循环读取数据值。因为循环变量的范围是从0到MAX1,所以该程序不允许其用户输入超过数组容量的数据量。程序分别将变量count和indicator初始化为0和y,因此while循环至少执行一次。程序提示用户输入要求的各个数值,然后将输入值读入适当的数组元素中。用来存储具体数值的元素由变量count确定,第一次输入时该变量是0。我们以count作为索引值,在cin语句中指定数组元素,然后使count加1,从而为下次输入做好准备。输入各个数值之后,程序提示用户确认是否要输入另外的数值。输入的字符被读入indicator变量中,然后在循环条件中进行测试。如果没有输入y或Y,则循环终止,变量count小于指定的最大值MAX。输入循环结束以后(无论通过什么方法),count的值将比每个数组中最后输入的元素的索引值大1(记住,我们每次输入新元素之后都使该变量加1)。检查该变量可以验证是否至少输入了两对数值。如果不是,则程序将以一条适当的消息结束,因为计算里程至少需要两个里程表读数。生成结果输出是在for循环中产生的。控制变量i从1变化到count1,使程序计算当前元素milesi和前一个元素milesi1之间的差值,以作为本次行驶的里程。注意,数组的索引值可以是任何结果为整数的表达式,前提是该整数是相应数组的合法索引值 即从0到数组元素的数量减1。如果索引表达式的值不在对应于合法数组元素的范围之内,那么程序将引用一个错误的存储单元,其中可能包含其他数据、无用信息甚至程序代码。如果对错误元素的引用出现在表达式中,则程序将使用随机的一个数据值进行计算,这当然会产生意外结果。如果需要将某种结果存储在数组元素中,但使用的是非法索引值,则将覆盖位于该存储单元的任何数据。如果被破坏的数据是程序代码的组成部分,则结果是灾难性的。当我们使用非法索引值时,编译或运行过程中并没有任何警告产生。唯一能够避免此类错误的方法是用程序代码来防止其发生。cout语句为除第一对以外的所有输入值生成输出。程序还使用循环控制变量i,为每行输出生成一个行号。每加仑汽油行驶的英里数是在输出语句中直接计算的。在表达式中使用数组元素的方式与使用任何其他变量完全相同。4.1.3 初始化数组为了在声明时初始化数组,我们应该将逗号分开的初始化数值放入大括号内,然后将这样的初值集合放在数组名后面的等号之后。下面是声明并初始化数组的示例:int cubic_inches5 = 200, 250, 300, 350, 400 ;该数组的名称是cubic_inches,包括5个元素,各存储一个int型数值。大括号内初始化列表中的数值对应于连续的数组索引值,因此cubic_inches0的值是200,cubic_inches1的值是250,cubic_inches2的值是300,依此类推。指定的初始化数值不能比数组的元素多,但可以比数组的元素少。如果少,则列表中的初值被分配给从第一个元素(对应于索引值0)开始的连续元素。那些没有得到初值的数组元素被初始化为0。如果根本没有提供初始化列表,则情况就不是这样。如果没有初始化列表,数组元素包含的将是无用数据。另外,如果我们使用初始化列表,则列表内必须至少包括一个初值,否则编译器将生成一条出错消息。下面这个示例用来说明数组的初始化方法。试一试:初始化数组/ Ex4_02.cpp/ Demonstrating array initialization#include #include using std:cout;using std:endl;using std:setw;int main()int value5 = 1, 2, 3 ;int junk 5;cout endl;for(int i = 0; i 5; i+)cout setw(12) valuei;cout endl;for(int i = 0; i 5; i+)cout setw(12) junki;cout endl;return 0;该示例声明了两个数组。第一个数组value被部分初始化,但第二个数组junk完全没有被初始化。该程序生成两行输出,笔者计算机上的输出结果如下:1 2 3 0 0-858993460 -858993460 -858993460 -858993460 -858993460在您的计算机上,第二行值(对应于junk0到junk4的值)可能完全不同。示例说明数组value的前3个数值是初值,后两个是默认值0。在junk数组中,所有数值都是荒谬的,因为我们根本没有提供任何初值。那些数组元素保留着上次使用这些存储单元的程序遗留下来的内容。将整个数组初始化为0的便捷方法是仅指定一个初值0。例如:long data100 = 0; / Initialize all elements to zero这条语句声明数组data,并将全部100个元素初始化为0。第一个元素是用大括号内的数值初始化的,其余元素因语句中省略了相应的初值而被初始化为0。倘若提供初值的话,我们还可以省略数值型数组的长度。数组元素的数量由指定的初值数量决定。例如,数组声明语句int value = 2, 3, 4 ;定义的数组有3个元素,初值分别是2、3和4。4.1.4 字符数组和字符串处理char类型的数组被称作字符数组,通常用来存储字符串。字符串是附加有特殊字符(串尾标志)的字符序列。串终止字符表明字符串已经结束,该字符由转义序列0定义,有时被称为空字符,占用一个字节,其中8位全为0。这种形式的字符串经常被称作C型字符串,因为以这样的方式定义字符串是在C语言中推出的,后来Bjarne Stroustrup以C语言为基础开发出了C+。这不是唯一能用的字符串表示法,本书稍后将介绍其他表示方法。特别需要指出的是,C+/CLI程序使用一种不同的字符串表示法,而MFC定义了表示字符串的CString类。C型字符串在内存中的表示如图4-2所示。图 4-2 图4-2说明了内存中字符的表示形式,同时给出一种我们即将讨论的的字符串声明形式。注意:字符串中每个字符占用一个字节,因此算上最后的空字符,字符串需要的字节数要比包含的字符数多一个。我们可以用字符串字面值来声明并初始化字符数组。例如:char movie_star15 = Marilyn Monroe;注意,终止字符0是编译器自动添加的。如果在该字面值中显式添加0,则最终将得到两个空字符。但是,我们给字符数组指定元素数量时必须考虑到终止字符的存在。如图4-1所示,我们可以让编译器来算出已初始化的数组的长度。下面是另一个示例:char president = Ulysses Grant;因为长度没有指定,所以编译器将分配足够的内存空间来容纳该初始化字符串及终止字符。在本例中,编译器将给数组president分配14个元素。当然,如果希望稍后使用该数组来存储另一个字符串,则其长度(包括终止空字符)不能超过14个字节。通常,确保数组足以存储随后希望存储的任何字符串是编程人员的职责。我们也可以创建由Unicode字符组成的字符串,字符串中的字符类型为wchar_t。下面是一个创建Unicode字符串的语句:wchar_t president = LUlysses Grant;前缀L表示字符串字面值是一个宽字符串,因此字符串中的每个字符(包括终止空字符)都会占两个字节。当然,对字符串的索引会引用字符,而不是字节,因此president2对应于字符Ly。1. 字符串输入头文件包含许多从键盘上读取字符的函数定义。下面将看到的是getline()函数,该函数读取从键盘输入的字符序列,并将其以字符串形式(以0字符终止)存入字符数组中。我们通常像下面这样使用getline()函数:const int MAX = 80; / Maximum string length including 0char nameMAX; / Array to store a stringcin.getline(name, MAX, n); / Read input line as a string这些语句首先声明一个有MAX个元素的char型数组name,然后使用getline()函数从cin中读取字符。如上所示,数据源cin与函数名称之间有一个句点。句点表示我们在调用的getline()函数属于cin对象之一。getline()函数中各参数的意义在图4-3中给出。图 4-3 因为getline()函数的最后一个参数是n(换行符或行结束字符),而第二个参数是MAX,所以如果读到n字符,或者已经读入MAX1个字符,则无论哪种情况先发生,都停止从cin读取字符。可读取的最大数量是MAX1而不是MAX,因为数组中存储的字符序列要附加0字符。在键盘上按Return键将产生n字符,因此它通常是最方便的使输入终止的字符。当然,我们可以通过改变最后一个参数而指定其他字符。输入数组name中不存储n字符,但前面曾经说过,0字符将附加到数组中输入字符串的尾部。稍后讨论类时,我们将学习更多与这种语法形式有关的知识。在此期间,我们只需视之为理所当然,并在示例中使用它即可。试一试:编程处理字符串现在,我们已经有足够的知识来编写一个读取字符串并计算其中包含多少字符的简单程序。/ Ex4_03.cpp/ Counting string characters#include using std:cin;using std:cout;using std:endl;int main()const int MAX = 80; / Maximum array dimensionchar bufferMAX; / Input bufferint count = 0; / Character countcout Enter a string of less than 80 characters:n;cin.getline(buffer, MAX, n);/ Read a string until nwhile(buffercount != 0) / Increment count as long ascount+; / the current character is not nullcout endl The string buffer has count characters.;cout endl;return 0;该程序的典型输出如下所示:Enter a string of less than 80 characters:Radiation fades your genesThe string Radiation fades your genes has 26 characters.示例说明该程序首先声明一个字符数组buffer,然后显示一条提示用户输入的消息,之后将从键盘输入的字符串读入到buffer数组中。当用户按下Return键,或者已经读入MAX1个字符时,则结束从键盘读取字符的操作。while循环用来计算输入字符的个数。只要buffercount引用的当前字符不是0,该循环就继续执行。像这样一边在数组中逐步前进,一边检查当前的字符,是本地C+程序中常见的一种技术。该循环中的唯一动作是每看到一个非空字符,就使count加1。注意:库函数strlen()的功能和该循环的功能相同,这个函数将在本章后面介绍。在示例的最后,我们用一条输出语句来显示输入的字符串和字符数量。注意,使用转义字符是为了输出双引号。4.1.5 多维数组迄今为止,我们定义过的数组都只有一个索引值,它们被称为一维数组。数组还可以有多个索引值,这样的数组被称作多维数组。假设有一块农田,我们在上面种植了豆类作物,每行10株,共12行(因此总共有120株)。我们可以使用下面这条语句,声明一个数组来记录每株作物的产量:double beans1210;这条语句声明了一个二维数组beans,第一个索引值是行号,第二个索引值是行内的编号。引用任何一个元素都需要两个索引值。例如,我们可以用下面的语句,设定对应于第三行第五株作物的那个元素的值。beans24 = 10.7;记住,索引值是从0开始的,因此行索引值是2,行内第五株作物的索引值是4。农民可能有好几块相同的农田,上面以相同的模式种植着豆类作物。假设有8块农田,那么可以用三维数组来记录产量,其声明语句如下:double beans81210;该数组可以记录每块农田内所有作物的产量,最左边的索引值引用具体的田块。如果我们的豆类种植业已经达到国际规模,则可以使用四维数组,增加的那一维用来表示国家。数组在内存中的存储方式是使最右边的索引值最快地变化。因此,数组data34是3个各自包含4个元素的一维数组。该数组的排列如图4-4所示。正如图4-4中的箭头所指示的那样,数组元素存储在连续的内存区域中。第一个索引值选择数组的某一行,第二个索引值选择这一行内的元素。注意,本地C+中的二维数组实际上是一维数组的一维数组。三维的本地C+数组实际上也是一维数组,其中各个元素又都是一维数组的一维数组。多数时候,这不是我们需要担心的事情,但正如稍后将看到的那样,C+/CLI数组不是这样。就图4-4中的数组而言,这意味着表达式data0、data1和data2都是一维数组。数组元素存储在连续的区域中图 4-4 1. 初始化多维数组为了初始化多维数组,我们需要扩展原来初始化一维数组的方法。例如,我们可以用下面这条声明语句初始化二维数组data:long data24 = 1, 2, 3, 5 , 7, 11, 13, 17 ;如上所示,该数组中每行的初值都被包围在各自的大括号内。因为每行有4个元素,所以每组有4个初值,因为data数组共有两行,所以外层大括号内共有两组初值,组与组之间用逗号分开。我们可以在某行内省略某些初值,这样未获得初值的数组元素将被初始化为0。例如:long data24 = 1, 2, 3 , 7, 11 ;上面为省略掉的初值留出了空间,以便使读者看清省略初值的位置。元素data03、data12和data13没有初值,因此值是0。如果希望将整个数组初始化为0,我们只需写成:long data24 = 0;如果要初始化更多维的数组,则记住一点:数组中有多少维,初值组就需要多少层嵌套的大括号。试一试:存储多个字符串我们可以使用单个二维数组存储多个C型字符串。下面就是一个这样的示例程序:/ Ex4_04.cpp/ Storing strings in an array.#include using std:cout;using std:cin;using std:endl;int main()char stars680 = Robert Redford,Hopalong Cassidy,Lassie,Slim Pickens,Boris Karloff,Oliver Hardy;int dice = 0;cout endl Pick a lucky star! dice;if(dice = 1 & dice = 6) / Check input validitycout endl / Output star name Your lucky star is starsdice - 1;elsecout endl / Invalid input Sorry, you havent got a lucky star.;cout endl;return 0;示例说明本示例的要点是数组stars的声明。stars是二维数组,元素的类型为char,该数组可以容纳6个字符串,各字符串可包含80个字符(包括编译器自动添加的终止空字符)。该数组的初始化字符串被包围在大括号之间,相互以逗号分开。注意:以这种方式使用数组的缺点之一,是几乎总有一些未被使用的存储单元。本例中所有字符串都小于80个字符,因此数组中各行剩余的元素都被白白浪费掉了。我们也可以省略数组第一维的大小,而让编译器来算出有多少个字符串,声明如下:char stars80 = Robert Redford,Hopalong Cassidy,Lassie,Slim Pickens,Boris Karloff,Oliver Hardy;这样声明将使编译器根据指定的初始化字符串的个数确定第一维的大小。因为有6个字符串,所以结果是完全相同的,但是这样可以避免出错的可能性。在这里,我们不能将第二维也省略掉。在二维或者多维数组中,最右边的一维必须是确定的。注意:这条声明语句最后的分号不能省略。当为数组提供初值时,人们很容易忘记应该以分号结束。在下面语句中需要引用某个字符串的位置,我们只需指定第一个索引值:cout endl / Output star name Your lucky star is starsdice - 1;单个索引值将选择特定的有80个元素的子数组,输出操作将显示出子数组中终止空字符之前的所有内容。指定的索引值是dice1,因为dice的值是从16,而索引值无疑应该是从05。4.2 间接数据存取前面讨论的变量使我们能够命名某个存储单元,然后在其中存储特定类型的数据。变量的内容或者是从某种外部设备(比如键盘)输入的,或者是根据其他输入的数值计算出来的。C+中还有一种变量不存储输入或计算出来的数据,但却大大扩充了程序的功能和灵活性。此类变量被称为指针。4.2.1 指针的概念用来存储数据的各个存储单元都有地址。地址给PC硬件提供了引用具体数据项的途径。指针变量存储特定类型的另一个变量的地址。就像其他变量一样,指针变量也有名称,而且指针还有类型,以指出其内容引用的是什么类型的变量。注意,指针变量的类型隐含着该变量是指针的事实。如果某个指针变量的内容是包含int类型数值的存储单元的地址,则该变量的类型是“指向int类型的指针”。4.2.2 声明指针除了在名称前面用星号表明是指针变量以外,指针的声明与普通变量类似。例如,为了声明long类型的指针pnumber,我们可以使用下面的语句:long* pnumber;该语句中星号离类型名称更近。如果愿意,我们也可以将其写成下面的形式:long *pnumber;编译器根本不介意我们怎样写,但变量pnumber的类型是“指向long类型的指针”,因此使星号离类型名称更近往往能更清楚地表明这一点。无论选择哪种书写指针类型的方法,结果都相同。我们可以在同一条语句中混合普通变量和指针的声明。例如:long* pnumber, number = 99;像前面一样,这条语句声明了变量pnumber,其类型为“指向long类型的指针”,同时还声明了long类型的变量number。一般来说,将指针的声明同其他变量分开更好,不然可能使人对所声明变量的类型产生误解 尤其当星号离类型名称更近时。下面的语句看起来更清楚,而且将声明分开写在两行上使我们能够分别给它们添加注释,这有助于使程序更易于理解。long number = 99; / Declaration and initialization of long variablelong* pnumber; / Declaration of variable of type pointer to long在C+中,使用以字母p开始的变量名表示指针是通用惯例,这使我们能更容易看清程序中哪些变量是指针,从而使程序更易于理解。下面我们先举例说明指针的工作过程,暂时先不管指针有什么用途。我们很快就将学习如何使用指针。假设有一个long类型的变量number(上面声明过),其包含的数值是99,还有一个变量pnumber,其类型为“指向long类型的指针”,可用来存储变量number的地址。但是,我们如何获得变量的地址呢?取址运算符我们需要的是取址运算符&。该运算符是一元运算符,用于获得变量的地址,又名引用运算符 本章稍后再讨论如此命名的原因。为了设置刚才讨论的指针,可以使用下面这条赋值语句:pnumber = &number; / Store address of number in pnumber该操作的结果如图4-5所示。我们可以使用&运算符获得任何变量的地址,但需要有适当类型的指针来存储地址。例如,如果我们希望存储double型变量的地址,则相应的指针必须被声明为double*类型,即“指向double类型的指针”。图 4-5 4.2.3 使用指针如何获得变量的地址,并将其存入某个指针变量都已经讲解清楚了,但真正有趣的是如何使用指针。指针的重要用途是存取指针所指变量中的数据值,这需要使用间接运算符*。1. 间接运算符将间接运算符*与某个指针一起使用,可以存取该指针指向的变量的内容。“间接运算符”的名称源于间接存取数据的事实。该运算符又名解除引用运算符,而存取指针所指的变量中数据的过程被称为解除对指针的引用。该运算符可能使人混淆,因为相同的符号*现在有多种不同用途。星号*是乘法运算符,还是间接运算符,此外还用于指针的声明。我们每次使用*符号时,编译器都能根据上下文区分其意义。当我们使两个变量相乘时 比如A*B,该表达式除了是乘法操作以外,没有任何其他有意义的解释。2. 需要使用指针的原因为什么一定要使用指针?毕竟,取出我们已经知道的某个变量的地址,然后将其放入某个指针内,以便能够解除对该指针的引用,看起来似乎是完全不必要的额外开销。但指针实际上非常重要,原因不止一种。我们可以使用指针符号处理数组中存储的数据,相应的执行速度往往比使用数组符号要快。另外,当我们稍后学习如何定义自己的函数时,将看到指针被广泛使用,以便能够在函数内部访问在函数外部定义的大块数据 比如数组。但最重要的一点是使我们能够动态地 即在程序执行过程中,给变量分配存储空间。这种功能使程序可以根据实际的输入,来调整自己的内存使用量。因为我们预先不知道将动态创建多少个变量,所以首选方法是使用指针。因此,我们应该确保真正掌握指针的用法。试一试:使用指针在下面的示例中,我们可以试一试指针操作的各种用法。/Ex4_05.cpp/ Exercising pointers#include using std:cout;using std:endl;using std:hex;using std:dec;int main()long* pnumber = NULL; / Pointer declaration & initializationlong number1 = 55, number2 = 99;pnumber = &number1; / Store address in pointer*pnumber += 11; / Increment number1 by 11cout endl number1 = number1 &number1 = hex pnumber;pnumber = &number2; / Change pointer to address of number2number1 = *pnumber*10; / 10 times number2cout endl number1 = dec number1 pnumber = hex pnumber *pnumber = dec *pnumber;cout endl;return 0;在笔者计算机上,本示例产生下面的输出:number1 = 66 &number1 = 0012FEC8number1 = 990 pnumber = 0012FEBC *pnumber = 99示例说明本示例没有输入,所有操作都是用变量的初值执行的。将number1的地址存入指针pnumber之后,number1的值通过下面这条语句中的指针而间接增加:*pnumber += 11; / Increment number1 by 11注意:当首次声明指针pnumber时,我们将其初始化为NULL。我们将在下一小节讨论指针初始化的问题。间接运算符使我们将pnumber所指变量 即number1 的内容加上11。如果忘记在这条语句中写上*号,则计算机将使该指针存储的地址加上11。number1的值以及存储在pnumber中的number1的地址都被显示出来。程序使用hex操作符,以生成十六进制形式的地址输出。通过使用hex操作符,我们可以使普通整型变量的值也以十六进制的形式输出。只要我们以与发送endl完全相同的方式将hex发送到输出流中,则所有后面跟着的输出就都是十六进制。如果我们希望后面的输出再变为十进制,则需要在下一条输出语句中使用dec操作符,将输出再次切换回十进制模式。在第一行输出之后,pnumber的内容被设置为number2的地址。变量number1的值则变为number2的10倍:number1 = *pnumber*10; / 10 times number2计算是借助于指针通过间接访问number2的内容进行的。第二行输出显示出计算的结果。您在输出中看到的地址值可能与上面的示例输出完全不同,因为这些值反映的是内存中加载该程序的位置,这取决于操作系统的配置情况。地址值的前缀0x表明它们是十六进制数。注意,地址&number1和pnumber(包含&number2)相差4个字节。这表明number1和number2占用着相邻的存储单元,因为每个long类型变量占用4个字节。4.2.4 初始化指针使用未初始化的指针是相当危险的。我们很容易通过未初始化的指针改写随机的存储区域,由此产生的后果完全取决于我们的运气。将指针初始化为某个已定义变量的地址非常简单。从下面可以看出,我们只需在变量number的名称前面加上&运算符,即可将指针pnumber初始化为该变量的地址。int number = 0; / Initialized integer variableint* pnumber = &number; / Initialized pointer记住,当我们用另一个变量的地址初始化某个指针变量时,必须在指针声明之前已经声明过该变量。当然,我们有时不想在声明指针时将其初始化为具体变量的地址。这种情况下,可以将其初始化成等于0的指针。为此,Visual C+提供了已经被定义成0的符号NULL,因此我们可以使用下面这条语句来声明并初始化指针,而不必像上面的示例那样:int* pnumber = NULL; / Pointer not pointing to anything这样可以确保该指针不包含任何有效地址,同时赋予指针一个可以在if语句中检查的值,比如:if(pnumber = NULL)cout endl pnumber is null.;当然,我们也可以用0显式初始化指针,这同样能够确保赋给指针一个不指向任何对象的值。系统不可能给任何对象分配地址0,因此将0用作地址实际上表明该指针没有任何目标。尽管这样做确实有点儿不太清晰,但如果我们希望用其他编译器来编译自己的代码,那么使用0来初始化那些应该为空值的指针更可取。注意:这样做也更符合目前ISO/ANSI C+中的良好习惯,即如果我们有一个有名称的C+对象,那么该对象应该属于某个类型。但NULL不属于任何类型,它只是0的别名。稍后我们将看到,在C+/CLI中情况与此不同。要使用0作为指针的初值,我们只需这样编写代码:int* pnumber = 0; / Pointer not pointing to anything为了检查某个指针是否包含有效地址,可以使用下面的语句:if(pnumber = 0)cout endl pnumber is null.;同样,也可以使用这条语句:if(!pnumber)cout endl pnumber is null.;该语句与前面示例的作用完全相同。当然,我们还可以使用下面的形式:if(pnumber != 0)/ Pointer is valid, so do something useful注意:NULL指针指向的地址包含无用数据。我们永远不应该解除对某个空指针的引用,因为那样将使程序立即终止。1. 指向char类型的指针char*类型的指针具有非常有意思的属性,即可以用字符串字面值初始化。例如,我们可以用下面的语句声明并初始化这类指针:char* proverb = A miss is as good as a mile.;该语句乍看起来类似初始化char数组,但二者有细微的区别。这条语句用引号之间的字符串创建某个以/0终止的字符串字面值(实际上是const char类型的数组),并将该字面值的地址存储在指针p
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025合同范本雇佣短期合同律师合同参考样本
- 商业步行街场经营管理合同
- 消费电子厂商与经销商渠道拓展合作协议
- 2025合同模板 股权转让协议条款明确适合修改使用或参考范本
- 2025购买住宅用地定金合同模板
- 疼痛诊疗学(医学高级):运动系统疾病题库考点(强化练习)
- 小学二年级心理健康教育教案
- 四大规划面试题目及答案
- 电气火灾试题及答案
- 新闻宣传业务试题及答案
- 2025年数字重庆大数据应用发展有限公司招聘笔试参考题库附带答案详解
- 2025年春季《中华民族共同体概论》第三次平时作业-国开(XJ)-参考资料
- 会计领军笔试题目及答案
- 2025年四川省成都市青羊区中考二诊化学试题(原卷版+解析版)
- (人教版)2025年中考化学真题试题(含解析)
- PVC拆除施工方案
- 2025年托育服务宣传月活动总结(普惠托育科学育儿)
- 中考数学复习-中档题训练(四)(含答案)
- 医学实验室质量控制知识试题及答案
- 驾驶员消防安全培训
- 沪教版五年级英语下册期末复习总结
评论
0/150
提交评论