Unix/Linux C++应用开发-C++基础概念"数组、指针和字符串"

来源:互联网 发布:小知科技 alex 编辑:程序博客网 时间:2024/05/16 08:16

数组、指针和字符串是C++语言中很重要的概念。将三个重要的概念放在一起讲述,是因为它们之间有着千丝万缕的联系。软件编程中经常结合三者在一起,从而解决实际问题。下面各个章节将会具体通过相应实例讲述其基本概念、使用方式以及之间的基本关系。

6.1 数组

C++数组是一种复合式的数据类型,用于存储连续的相同类型的数据。数组可以使用所有的C++数据类型,如intfloatdoublecharstruct等,包括类对象在内。所有的数据都存储在连续的内存空间中,位置基本固定,这使得数据的随机访问速度更快。下面将会详细讲述数组的基本概念以及相应的操作方式。

6.1.1  数组定义

数组可以看作为存放相同类型数据的一个集合。通常将数组根据维数划分为一维、二维…N维数组。最简单的一维数组通常表示数学中的向量概念,C++中一维数组一般语法定义如下所示。

Data_type ArrayName[length];

作为C++的一种复合类型,数组与其它数据类型的变量一样。数组在使用前必须作声明、定义,这样才会让随后的程序认识该变量,并且通过定义为其分配相应的内存空间。Data_type说明了数组的类型,即表示该数组中元素的类型。前面已经讲述过数组是存储同类型元素的顺序结构,那么Data_type说明了数组中所有的元素的数据类型。ArrayName为数组的名字,它可以使用除了C++关键字以外的标识符。length表示数组中存放元素的个数,也可以称为数组的长度。

下面通过如下几个数组定义的实例了解数组定义的基本概念,实例如下所示。

int array1[100];                    //定义整型数组,该数组拥有100个整型元素

char array2[100];                //定义字符型数组,该数组拥有100个字符型元素

double array[100];              //定义double型数组,该数组拥有100double型元素

上述几个实例说明采用C++不同的数据类型作为数组元素类型定义数组,同时指定数组的大小。该数组元素的大小必须是常量表达式。了解了数组的基本定义后,下面开始了解数组的初始化以及其元素访问。一元数组的初始化,采用一对大括号表示赋值数据的边界,在大括号的内部用逗号作为数据元素分隔符,按照一定的顺序来赋值。下面通过几个数组赋值的实例来熟悉一下数组初始化的应用。

int arrary[5] = {1,2,3,4,5};   //定义五个整型元素的数组,并且使用整型值12345分别给数组中的元素赋值

在代码中,采用初始化赋值的方式一次性的将数组中元素都依次赋值,数组赋值的顺序是从左至右,不足使用0来补齐。示例如下:

int arrary[5] = {1,2,3};         //定义五个整型元素的数组,并且使用整型值123分别给数组前三个元素赋值,后两个元素默认使用0赋值

对于数组初始化,不一定非要给出其明确的长度,数组会自动计算赋值的元素个数。这种初始化方式使用实例如下所示。

int array[] = {1,2,3,4};                   //定义4个整型的元素数组,并且使用整型值给其赋值

以上两种数组显式初始化的方式有点不同于第一种。第二种方式是显式定义数组长度,使用整型值依次从左至右给数组赋值,不足位数的直接使用默认0赋值。第三种方式由于数组实现了显式初始化,在确定显式的初始化元素个数后,数组的长度不必须提供出来。此时会根据初始化元素的个数来确定数组大小。

另外这里需要注意的是,数组之间并不能像C++的基本数据类型变量那样互相初始化以及赋值。如果需要互相赋值,则可以将数组中元素拷贝到其对应类型的数组中,并且需要按照顺序一个一个实现元素的拷贝。

6.1.2  字符数组

C++数组通常通过下标来访问元素,数组的下标规定从0开始依次递增。开发者可以通过指定的数组下标来访问该位置的元素。同样开发者也可以采用下标来给该数组的元素赋值或者初始化。使用数组下标赋值以及访问元素方式如下实例所示。

int array[5];                 //声明拥有5个整型元素的数组array

array[1] = 2;                //通过下标指定将整型值2赋值给数组第二个元素

int value = array[1];   //通过下标访问,将数组中第二个元素的值赋给整型变量value

以上的实例操作通过数组指定下标进行数组元素初始化,并且通过下标访问数组元素值用来供程序使用。另外数组可以定义成字符以及字符串型数组,这部分的使用需要有些注意的地方,下面将会逐一的讲述。

对于字符数组的初始化定义和前面讲述的整型数组相似,需要注意的是字符使用单引号表示。初始化时需要提供一个由逗号分开的字符常量列表。同时字符数组也可以使用字符串常量来初始化,但这里需要注意的是字符串包含一个终止符。那么字符串数组就要求数组元素个数要多出来一个专门存放该字符串的终止符。字符数组的定义使用如下所示。

const char str1[3] = {‘W’,’E’,’L’};

const char str2[4] = “WEL”;

const char str3[4] = “WELC”;

如上述定义,第一行定义了拥有3个元素的常量字符数组,并且使用3个字符分别为其元素初始化。第二行同样定义了拥有4个元素的字符数组,使用3个字符的字符串给其赋值,最后一个元素存放了字符串结束符。第三行则使用错误了,因为此刻赋值的字符串长度为5,而数组的元素个数只有4个,该定义将会出错。

数组在C++中的作用前面已经讲述过,主要用来顺序的存储数据。数据在数组中顺序存储,方便用户随机访问并修改其数据。数组除了用来存储程序中处理的数据以外,通常应用程序需要对其数据作一系列的其它操作处理。根据处理需求对数组中元素的排序即为在数组之上基本操作之一。下面将会详细讲述数组元素基本排序的使用。

6.1.3  数组应用

数组在程序应用中除了基本的存储临时数据处理以外,数组可以用来存储大容量数据,并且根据需要会对其元素作出一些操作。其中针对大数据量数组的元素的排序最为常用。数组元素的排序方式多种多样,从影响效率的角度来讲,排序过程中移动数组数据次数最少的排序方式效率通常最高。

本小节主要使用其中的一种排序方式,来简单说明程序在数组之上的操作应用。这里选择使用最常见的“冒泡法”来演示程序中数组排序的基本操作。冒泡法排序,主要将数组中元素依次同数组中其余的元素比较并且交换它们,一直比较到数组的最后一个元素即完成排序功能。下面通过冒泡排序法的数组元素排序实例来说明冒泡排序法应用于数组的操作情况,该实例代码编辑如下。

/*

* 函数功能:实现数组元素冒泡排序功能

* 函数参数:传入整型数组名及其数组长度

* 返回值:空

*/

void bubbleCompositor(int *array,int len)

{

         inti,j,temp;                                              //定义三个整型变量,ij用来作循环条件变量,temp用来作数组元素交                                                                    //换的临时变量

         for(i=0;i<len-1;i++)                                //第一重for循环,从数组下标0开始到倒数第二个元素用来依次跟后续     {                                                                //元素比较

                for(j=i+1;j<len;j++)                     //数组第二重for循环,从数组下标1开始直到最后一个元素用于比较

                 {

                       if(array [i]> array [j])            //比较数组前后位置的数据,如果符合判断条件则执行如下的元素替换

                      {

                             temp= array [i];          //当元素需要比较时,先将i元素给临时变量

                             array [i]= array [j];      //j的元素值放到i元素位置中

                             array [j]=temp;           //将临时变量中值放到j位置中

                      }

                }

         }

}

通常数组作为函数参数传递只传其数组名,即指向数组首元素的指针。随即再将数组元素的长度作为参数传入。这样在函数内部可以通过操作数组名以及数组的长度来完成数组元素的排序功能。函数的内部传入的数组,通过下标的操作实现数组中元素的比较排序工作。数组中元素的排序方法除了冒泡法还有更多的方式,比如选择排序、快速排序法等。

数组应用范围广泛,这里只能通过数组元素排序中的一种方式来简单讲述数组的应用。更多的数组应用在后续章节实践中会作出详细讲解,同时也需要初学者不断的实践积累。

6.1.4  多维数组

从上述章节讲述来看,数组的定义基本都是基于一维方式来实现的。C++中允许定义二维、三维乃至多维数组应用。多维数组定义一般语法形式如下所示。

type  arrayname[length][length]…..[length];

多维数组定义和一维数组定义形式类似,每增加一维只需要在其后添加一项并说明其长度即可。通常软件编程中多维数组的应用最多使用到3维,最常用的是二维数组的使用。二维数组有两个下标,多维数组中下标的个数也决定了数组的维数。由于多维数组中二维数组最常用,那么下面将会以二维数组使用情况,对于其它多维来讲使用方法类似。

二维数组在定义使用上需要注意一个问题,那就是元素的存放顺序问题。二维数组拥有两个数组下标,第一个数组下标说明其行数,第二个数组下标说明其列数。按照行优先的方式来存储数组元素,二维数组定义实例以及其初始化情况如下所示。

int array[2][3];                      //定义一个23列的整型二维数组

char str[3][2];                       //定义一个32列的字符型二维数组

array[2][3] = {{1,2,3},{4,5,6}};               //初始化该二维数组

str[3][2] = {{‘a’,’b’},{‘c’,’d’},{‘e’,’f’}};

如上定义了两个二维数组,并且随后对其进行了初始化。第一个二维数组array实例中表示该数组拥有23列元素。由于二维数组采用行优先的存储方式,那么初始化时是按照行中对应得列数来存放的,也就是array二维数组初始化可以采用如下方式来理解。

array[2][3] = {1,2,3,4,5,6};

这种初始化方式与上述方式等价,在理解行优先存储时更可以说明的更清晰些。行优先即从初始化第一个值开始依次将数组第一行初始化,之后再初始化第二行依次下去直至初始化完所有的元素。不足的初始化值默认使用0来初始化。初始化时为了表明清楚行列对应的元素,初始化值大括号内再采用大括号来表示行即array[2][3] = {{1,2,3},{4,5,6}}{1,2,3}用户初始化该数组第一行,{4,5,6}用于初始化该数组第二行,这种方式会让初始化更加清晰,建议在开发中采用。

二维数组的初始化在实际开发中通常可以使用两个for循环结构来实现,其实现方式如下所示。

const int row = 4;                          //定义整型常量row

const int col = 3;                           //定义整型常量col

int array[row][col];                        //定义43列的二维数组array

for(int i = 0;i < row;i++)                //for循环控制结构,使用行数为控制条件

{

         for(intj = 0;j < col;j++)        //for循环控制结构,使用列为控制条件

         {

                   array[i][j]= i;                //使用变量i为指定二维数组元素赋值

         }

}

以上实例使用两个for循环控制结构来初始化数组。数组定义时其长度必须是固定值,这里采用常量表达式方式来确定数组定义时的下标。由于二维数组采用行优先存储策略,所以第一重循环采用行数为控制条件,第二重循环采用列数为循环控制条件,这样实现行优先存储。

多维数组在占用内存空间上讲比较的多,通常不是特殊情况下基本很少用到3维以上的数组定义。数组在使用时,是预先分配固定的内存空间,这就意味着在程序使用前需要估算好数组的大小是否能够适合当前数据处理。通常都定义为程序不可能超出范围的值,否则有可能造成数组存储越界,导致计算结果不正确。

这种情况下在使用中有可能就会浪费了一定的空间。尤其是多维数组,维数越多在数据量有限的情况下会发现内存空间浪费的越严重。这种情况,会在后续的动态数组定义,以及STL中数组方式再封装的vector中提出比较好的解决方法。

6.1.5  实例 6-1数组元素冒泡排序

数组是一类存储多个同类元素的顺序型存储数据结构,该数据结构中元素排序操作相当普遍,最常见的情形是数组中元素求取最值,或者需要按照大小规则来排序,因为程序处理中的数据本身要求按照一定顺序去处理。如下实例通过数组中元素冒泡排序的基本实现来了解数组元素排序的常见实现方式。

1.准备实例

打开UE工具,创建新的空文件并且另存为chapter0602.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

/**

* 实例chapter0602

* 源文件chapter0602.cpp

* 数组元素冒泡排序

*/

#include <iostream>

using namespace std;

/*

* 函数功能:实现数组元素冒泡排序功能

* 函数参数:传入整型数组名及其数组长度

* 返回值:空

*/

void bubbleCompositor(int *array,int len);

/*主程序入口*/

int main()

{

         constint size = 10;                                                   //定义整型常量size,表示数组长度

         intarray[size] = {1,4,2,5,6,8,45};                           //定义包含size个元素的数组array同时初始化值

         bubbleCompositor(array,size);                             //调用冒泡排序实现函数,传入数组名与数组元素个数

         for(inti = 0;i < size;i++)                                           //循环打印数组排序后的元素

         {

                   cout<<array[i]<<"";

         }

         return0;

}

/*数组元素冒泡排序函数定义实现*/

void bubbleCompositor(int *array,int len)

{

         inti,j,temp;                           //定义三个整型变量,ij用来作循环变量,temp用来作数组元素交换的临时变量

         for(i=0;i<len-1;i++)             //第一重for循环,从数组下标0开始到倒数第二个元素用来依次跟后续元素比较

         {

                for(j=i+1;j<len;j++)                      //数组第二重for循环,从数组下标1开始直到最后一个元素用于比较

                {

                       if(array [i]> array [j])                    //比较数组前后位置的数据,如果符合判断条件则执行如下的元素替换

                      {

                             temp= array [i];                   //当元素需要比较时,先将i元素给临时变量

                             array [i]= array [j];               //j的元素值放到i元素位置中

                             array [j]=temp;                     //将临时变量中值放到j位置中

                      }

                }

         }

}

上述实例主要演示了采用数组实现其元素冒泡排序的功能。排序是应用软件程序中一个比较热点的方面,尤其是大数据量的排序操作问题。排序方法的选择会严重影响到程序的处理效率,本小节仅仅就冒泡排序法作一个简单的分析。

2.编辑makefile

Linux平台下需要编译源文件为chapter0602.cpp,相关makefile工程文件编译命令编辑如下所示。

OBJECTS=chapter0602.o

CC=g++

 

chapter0602: $(OBJECTS)

    $(CC)$(OBJECTS) -g -o chapter0602

clean:

    rm -fchapter0602 core $(OBJECTS)

submit:

    cp -f -rchapter0602 ../bin

    cp -f -r*.h ../include

上述makefile文件套用了上个实例模板。之所以其中采用变量定义替换的方式,目的就是为了方便编译程序的替换。从makefile工程文件中可以看出,布局是相同的。不同的地方仅仅是代码的文件名、生成可执行程序的名称等,大大方便了每次都要重新编写一遍编译命令的编辑方式。

3.编译运行程序

当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

[developer @localhost src]$ make

g++ -c -o chapter0602.o chapter0602.cpp

g++ chapter0602.o -g -o chapter0602

[developer @localhost src]$ make submit

cp -f -r chapter0602 ../bin

cp -f -r *.h ../include

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ ./chapter0602

0 0 0 1 2 4 5 6 8 45

以上实例简单使用数组实现了其元素排序功能,调用冒泡排序函数,实现其整型元素从小到大排序并将排序后的数据输出。另外由于数组初始化时给出的显式元素个数不足数组长度,所以数组中后续元素采用0默认值来补足。此为上述实例中存在三个零值参与排序的原因。

本实例中涉及下章讲述的函数概念,此处可以从中了解大致的基本使用情况明白大致表示排序功能提供给主程序调用概念即可。数组元素排序方式多种,初学者可以借鉴以上实例方式来分别实现其余的排序方式锻炼自己分析解决程序问题的能力,在指针数组章节中会给出完整的指针数组实现元素排序操作的实例,指针数组中由于存放着指针变量,排序中的元素交换直接变为修改指针的指向,省去了数组元素排序时的位置移动,一定程度上提高了排序的效率。

6.2  指针

从本节开始,讲述C++语言中一个相当重要的概念即指针。延续低级语言中功能强大的指针概念,C++语言中依然很好的支持了指针的使用。在C++后续的纯面向对象语言中基本都避免使用指针,以免给程序带来不安定的因素。C++语言之前的C语言之所以功能很强大,并且可操作性很强,相当程度上是因为指针的存在和运用。

6.2.1  指针变量声明及初始化

指针在C++中可以看作为一个特殊的变量。那么既然是变量就有其类型、变量值等概念。通常指针是CC++初学者比较难理解的一个概念。下面通过从指针的基本类型,指针指向的类型,指针变量的值等方面进行讲述,让初学者清晰的了解清楚指针的基本概念以及使用方式。

指针变量类型,在看指针变量之前,首先了解一下指针变量的定义情况。常见的指针变量定义由指针基本类型、指针符以及变量名组成,如下所示几个指针定义实例。

int *p;                                    //整型指针变量定义

char *str;                               //字符指针变量定义

指针变量的类型即为定义实例中变量之前的那个类型。如上所示的实例中整型指针p本身的类型是int*,而字符指针本身的类型是char*。基本上可以总结规律为指针变量名前部分可以看作是指针的类型。指针变量本身占用内存空间大小在平台一定的基础上是固定的。32位平台下通常为4个字节大小,这里需要明白一个概念指针变量本身大小是固定。下面再来看指针指向的类型情况。

指针指向的类型为定义时的类型说明,即指针变量中第一个类型说明符。如上述实例中指针p指向的类型为int,指针str指向的类型为char,需要与指针变量本身的类型需要区分开来。指针定义类型才决定了指针变量中指向的内存地址大小,即上述例中指针p所指向的类型为int型,字符指针str指向的类型为char型。指向类型决定了指向可操作的这块内存在32位平台下分别为41个字节大小。

下面来介绍下指针变量中存放的值,即为指针变量中存放的内容。指针变量中值是以地址的方式来存放的。那么地址方式怎么能够来表示指针可以操作的那一段内存空间呢。其实这里是通过将指针变量指向的那段内存空间的首地址存放在指针变量中,而指针变量初始化后,所指向得初始化数据的类型就决定了那块内存空间从首地址到该类型表示的内存空间大小的内存区域。

比如指针定义及初始化如下所示。

int value = 4;                        //整型变量定义并初始化为值4

int *p = &value;                   //整型指针定义,并指向整型变量value的地址

上述例子中,首先定义了整型变量value,并且初始化值为整型值4。随后定义整型指针变量,同时取整型变量value的地址,给该指针变量初始化。这里取地址符号为&,作用为取整型变量value的地址,随后该地址作为整型指针变量指向的值来使用。该实例说明了指针变量p此刻其值为整型变量value的首地址,可以操作32位平台大小为4个字节的内存空间。

软件编程中指针操作时始终明白指针本身的类型,指针指向的类型以及指针指向的内存地址以上几个概念,相信会将指针这样一个利器发挥到淋漓尽致。

6.2.2  指针运算

指针作为变量,同样可以进行相应的运算操作,不过只限定为加减法运算。但这种简单的运算方式跟普通变量运算存在一定的区别。

首先这里需要介绍指针运算符的基本概念。指针中有两个运算符号经常使用,这就是*&符号。不同于定义指针变量是使用含义的*符号,在指针运算中称为间接运算符。它表示指针变量中存放地址所对应的值。例如上个实例,指针p在运算中作为右值应用解引用运算符时,即*p表示的就是整型值4。另外&运算符在指针运算中也不表示变量引用,而是表示返回其随后操作数的地址。即上例中&value即表示取得整型变量value对应的地址,将其赋值给指针变量。

对于指针,C++限定为加法和减法两种操作。指针的运算可以通过一些指针运算实例来理解。

int value = 4;                        //整型变量value定义,并初始化为值4

int *p = &value;                   //整型指针变量定义,指向整型变量value

++p;                                       //指针指向地址++

--p;                                         //指针指向地址--

char array[5];                       //字符数组定义

char *str;                               //字符指针变量

str = array;                            //字符指针指向字符数组首地址

++str;                                     //字符指针指向地址++

如上操作实例,首先定义整型变量value并赋值为整型常量值4。随后将该整型变量取地址赋值给整型指针p,即此刻指针p为一个自身类型为int*型。该指针指向类型为int类型,同时存储着value变量地址的指针。

在此基础上,针对指针运用++运算符号,即指针变量p+1指向内存中下一段地址。而这个地址是根据指向的类型来定的。由于指针指向类型为整型,那么在32位平台为4个字节大小。执行++操作符之后地址需要加4,即指向了该地址之后4个字节处。如果value变量地址为0xbfffe234,那么++操作后该指针变量存放的地址为0xbfffe238,因为地址值为字节表示,所以直接在原有的地址基础上加4个字节即可。

随后执行的--操作符表示将该指针指向的地址值向后退出4个字节,此刻地址值又变为0xbfffe234。接下来定义了一个拥有5个元素的字符数组。由于数组的名称即为指向数组首元素地址的指针,随后将其赋值给字符指针str,即str指向了该数组的首元素地址。在该字符指针之上操作++运算符后,指向了数组的下一个元素即array[1]的地址。

指针运算除了只允许进行加法减法运算以外还支持关系运算操作,即主要用于指针之间比较运算操作。指针关系运算,可以将基本关系运算符运用到其中,大部分关系运算用于指针操作的一些判断。如下一些关系运算实例来说明关系运算符在指针运算中的使用。

int *ptr1,*ptr2;                                //定义两个整型指针

if(ptr1 == ptr2)                               //判断指针ptr1ptr2是否指向同一内存地址

if(ptr1 > ptr2)                                //判断指针ptr1是否处于内存中高地址位置

if(ptr1 == 0)                                   //判断指针ptr1指向地址是否为空

if(ptr1 != 0)if(ptr1 != NULL)   //判断指针ptr1指向地址是否不为空

指针变量运算中,关系运算可以运用于基本控制结构的条件判断来使用。以上几种关系运算在软件编程中也都比较常用。尤其if(ptr1 != NULL)的判断使用对于指针操作来讲,使用之前判断一下指针指向是否为空,可以避免指针操作过程中使用空指针产生程序错误的情况。

指针的运算本身其实并不复杂,只要初学者掌握了指针的几个基本概念,即所操作的指针本身的类型、指针指向的类型以及指针变量中存放的地址值,那么在操作指针过程中就会变得简单易理解。

6.2.3  常量指针与指针常量

常量指针在指针的应用中是个比较容易出错的概念。许多初学者容易将“常量指针”与“指针常量”的概念相互混淆。本小节将会讲述它们的基本概念及区别所在,让初学者在使用其之前能够清晰的了解常量指针的概念和使用方法。

1.常量指针

常量指针其实是一个指向常量的指针,这么讲可能初学者不容易理解,下面通过一个实例来说明。

int value1 = 9;                                                 //定义整型变量,初始化值为9

int value2 = 100                                              //定义整型变量,初始化值为100

const int *ptr = &value1;                                //定义常量指针常量,将整型常量value1的地址初始化该指针

*ptr = 10;                                                          //修改常量指针常量所指向地址中存放的值,该步为错误操作

ptr = &value2;                                                  //修改常量指针所指向的地址,该步为正确操作

如上实例首先定义了整型变量value,其值为9。接着定义了int*类型的常量指针ptr,同时将整型常量value的地址初始化ptr。此刻采用前面讲述的方式,明确的了解该指针本身的类型、指向的类型以及指针的值。通过理解这几个基本概念,知道该指针本身的类型为int*,指针指向的类型为constint,指针的值为整型变量value的地址。

这样常量指针的基本概念应该非常清晰了,首先常量指针是一个指针变量。其指向的地址的数据为常量整型,即其地址中存放的值被规约为常量。整个程序运行过程中不能通过该指针修改其常量的值。如上*ptr = 10则试图修改指针指向地址中的值,此刻程序编译会报出相应的错误。

由于常量指针仅仅是指向的值为常量,那么指针本身改变指向的地址并不受影响。如上述修改常量指针的指向,使其指向整型变量value2的地址,程序编译时并不会出任何错误。

那么常量指针的基本概念可以总结如下。首先常量指针是一个指针变量,其指向的地址中存放的值被规约为常量。可以随意修改指针的指向,但是不可以随意修改指针指向地址中的常量值。const修饰符用来规约指针指向地址的值的,所以此时修改常量指针指向的地址中常量值是非法的。

2.指针常量

常量指针的概念清晰以后,再来理解指针常量的概念应该比较容易。这里仍然通过一个实例来剖析。

int value1 = 9;                                                 //整型变量定义,赋值为9

int value2 = 10;                                                        //整型变量定义,赋值为10

int * const ptr = &value1;                               //指针常量定义,指向变量value1

*ptr = 100;                                                                 //修改指针常量指向地址的值

ptr = &value2;                                                  //错误操作

指针常量是针对指针本身来讲的,指针中存放的是地址。指针常量即指针的地址为常量不能随意修改,即指针不能随意修改其指向。但是指针指向地址的值是可以随意修改的。如上实例中,定义两个整型变量并初始化,定义指针常量使用value1的地址为其初始化。一旦确定其指针常量的指向的地址后即不能随意修改其指向,这里可以修改指针指向地址中存放的值。如上修改为100。但是接着指针常量想修改其指向value2则是非法的。

总结指针常量概念如下,指针常量是用来修饰指针本身的,由于指针中存放有地址,即指针常量表示指针本身是常量。其中存放的地址一旦初始化确定就不允许修改,但是可并不针对指针所存放的地址的值作限制,所以指针所指向地址中存放的值可以修改。

综上所述,常量指针与指针常量的概念区别在于常量修饰点不同,常量指针主要限制在指向所指向的地址的值上,指针常量主要限制在指针所指向的地址上。在软件开发中,需要清晰的了解两种基本的指针概念,后续应用中会讲述其应用场合。

6.2.4  指针与数组

C++中指针与数组的概念存在着一定的关联。通常在软件开发中都会将数组名看作指针来使用。事实上数组名作为指针只能是指针常量,即数组名是指向数组首元素地址的指针。但是该指针指向的地址不能被改变,所以最多数组名可以看作是前面讲述的指针常量的概念。即如下实例操作是非法的。

int array[100];                               //定义整型数组,拥有100个数组元素

array++;                                         //非法操作,因为指向的地址固定不能被修改

当数组名作为函数参数传递时,数组名在当作形参传递时在编译器层面会将其转换为指针变量。可以使用指针的一切特性,即期指向的地址可以被修改。可以使用自增、自减等操作。如下实例操作是被允许的。

int strlen(char *str);                      //声明计算字符串长度的函数

int main()                                       //主函数入口

{

         chararray[10] = “hello”;     //定义字符数组,包含10个字符元素,初始化为字符串”hello”

         strlen(array);                       //根据传入的字符数组指针为参数,调用字符串长度计算函数

         return0;

}

int strlen(char *str)                       //字符串长度计算函数定义

{

         str++;                                     //转化为指针变量进行加减操作

}

上述代码片段仅仅用于演示数组与指针变量之间的转换操作,并没有真正实现计算字符串长度的功能。总结数组与指针之间的关系,数组这种存储同种类型元素的数据结构。其名字可以当作指针常量使用,但是其指向的地址不能改变。但是一旦数组名作为函数的形参使用时,会完全的转变为普通的指针变量,可以对其使用指针操作的方式。

6.2.5  指针数组

前面章节已经介绍过数组这种复合类型的数据结构。指针也可以像C++中其它数据类型一样用来定义数组。指针变量作为数组元素称为指针数组,其常见定义实例如下所示。

int array1[10];                               //定义数组array1,包含10个整型数据元素

int *array2[10];                              //定义数组array2,包含10个整型指针元素

char *str[10];                                 //定义数组str,包含10个字符指针元素

上述实例定义两个指针数组,这里可以通过数组名前的修饰类型来理解。普通类型数组通常由数组名前的修饰类型来决定,即决定该数组中能够存放的数据类型。如上述实例中第一个,数组名array1前修饰类型为int整型,即决定了该数组中存放着整型元素。

紧接着下面定义了两种类型的指针数组。采用同种方式来辨别,数组名array2str前修饰类型分别为int*char*。这两种类型在前面指针概念讲述时分析过,表示指针变量的类型,即数组中存放着该类型的指针元素。

指针数组的存在有其适当的应用场合,通常数组长度一旦固定后在程序中其它地方不能改变。除非是采用动态申请内存空间的动态数组或者是在定义时再作修改。软件开发中如果使用普通数据并且固定长度时,总是尽可能考虑该数组中存放的数据元素最大可能长度。那么在使用时多半是会浪费些空间,尤其对于数组中存放的大数据对象来讲,浪费空间的几率更大些。

而指针数组,由于数组中存放的是指针元素,32位平台下每个指针通常只有4个字节。加上指针数组可以按照需求来动态生成使用,某种程度上避免了空间浪费,并且使用指针作为元素后程序操作会变得更加的灵活。另外通过指针数组将同类型的指针进行有序的管理,方便数据的索引。

6.2.6  实例 6-2指针数组应用于数据排序

指针数组应用于数据排序最大的好处即省去了数组元素排序中的元素移位操作,由于指针数组中存放的元素主要为指针变量,即32位平台下4字节大小,操作指针变量要比数组中直接存放数据效率有所提高。另外排序比较操作中,遇到数据元素比较需要移位时,指针数组只需要修改其中指针变量指向即可。改进上述整型数组元素排序方法,采用指针数组方式实现。

1.准备实例

打开UE工具,创建新的空文件并且另存为chapter0603.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

/**

* 实例chapter0603

* 源文件chapter0603.cpp

* 指针数组应用于数据排序实例

*/

#include <iostream>

using namespace std;

/*

* 函数功能:利用指针数组实现冒泡排序功能

* 函数参数:传入指针数组数组名及其数组长度

* 返回值:空

*/

void bubbleCompositor(int *array[],int len);

/*主程序入口*/

int main()

{

         constint size = 10;                                         //定义整型常量表示指针数组长度

         intdata[size] = {1,4,3,5,9,6,10,2,7,8};                  //定义整型类型数组,包含size个元素,并初始化

         int*array[size];                                                //定义整型指针类型数组,包含size个整型指针元素

         for(inti = 0;i < size;i++)                                  //通过循环结构将整型数组中元素初始化给指针数组中的指针元素

         {

                   array[i]= &data[i];                                  //即指针数组中的指针元素指向了整型数组中的对应元素

         }

         bubbleCompositor(array,size);                   //调用指针数组实现的冒泡排序函数,传入指针数组以及数组大小

         for(inti = 0;i < size;i++)                                  //循环打印排序后的指针数组中的元素指向值

         {

                   cout<<*array[i]<<"";

         }

         cout<<endl;

         return0;

}

/*指针数组排序函数定义实现*/

void bubbleCompositor(int *array[],int len)                  //指针数组实现冒泡排序功能函数定义

{

         inti,j;                                                                 //定义两个整型变量分别表示需要比较指针数组元素下标

         int*temp;                                                         //定义整型指针作为临时变量,用于比较时交换元素使用

         for(i= 0;i < len-1;i++)                                              //通过两重循环结构,分别对比前后位置的元素

         {

                   for(j= i+1;j < len;j++)

                   {

                            if(*array[i]> *array[j])                            //如果前面元素大于后面的元素,则修改指针数组中指针元素指向

                            {

                                     temp= array[i];                   //将指针数组中第i个元素指向地址赋给临时指针变量

                                     array[i]= array[j];                 //修改第i个指针元素指向地址,使其指向第j指针元素指向的地址

                                     array[j]= temp;                   //最后将临时指针变量中指向第i元素地址赋给第j个指针数组元素

                            }        

                   }

         }

}

上述实例主要采用指针数组方式实现冒泡排序功能。程序主要由主函数与排序函数两个部分组成,程序内部主要展示了指针数组在实际程序中的应用情况。程序详细分析会在后面介绍。

2.编辑makefile

Linux平台下需要编译源文件为chapter0603.cpp,相关makefile工程文件编译命令编辑如下所示。

OBJECTS=chapter0603.o

CC=g++

 

chapter0603: $(OBJECTS)

    $(CC)$(OBJECTS) -g -o chapter0603

clean:

    rm -fchapter0603 core $(OBJECTS)

submit:

    cp -f -rchapter0603 ../bin

    cp -f -r*.h ../include

上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

3.编译运行程序

当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至bin目录,执行该程序文件运行结果如下所示。

[developer@localhost src]$ make

g++ -c -o chapter0603.o chapter0603.cpp

g++ chapter0603.o -g -o chapter0603

[developer @localhost src]$ make submit

cp -f -r chapter0603 ../bin

cp -f -r *.h ../include

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ ./chapter0603

1 2 3 4 5 6 7 8 9 10

上述实例实现思路与实例6-1相同,该程序主要实现整型数组元素的排序功能。排序使用的方法依然是最常见的冒泡排序法。此处所不同的是使用指针数组方式实现,首先通过将整型数组中的元素地址分别赋给指针数组的指针元素,即指针数组中的整型指针元素都指向了对应的整型数组中元素地址。随后在排序功能函数中直接处理指针元素,根据判断大小修改指针元素的指向即可。省去了移动数组元素的步骤。

指针数组在软件编程中有着很广的应用。学习完本章后发现使用好指针对于开发高效率,功能强大的应用程序有着巨大的帮助。初学者在学习过程中紧紧扣住前面讲述的指针的基本概念,在实践中分清指针的基本信息,相信通过不断的实践总结一定会达到熟练运用指针实现更多复杂高效的应用程序。

6.3  字符串

C++中字符串在软件编程中使用的频率比较高。尤其是处理文件数据等方面,对字符串的操作的使用也是开发者基本功之一。下面将会讲述C++字符串基本概念及其基本操作实现,启发初学者应对字符串操作给出更好的实现。

6.3.1  字符串基本概念

C++中字符串包含了C语言字符串风格和C++标准库封装的字符串类型两种。这里主要讲述C++中字符串的基本概念,标准库的string类类型将会在C++标准库简介那章详细讲述。

C++中标准化之前并没有提出字符串类型。字符串不是以一个变量方式来存储使用的,而是通过字符类型的数组以及指针来表示的。C++中字符串并不属于其基本数据类型,可以将其看成是由若干个合法的字符组成的有效序列。这里的合法字符即为C++中允许的字母、数字和转义字符等。

C++标准string类没有公布前,竟然没有专属变量表示字符串。在定义使用时候需要依靠字符类型数组和字符指针来配合操作字符串。下面通过几个实际使用字符类型数组及字符指针操作字符串的实例来说明字符串基本概念。

char array[10] = “hello”;

| h | e | l | l | o | \0 |  | |  | |                       //该字符类型数组中元素表示

不同于字符,字符串通过双引号来表示并且通常以一个结束符\0为结尾。字符串不同于字符之处就在于其可以表示字符的组合。并且因为有了结束符号的存在,在很多字符串操作中都会变得方便。如上实例定义中,定义了包含10个元素的字符类型数组array,同时使用字符串”hello”初始化该数组。此刻该数组中每个元素挨个存放着字符串中的每个字符,即array[0]= ‘h’array[1] = ‘e’等依次对应存放。

但是在操作字符串的时候,总是将字符串当作一个整体来看待,而不是一个个字符组合来表示。这个时候一个字符串它的表示即采用结束符\0来区别于字符。有了结束符后,操作字符串就显得方便多了。如计算其长度,字符类型数组长度和字符串长度很多时候并不相同。

上述定义10个元素的数组中存放了5个字符的字符串,那么计算该字符串长度时就可以根据确定的起始字符计算到终止字符前字符数即为该字符串有效长度。除了计算其长度,因为结束符的存在,字符串相当一部分操作都可以以此来作为结束条件将字符串作为一个整体来处理。

另外字符类型数组处理总是跟字符类型指针密切相关。所以字符串的处理还可以通过字符类型指针来操作。事实上,字符类型数组当作参数传递时总是以字符类型指针的方式使用的。并且C++中包含的C语言字符串操作库函数基本都是以字符类型指针作为接口参数,理由是字符类型指针操作更具有灵活性。如下实例即使用了字符数组与指针操作字符串。

1.准备实例

打开UE工具,创建新的空文件并且另存为chapter0604.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

/**

* 实例chapter0604

* 源文件chapter0604.cpp

* 字符串拷贝功能函数

*/

#include <iostream>

using namespace std;

/*

* 函数功能:拷贝字符串

* 函数参数:两个字符类型指针变量参数

* 返回值:字符类型指针值

*/

char *strcpy(char *dest,char *src);

/*主程序入口*/

int main()

{

         charstr[10] = "hello";                                     //定义拥有10个元素的字符数组,同时使用字符串hello初始化

         charstr1[10] = "nihao";                         //定义拥有10个元素的字符数组,同时使用字符串nihao初始化

         cout<<strcpy(str,str1)<<endl;              //打印调用字符串拷贝函数拷贝结果信息

         return0;

}

/*字符串拷贝功能函数定义实现*/

char *strcpy(char *dest,char *src)                        //字符串拷贝函数定义

{

         assert((dest!= NULL) && (src != NULL));  //定义断言用来判断传入的两个指针参数是否为空

         char*temp = dest;                                          //定义临时变量指针,指向目标字符指针

         while((*dest++= *src++) != '\0')                    //while循环结构,用来实现拷贝字符串

                   ;

         returntemp;                                                    //返回临时变量指针

}

上述实例主要演示C++语言中字符串拷贝功能。程序由主函数与字符串拷贝函数组成,在字符串拷贝函数的内部采用指针的方式操作字符串。程序详细分析在后面讲述。

2.编辑makefile

Linux平台下需要编译源文件为chapter0604.cpp,相关makefile工程文件编译命令编辑如下所示。

OBJECTS=chapter0604.o

CC=g++

 

chapter0604: $(OBJECTS)

    $(CC)$(OBJECTS) -g -o chapter0604

clean:

    rm -fchapter0604 core $(OBJECTS)

submit:

    cp -f -rchapter0604 ../bin

    cp -f -r*.h ../include

上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

3.编译运行程序

当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至bin目录,执行该程序文件运行结果如下所示。

[developer@localhost src]$ make

g++ -c -o chapter0604.o chapter0604.cpp

g++ chapter0604.o -g -o chapter0604

[developer @localhost src]$ make submit

cp -f -r chapter0604 ../bin

cp -f -r *.h ../include

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ ./chapter0604

nihao

以上实例主要实现简单的字符串拷贝功能。首先定义了两个字符类型数组,分别存放着字符串。这里定义实现了字符串拷贝功能函数,提供接口采用了字符指针,即将目标字符数组和源字符数组以字符指针为参数的方式传入函数进行处理。函数内部首先需要判断传入的指针参数是否为空,这是使用指针的一个良好习惯。

随后定义临时指针变量指向目标字符指针,接着通过while循环进行赋值并且作出是否已到结束符的判断。如果未到字符串结束则继续赋值拷贝单个的字符。如果已经到字符串结束位置,则直接返回临时变量指针,即此刻返回目标字符指针。字符类型数组传入整个字符串,字符指针获取到字符串的首地址,字符指针的操作必须是单个字符串通过++递增运算逐个处理。

C语言中一般使用字符串数组以及相应的字符指针来存储和操作字符串。C++中依然保留这样的的做法。由于C语言提供的字符串操作大多涉及指针直接的使用,初学者在这个部分容易出现错误。C++语言在支持C语言提供的一组操作字符串库函数的基础上采用面向对象的思想封装实现了字符串类型,让字符串操作可以像操作变量类型那么容易不易出错,并且提供了更丰富的操作功能。

6.3.2  字符串基本操作

字符串虽然不是C++语言提供的类型,但是C++的标准库对其给予了很好的支持。C++语言在库封装实现中除了继续保留C库中针对字符串的基本操作功能以外,还提供了C++标准的字符串类型以及更加丰富的字符串操作功能。C++字符串string类型的及其提供的功能方法会在后续章节介绍,这里字符串基本操作主要还是介绍用于字符串处理的C库提供的函数。因为一方面这些库函数在C++程序中仍然大量使用,另一方面熟悉C字符串库函数提供的基本操作功能,也有助于字符串的理解。

C++的库函数提供了约20多个处理字符串的功能函数。该类函数主要用于针对字符串常需要进行的拷贝、字符串连接、求取字符串长度等操作。这些字符串处理功能库函数都已经实现好,意味着在日常的软件开发中只需要包含正确的头文件,直接使用库提供的功能接口函数即可。下面将会挑选几个库中提供的字符串操作功能函数,来了解字符串的基本操作情况。

1.字符串拷贝库函数strcpy()

对于数组来讲,前面已经讲述过数组之间相互赋值必须逐个元素相互赋值。这条规则对于字符数组同样起作用,字符数组中存储着字符串。但是字符数组之间不能直接使用赋值语句对于其整体赋值。如下操作会引起编译器在编译时刻的错误。

char str1[10] = “hello”;                          //定义字符数组str1,包含10个元素,初始化值为字符串hello

char str2[10] = “nihao”;                         //定义字符数组str2,包含10个元素,初始化值为字符串nihao

str1 = “c++”;                                            //错误操作

如上一段实例代码,通常字符数组在定义时可以使用字符串初始化,即将字符串常量存放在字符数组中。但是却不能使用字符串直接给字符数组赋值,否则会引起编译错误。由于存储字符串的字符数组赋值需要采用其单个元素互相赋值,这种方式在字符串长度比较大的时候对于开发者会很不方便。因此库函数strcpy()提供了基本的字符串拷贝操作,用来将字符串拷贝至字符数组中,对于开发者来讲只需要提供接口相应的正确参数直接调用库函数即可。

strcpy()函数属于库提供的功能接口,其原型、功能及其调用说明如下所示。

声明原型:char *strcpy(char *dest,char *src);

基本功能:将src所指向的字符串拷贝至dest所指向的字符数组中

    明:src为源字符指针,指向需要拷贝的字符串;dest为目标字符指针,指向需要拷贝字符串的目的地即对                        应的字符数组

该库函数调用实现非常的简单方便,只需要定义好与strcpy()函数参数对应的操作数,调用该函数传入对应参数即可。其调用实例如下所示。

char str1[10] = “hello”;                          //定义字符数组str1,包含10个元素,初始化值为字符串hello

char str2[10] = “nihao”;                         //定义字符数组str2,包含10个元素,初始化值为字符串nihao

strcpy(str1,str2);                                     //调用拷贝字符串方法

如上通过调用strcpy()库函数,直接实现将源字符串”nihao”拷贝进str1字符数组。字符串拷贝操作解决了字符数组直接赋值问题。在后续C++标准库函数提供string类类型的操作中会发现,当字符串成为一种类型的时候,之间的赋值会变得相当的简单。

2.字符串比较库函数strcmp()

字符串之间的比较不同于其它C++的基本类型变量。C++基本类型变量比较可以直接使用其关系运算符来进行。字符串的比较由于其特殊性必须将字符串中组合的字符逐个进行比较,基于这种情形下库函数提供了strcmp()函数,主要用于比较两个字符串大小。其基本说明如下所示。

声明原型:int *strcmp(char *str1,char *str2);

基本功能:比较字符串str1str2str1>str2则函数返回大于0的值;str1=str2则函数返回0str1<str2则函数返            回小于0的值

    明:str1str2都为指向字符数组的字符指针,字符串比较按照逐位字符取其ASCII码值进行比较

使用该库函数实现字符串比较非常的简单,传入正确的参数,直接调用该函数即可,使用实例如下。

char str1[10] = “hello”;                          //定义字符数组str1,包含10个元素,初始化值为字符串hello

char str2[10] = “nihao”;                         //定义字符数组str2,包含10个元素,初始化值为字符串nihao

strcmp(str1,str2);                                   //调用字符串比较方法

上述实例调用strcmp()库函数之后,程序将会从左向右逐位的比较两个字符串。方法将会逐位判断单个字符对应的ASCII码值以字符串结束符为终止条件。只要发现不同字符比较出值得大小即根据判断规则返回比较结果值,程序根据返回值来判断两个字符串的大小。

如上述两个字符串“hello”和“nihao”,调用strcmp比较函数。首先比较第一个字符’h’’n’ASCII码值的大小。由于nASCII值大于h,所以程序直接返回小于零的值,表示str1字符串小于str2字符串。字符串比较从第一位开始,遇到相等则顺后到下一位比较。如果全部相等则表明两个字符串相等。反之则一路比较下去直至遇到最短的那个字符串出现结束符则结束其对比。

3.字符串取长度库函数strlen()

字符串长度求取在之前的章节中也有提过。通常通过计数的方式依次比较字符串单个字符,直至出现\0为止。这里求取字符串的长度应该理解为字符串有效位的长度,即不包含\0终止符。针对这样的需求,库函数依然提供了取字符串有效长度的基本实现,其使用说明如下。

声明原型:int strlen(char *str);

基本功能:计算传入的字符串str的有效长度

    明:返回字符串str长度,不包括\0

该库函数使用更加简单,对外只有一个参数接口。调用该函数时直接传入需要计算长度的字符串即可。需要注意的是strlen函数计算的是字符串有效长度,即不包含\0在内!

C++库提供的字符串基本操作函数众多,本小节仅仅介绍几个主要常用的方法。初学者在需要使用的时候可以参照相关的帮助文档,并且初学者也可以自己试着封装实现类似功能的函数。字符串基本操作的实现也是锻炼C++程序员基本功重要方式之一。

6.3.3  实例 6-3字符串基本操作的实现

通过前面字符串的简单介绍,初学者应该可以理解字符串基本的概念以及其库函数提供的基本操作的使用。下面通过实现一些类似库函数提供的字符串处理功能的函数,让初学者在实践中总结字符串操作的运用,也可以通过下面实例的启发,编写出更好的字符串处理函数。

1.准备实例

打开UE编辑器,新建空白文件另存为chapter0605.cpp。随后连同makefile编译文件一起通过FTP工具传输至Linux服务器。客户端通过scrt访问操作。实例程序代码编辑如下所示。

/**

* 实例chapter0605

* 源文件chapter0605.cpp

* 字符串常用操作函数封装

*/

#include <iostream>

using namespace std;

/*

* 函数功能:拷贝字符串

* 函数参数:两个字符类型指针变量参数

* 返回值:目标字符指针

*/

char *strcpy(char *dest,char *src);

/*

* 函数功能:比较两个字符串大小

* 函数参数:两个字符类型数组参数

* 返回值:比较后的整型值

*/

int strcmp(char str1[],char str2[]);

/*

* 函数功能:连接两个字符串

* 函数参数:两个字符类型指针变量参数

* 返回值:拼凑后的字符串

*/

char *strcat(char *dest,char *src);

/*

* 函数功能:字符串长度求取

* 函数参数:需要求取长度的字符指针参数

* 返回值:字符串长度

*/

int strlen(char *str);

/*主程序入口*/

int main()

{

         charstr1[100] = "hello";              //定义拥有100个元素的字符数组,使用字符串常量hello初始化

         charstr2[100] = "welcom";                  //定义拥有100个元素的字符数组,使用字符串常量welcom初始化

         intflag = strcmp(str1,str2);                  //定义标记变量flag,调用字符串比较函数,将返回结果赋给flag

         if(flag>0)                                      //如果返回的比较值大于0

         {

                   cout<<"str1large than str2!"<<endl;           //打印提示信息,字符串str1大于str2

         }

      else if(flag == 0)                                                      //如果返回值的比较恒等于0,字符串str1等于str2

      {

             cout<<"str1 equalstr2!"<<endl;                   //打印字符串str1等于str2的提示信息

      }

       else

             cout<<"str1 less thanstr2!"<<endl;                      //如果以上条件都不符合,则str1小于str2

         cout<<"string'sstr1 length:"<<strlen(str1)<<endl;       //调用求取字符串str1的长度函数,并打印结果

         cout<<"string'sstr2 length:"<<strlen(str2)<<endl;       //调用求取字符串str2的长度函数,并打印结果

         cout<<"str1and str2:"<<strcat(str1,str2)<<endl;          //调用连接字符串函数,连接str1str2,并打印结果

         cout<<"str1:"<<strcpy(str1,str2)<<endl;                         //调用字符串拷贝函数,将str2拷贝至str1并打印结果

         return0;

}

/*字符串拷贝函数定义实现*/

char *strcpy(char *dest,char *src)

{

         assert((dest!= NULL) && (src != NULL));            //断言判断处理输入的参数指针是否为空

         char*temp = dest;                                                   //定义临时字符指针指向目标字符指针dest

         while((*dest++= *src++) != '\0')                             //循环控制,将源字符拷贝进目标字符数组中,以\0为结束

                  ;

         returntemp;                                                              //返回临时字符指针

}

/*字符串比较函数定义实现*/

int strcmp(char *str1,char *str2)        

{

         inti = 0;                                                                      //定义整型变量i并初始化为0

         while(str1[i]== str2[i])                                              //首先比较字符串首字母是否相等

         {

                   if(str1[i]== '\0')                                                 //如果相等,则判断首字符是不是结束符

                   {               

                            return0;                                                  //如果是,则直接返回0,表示两个字符串相等

                   }

                   i++;                                                                  //如果没有到结束符则i+1,继续比较下一位

         }

         return(str1[i] - str2[i]);                                              //如果遇到字符不相等则直接返回两个字符ASCII相减值

}

/*字符串连接函数定义实现*/

char *strcat(char *dest,char *src)

{

         inti,j;                                                                           //定义两个整型变量,分别表示目标符数组和源字符数组下标

         for(i= 0;dest[i] != '\0';i++);                                       //通过本循环语句将目标字符串定位到尾部

                   for(j= 0;(dest[i]=src[j]) != '\0';)     //当目标字符数组到达尾部时,将源字符数组字符依次存放到目标数组中

                   {

                            i++;                                        //相应的目标字符数组下标后移继续存放源字符数组字符

                            j++;                                      //源字符数组下标依次后移,逐位的将值存放到目标字符数组

                   }

         returndest;                                             //最后返回目标字符数组

}

/*字符串长度求取函数定义实现*/

int strlen(char *str)

{

         inti;                                                          //定义整型值,用于统计传入的字符串的长度

         for(i= 0;*str != '\0';str++)                       //循环控制判断,首先判断传入的字符数组是否到结束符

         {

                   ++i;                                                 //如果没到,则计数变量加1,继续判断对比

         }

         returni;                                                    //如果已经计算到最后结束符,则返回最终字符串长度

}

上述实例主要演示字符串操作自行封装功能。程序主要包括主函数与相关字符串操作函数,这些函数都是采用开发者自己的思路封装而成。程序详细讲述会在后面介绍。

2.编辑makefile

Linux平台下需要编译源文件为chapter0605.cpp,相关makefile工程文件编译命令编辑如下所示。

OBJECTS=chapter0605.o

CC=g++

 

chapter0605: $(OBJECTS)

    $(CC)$(OBJECTS) -g -o chapter0605

clean:

    rm -fchapter0605 core $(OBJECTS)

submit:

    cp -f -rchapter0605 ../bin

    cp -f -r*.h ../include

上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

3.编译运行程序

当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

[developer@localhost src]$ make

g++ -c -o chapter0605.o chapter0605.cpp

g++ chapter0605.o -g -o chapter0605

[developer @localhost src]$ make submit

cp -f -r chapter0605 ../bin

cp -f -r *.h ../include

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ ./chapter0605

str1 less than str2!

string's str1 length:5

string's str2 length:6

str1 and str2:hellowelcom

str1:welcom

如上实例主要实现了几个常见字符串处理的库函数,具体实现细节处理程序实例注释比较清楚。首先定义两个字符数组,分别存放着两个不同的字符串常量值。调用strcmp比较函数,根据逐个字符比较返回的结果判断字符串的大小。由于上述实例中字符串“hello”和“welcome”中首字母就不同,并且第一个字符串首字母ASCII值小于第二个字符串,所以运行结果为str1小于字符串str2

随后分别调用了计算字符串str1str2的长度函数。依照单个计数字符串的有效字符求取出相应的字符串长度并打印。strcat函数的调用主要实现了两个字符串相连接的功能,str1str2连接输出。主要处理过程即先定位到字符串str1的末尾,然后逐位的将字符串str2存储至str1中。这里要确保str1足够的空间存放。最后调用strcpy函数,将str2中字符串拷贝至str1中并打印。

C++中字符串应用相当普遍,在后续标准STL库中为C++字符串操作提供了一个统一封装操作的类型,即string字符串类类型。可以使用STL库提供的string类型比较轻松的定义和使用字符串基本操作。本小节主要讲述开发者针对最基本的字符串操作的自我实现情况。了解C++中字符串基本的操作的一般实现情况,随后再过渡到C++标准库STL提供的string字符串类型学习中。
原创粉丝点击