C++基础教程 学习笔记(二) 数组、字符串和指针

来源:互联网 发布:网页版淘宝怎么开微淘 编辑:程序博客网 时间:2024/04/28 13:27

数组是同类型变量的集合体,这些同类型的变量被赋予了一个共同的名称。数组可以具有
从一维到多维的多种类型,尽管一维数组是最常见的。数组为创建相关变量的列表提供了一种
便利的方法。

最常使用的数组是字符数组,因为它可以用来保存字符串。C++语言并没有定义内置的字
符串数据类型,而是将字符串作为字符数组来实现。这种实现字符串的方法比使用明确的字符
串类型的语言具有更强大的功能和更多的灵活性。

指针是一个包含内存地址的对象。通常,指针被用来访问另一个对象的值。这个另外的对
象常常是一个数组。事实上,指针和数组之间的关系要比用户所认为的更为紧密。

 

4.1一维数组

一维数组就是相关变量的一个列表。这样的列表在编程中是很常见的。例如,用户可以使
用一维数组来存储网络上当前用户的账户号码,使用另一个数组来存储一支棒球队的最近的击
球率。当用户计算一列值的平均数时,将经常使用数组来保存这些值。数组是现代编程的基础
性内容。

 

一维数组声明的通式是


type name[size];


其中“type”定义了数组的基本类型。该基本类型确定了组成数组的每个元素的数据类型。
数组所保存的元素数量在“Size”中指定。例如,下面的这个例子声明了一个名为sample的整
数数组,该数组的长度为10个元素:


int sample[10];


数组中的每个元素都通过下标进行访问。“下标”表明了数组中的每个元素所处的位置。
在C++语言中,所有的数组都将它们的第一个元素的下标设定为0。由于上例的sample数组有
10个元素,所以它的下标值是从0到9。通过使用元素的号码来确定数组的下标,就可以访问
数组元素了。要确定数组的下标,需要指定您所需要的元素的号码,并用方括号将其括起来。


所以,sample数组的第一个元素是sample[0],最后一个元素是sample[9]。例如,下面的程序
就将从0到9的数字加载到了sample数组。

 

要将一个数组的内容传输给另一个数组,用户必须逐个赋值,如下所示:

for(i=0;i<10;i++)a[i]=b[i];

 

无边界核查


C++语言不在数组上执行边界核查。这意味着没有任何东西能阻止用户超越出数组的末端。第4章数组、字符串和指针103
换句话说,对一个尺寸为N的数组,用户可以超过N来确定其下标,而不会生成任何编译时或
运行时的错误消息,即使这样作常常会引起灾难性的程序故障。例如,即使会导致crash数组
溢出,编译器也将编译和运行下列代码,并且不会发出任何错误消息:


int crash[10],i;
for(i=0;i<100;i++)crash[i]=i;


在这种情况下,即使crash数组只有10个元素长,该循环也将遍历100次!这会导致未存
储crash数组的内存被重写。


如果在赋值操作期间发生数组溢出,那么正被用作其他用途的内存,比如保存其他变量的
内存,有可能会被重写。如果在读取数据时发生数组溢出,则非法数据将破坏程序。无论如何,
作为程序员都有义务既要确保所有的数组都足够大,以便保存程序将提交给它们的数据,又要
在任何必要的时候提供边界核查。

 

4.2 二维数组


C++语言承认多维数组。最简单的多维数组是二维数组。二维数组实质上就是一维数组的
一个列表。要定义一个容量为10,20的二维整数数组twoD,您需要编写:


inttwoD[10][20];


仔细观察该定义。其他一些计算机语言使用逗号隔开数组尺寸,而C++语言却将每个容量
值单独置入一对方括号内。与此类似,要访问一个元素,用户需要指定由一对方括号括起来的104C++基础教程
该元素的下标。例如,对于数组twoD的点3,5来说,需要使用twoD[3][5]。

 

4.4 字符串

迄今为止,一维数组最常见的用途就是创建字符串。C++语言支持两种类型的字符串。第
一种类型,也是最常见的类型是以空字符终结的字符串,它是以空字符结尾的字符数组(空字符
就是0)。所以,一个以空字符终结的字符串就包含了组成串的字符以及一个空字符。由于以空
字符终结的字符串提供了高层次的功效,并能使程序员对字符串操作实施详细的控制,所以其
被广泛使用。当C++语言程序员使用术语string时,他或她通常是指以空字符终结的字符串。
C++语言定义的第二种字符串是string类,它是C++类库的一部分。因而,string不是一种内置
类型。它提供给字符串处理程序一个面向对象的方法。但是,它不如以空字符终结的字符串使
用广泛。在此我们仅论述以空字符终结的字符串。

4.4.1字符串基本知识


当定义将保存以空字符终结的字符串的字符数组时,用户需要定义数组的长度比其将保存
的最大的字符串长出一个字符。例如,如果您想定义一个str数组,该数组能够保存一个10个
字符的字符串,就需要作如下编写:


charstr[11];


之所以将数组长度指定为11,是为了给在字符串末尾的空字符留出空间。

4.4.2 从键盘中读取字符串

读取从键盘中输入的字符串的最容易的方式是在cin语句中使用char数组。例如,下面的
程序读取了由用户所输入的字符串:
//Using cin to reada string from the keyboard.
#include <iostream>
using name spacestd;
int main()
{
char str[80];
cout<<"Enter a string:";
cin>>str;//read string from keyboard 使用cin读取字符串
cout<<"Here is your string:";
cout<<str;
return 0;
}
运行的结果如下:
Enter a string:testing
Here is your string:testing
尽管从技术上讲,该程序是正确的,但该程序并非总是按照您所期望的方式运行。运行该
程序,并尝试输入“This is a test”字符串,您就会了解原因所在了:
Enter a string:This is a test
Here is your string:This
当该程序重新显示您所输入的字符串时,它仅仅显示了单词“This”,而不是完整的语句。
其原因在于当遇到第一个空白字符时,C++语言的I/O系统就会停止阅读字符串。空白字符包
括空格、制表符和换行符。


要解决空白符的问题,将需要使用C++语言库的另外一个函数gets()。调用该函数的一般
形式为:


gets(array-name)


如果用户需要自己的程序读取字符串的话,就可以调用gets()函数,将没有下标的数组名
作为该函数的参数。一旦从gets()函数返回,数组将保存从键盘输入的字符串。gets()函数将持
续读取字符,直到用户输入回车为止。gets()函数使用的头文件是<cstdio>。

 

 

另外一点需要注意:在cout语句中,str数组能被直接使用。通常,只要能使用字符串常量,就可以使用保存字符串的字符数组的名称。记住,无论是cin语句还是gets()函数都不会执行对数组的任何边界核查。所以,如果用
户输入的字符串的长度比数组的长度长的话,数组将被重写。稍后,我们将学习一个可替代gets()
的函数,该函数可以避免这个问题。

 

4.5一些字符串库函数


C++语言支持广泛范围的字符串操纵函数。其中最常用的有
strcpy()
strcat()
strcmp()
strlen()


所有的字符串函数都使用相同的头文件<cstring>。现在让我们来了解一下这些函数。


4.5.1   strcpy函数
调用strcpy()函数的一般形式为:
strcpy(to,from);
该函数能将from字符串中的内容复制到to字符数组中。请记住,构成to的数组必须足够
大,以便保存包含在from中的字符串。否则,to数组将会溢出,这很可能会使您的程序崩溃。
4.5.2   strcat函数
调用strcat()函数的一般形式为:
strcat(s1,s2);
strcat()函数能将s2添加到s1的末端;但并不修改s2。用户必须确保s1足够大,以便保存
它自己的原始内容和s2的内容。
4.5.3  srcmp函数
调用strcmp()函数的一般形式为:
strcmp(s1,s2);
strcmp()函数能比较两个字符串,如果两个字符串相等的话,返回0。如果s1在字典顺序第4章数组、字符串和指针111
上比s2大(即,依照字典顺序),则返回一个正数;如果比s2小,则返回一个负数。
使用strcmp()函数的关键在于记住当字符串匹配时,它就返回“假”。所以,假如用户希
望在字符串相等时,发生某些事情的话,将需要使用!运算符。如下列if语句,当str等于“C++”
时,控制该语句的条件就为“真”:
if(!strcmp(str,"C++")cout<<"strisC++";
4.5.4strlen函数
调用strlen()函数的通式为:
strlen(s);
该函数中的s是一个字符串。strlen()函数能返回s字符串的长度。

 

4.5.6使用空终结符

 

以空字符终结的字符串常常能够用来简化各种操作。例如,下面的程序将一个字符串转换
为大写字母。

 

 

该程序使用库函数toupper()来转换字符串中的每个字符。该函数能返回字符参数的大写形
式。toupper()函数的头文件为<cctype>。
请注意,for循环的测试条件仅仅是由控制变量作为下标的数组。将数组元素作为测试条件
的原因是一个真值是任何非零的值。要记住,除了终结字符串的空字符是零以外,所有字符的

值都是非零的。所以,循环将持续运行,直到其遇到空终结符为止,空终结符导致str[i]的值变
为0。由于空终结符标志着字符串的终结,所以循环就会精确地在所期望的地方停止运行。在
专业化编写的C++代码中,用户将会看到很多以类似的方式使用空终结符的实例。

 

4.6数组的初始化


C++语言允许数组被初始化。数组初始化的通式与其他变量初始化的通式相类似,如下所示:


type-specifierarray_name[size]={value-list}


其中,value-list是指一列用逗号隔开的值,这些值所属的类型与数组的基本类型相兼容。
第一个值被放在数组的首位,第二个值放在第二位,以此类推。请注意,要在}后增加一个分号。
在下面的实例中,一个具有10个元素的整数数组被初始化为从1到10的数字。


inti[10]={1,2,3,4,5,6,7,8,9,10};


这意味着i[0]的值为1,i[9]的值为10。


保存字符串的字符数组允许简写的初始化形式为:


chararray_name[size]=“string”;

 

例如,下面的代码片段将str初始化为字符串“C++”:


charstr[4]="C++";


这种方式和下面的书写方式是一样的:


charstr[4]={'C','+','+','/0'};


由于C++语言中的字符串必须以空字符作为结尾,所以用户必须确保所定义的数组能足以
包含字符串。这就是即使实例中的“C++”只有3个字符长,str也要有4个字符长的原因所在。
当使用字符串常量时,编译器会自动提供空终结符。

 

&是个一元运算符,能返回它的操作数的内存地址(如
前所述,一元运算符仅仅要求一个操作数)。例如:
ptr=&total;
把变量total的内存地址输入到ptr中。该地址是total在计算机内存中的位置。它与total
的值无关。运算符&的作用是返回它所引领的变量的“地址”。所以,上述赋值声明或许可以
被描述为“ptr接收total的地址”。要更好地理解该赋值,可以假设变量total位于位置100。

那么,在发生该赋值以后,ptr就具有了值100。

 


第二种运算符是*,它是&运算符的相反形式。它是一元运算符,该运算符能够返回位于其
操作数所指定的地址的变量的值。继续使用同一实例,如果ptr包含了变量total的内存地址,
那么,
val=*ptr;
将会将total的值置入val中。例如,如果total原先的值是3200的话,那么val也将具有
值3200,因为它是存储在赋给ptr的内存地址——位置100中的值。*运算符的作用可以记作为
“在地址中”。那么,在这种情况下,该声明或许可以被描述为“val接收了在地址ptr中的值”。

 

 

4.11.1   指针运算
在指针上只能使用4种算术运算符:++、––、+和–。要了解在指针运算中会发生什么,
可以把p1假设为一个当前值为2000的int型指针(即,它包含了地址2000)。假定32位的整数,
经过下列的表达式后

 

指针运算实质上就是内存地址之间的运算

 

除了指针与整数的加减,或者两个指针相减以外,在指针上不能再执行任何其他的算术运
算。例如,不能把指针与float型或double型的值相加减。

 

4.11.2三陵 指针比较

 

使用例如==、<和>这样的关系运算符能够比较指针。一般而言,要使指针比较的结果具
有意义,相比较的两个指针之间通常必须具有某种关系。比如,双方都指向同一数组中的元素(在

项目4-2中,我们将看到一个这样的实例)。但是,也存在一种其他类型的指针比较:任何指针
都可以与值为0的空指针相比较。

 

这两个语句都获得了第5个元素。如前所述,数组下标是从0开始的。所以当给str数组加
下标时,使用4访问第5个元素。由于p1当前指向了str的第1个元素,所以4也被添加给了

指针p1,以获得第5个元素。
由于*运算符的优先级比+运算符的优先级高,所以有必要用圆括号把p1+4括起来。否则,
表达式将首先找到p1所指向的值(数组中的第一个位置),然后把该值与4相加。
实际上,C++语言允许使用两种访问数组的方法:指针运算和数组索引。这很重要,因为
指针运算的访问速度有时比数组索引快——特别是当您以严格的先后顺序访问数组时。由于在
编程中常常要考虑速度因素,所以在C++程序中,使用指针访问数组元素是很常见的。用户有
时还能够使用指针代替数组索引,来编写较紧密的代码。

 

代码片断

#include <iostream>
#include <cctype>
#include <cstring>
using namespace std;

int main(){

 char str[80] ="This is Test!";
 int i;

 cout<<"The old string is :"<<str<<'/n';

 for (i=0;str[i];i++)
 {
 
  if (isupper(str[i])){
   str[i] = tolower(str[i]);  
  }
  else if(islower(str[i])){  
   str[i] = toupper(str[i]);
  }
 }
 cout<<"The new string is :"<<str<<'/n';

 char *p;
 p = str;//一个没有下标的数据名会返回指向数组第一个元素的指针

 while(*p){
  if(isupper(*p))
   *p = tolower(*p);
  else if(islower(*p))
   *p = toupper(*p);
  p++;

 }

 cout<<"指针访问数组:"<<str<<'/n';

 //------------------------------------------------------------
 //利用指针来颠倒这个字符串
 //---------------------------------------------------------------
 char str1[] = "this is test";
 char *start,*end;
 char t;
 int len;

 len = strlen(str1);
 start = str1;
 end = &str1[len-1]; //减1是为了排除终止符

 while(start<end){
 t = *start;
 *start=*end;
 *end = t;

 start++;
 end--;
 }
 cout<<"颠倒字符串:"<<str1;

 return 0;
}

 

4.14  指针数组

 

像任何元素一样,指标也可以建立指针数组

 

比如:

 

char *p[10];

 

p是代表一个包含10个char型指针的数组