C++ 初学者指南 第四篇(12)

必备技能 4.11: 指针和数组
    在C++中,指针和数组有着紧密的联系。实际上,指针和数组常常是能够互换的。考虑下面的代码片断:
char str[80];
char *p1;
p1=str;
这里,str是一个80个字符大小的数组,p1是一个字符指针。然而,第三行的代码就颇有意思了。该行中,p1被赋值为数组str中的第一个元素的地址。(也就是说在这个语句以后,p1就指向str[0]了。)为何了?这是由于在C++中使用数组名,而不使用索引会生成一个指向该数组的第一个元素的指针。所以,赋值语句
p1=str;
就是把str[0]的地址赋值给了p1。理解这一点是很是重要的:当在一个表达式中使用了没有索引的数组名字的时候。它将生成一个指向数组第一个元素的指针。既然如此,在这个赋值语句以后,p1就是指向str的开始,那么咱们就可使用p1来访问数组中的元素。例如,咱们须要访问数组中的第五个元素,咱们可使用
str[4]
或者
*(p1+4)
这两中写法都能获取到数组中的第五个元素。请记住,数组的起始索引是0,因此,当使用索引4的时候,咱们访问到的是数组的第五个元素。由于p1当前的值是指向数组的第一个元素的,因此给指针p1加上4获得的也是第五个元素的地址 。
 p1+4必须用挂号括起来,这是由于*运算符的优先级别高于+运算符。若是没有括号,那么这个表达式将先是获得p1指向的地址的值(也就是数组的第一个元素),而后在给这个值加一。实际上,在C++中有两种用来访问数组元素的方法:有指针参与的算术运算和对数组的索引。这点很是重要,由于指针参与的算术运算有时候比对数组的索引要要快不少,特别是在访问一个元素之间有严格顺序的数组的时候。因为在编程的时候,咱们常常须要考虑速度的问题,因此用指针来访问数组元素在编程中就变得很是广泛。一样,有时候使用指针可使得代码变得更紧凑。
     下面的示例程序演示了使用数组的索引和指针的算术运算来访问数组元素的不一样之处。这里咱们建立两个版本的程序来完成字符串中大小写转换的功能。第一个是使用数组的索引来完成,第二个是使用指针的算术运算来完成。第一个版本的程序以下:
#include <iostream>
#include <cstring>
using namespace std;ios

int main ()
{
    int i;
    char str[80] = "This Is A Test";
    cout << "Original string: " << str << "\n";c++

    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 << "Inverted-case string: " << str;数组

    return 0;
}
上面程序的输出以下:
Original string: This Is A Test
Inverted-case string: tHIS iS a test
     注意上面的程序中使用到了isupper()和islower()两个库函数来判断字母的大小写。函数isupper()在参数的字母是大写的时候返回真值;islower函数在参数的字母是小写的时候返回真值。在for循环中,经过了索引来访问数组中的元素,检查每一个字母的大小写。for循环直到遍历到字符串的结束标识时结束。由于0就是假值,循环就此结束。
     下面是用指针的算术运算来重写上面的程序:
#include <iostream>
#include <cstring>函数

using namespace std;性能

int main ()
{
    char *p;
    char str[80] = "This is A test";优化

    cout << "Original string: " << str << "\n";
    p = str; //给p赋值为数组的首地址
    while(*p)
    {
        if ( isupper(*p) )
        {
            *p = tolower( *p );
        }
        else if ( islower( *p ) )
        {
            *p = toupper( *p );
        }
        p++;
    }spa

    cout << "Inverted-case string : " << str ;指针

    return 0;
}
     在这个版本中,p被赋值为数组str的首地址。在while循环中,检查p指针指向位置的字母的大小写,并进行转换后,对p进行自增。循环直到遇到字符串的结束标识。因为一些c++编译器生成代码的方式的不一样,上面的这两种方式在性能和效率上也不尽相同。一般来讲,使用对数组的索引须要更多的机器指令。所以,在专业的C++代码中,使用上面的第二个版本,也就是经过指针来访问数组中的元素的方式更为广泛。然而,做为C++的初学者,咱们能够随意使用数组的索引这种方法,直到咱们灵活地掌握了指针的用法。
对指针进行索引
      正如咱们前面看到的那样,咱们能够经过指针的算术运算来访问数组中的元素。反之亦然!在C++中咱们还能够经过对指针进行索引,把它像数组同样使用。下面就是使用了这种方式的字母大小写进行转换的第三个版本:
#include <iostream>
#include <cstring>
using namespace std;索引

int main ()
{
    int i;
    char *p;
    char str[80] = "This is A test";

    cout << "Original string: " << str << "\n";
    p = str; //给p赋值为数组的首地址
    //下面经过对p的索引来访问数组中的元素
    for ( i = 0; p[i] ; i++ )
    {
        if ( isupper( p[i] ) )
        {
            p[i] = tolower( p[i] );  //把p看成数组来使用
        }
        else if ( islower( p[i] ) )
        {
             p[i] = toupper( p[i] );
        }
    }

    cout << "Inverted-case string : " << str ;

    return 0;
}
上面的这个程序建立了一个指针p,而后给它赋值为数组的首地址。在for循环的内部,咱们把p看成数组来使用,经过索引来访问数组中的元素,这点在C++中时彻底有效的。语句p[i]在功能上和*(p+i)是相同的。这个程序就更深一步地演示出了指针和数组的关系。
练习:
1. 可否经过指针来访问数组?
2. 是否能够对指针也进行索引,把它看作和数组同样了?
3. 只使用一个数组的名称,而没有使用索引,其结果值如何?
专家答疑:
问:指针和数组是否能够互换?
答:正如前面的几个程序那样,在大多数状况下指针和数组是强相关的,而且是能够互换的。例如,当一个指针指向一个数组的首地址的时候,咱们就可使用指针的算术运算或者相似于数组索引的方式来访问数组中的元素。然而,指针和数组并非彻底能够互换的。例以下面的程序片断:
int nums[10];
int i;
for ( i= 0; i< 10 ; i++)
{
    *nums =i;  //这样写是合法的。
    nums++; // 这样写是错误的,由于nums是不能被修改的。
}
这里,nums是一个整型数的数组。正如注释所描述的那样,对nums使用*运算是彻底能够接受的,而后修改nums的值倒是非法的。由于nums是一个指向数组首地址的常量。所以,不能对其进行自增的运算。一般来讲,使用数组的名称,而没有使用索引确实是产生一个指向数组首地址的指针,可是数组名的值不能被修改。
     尽管,使用数组名产生的是一个指针常量,可是它是能够被用于到指针风格的表达式中的,只要不去修改它的值。例如,下面的代码给num[3]赋值为100的语句是有效的:
*(num+3) = 100; // 这样写是合法的,由于没有修改num的值。
字符串常量
     你可能对下面代码中所涉及到的C++处理字符串常量的方式还不是很清楚:
cout << strlen ("Xadalu");
问题的答案在于:当编译器遇到一个字符串常量的时候,它就把这个字符串加入到程序的字符串表中,并生成一个指针指向它。所以,"Xadalu"就会产生一个指向该字符串的指针。下面的程序是完有效的,它打印出短语Pointers and power to C++.:
#include <iostream>
using namespace std;

int main ()
{
    char *ptr;

    ptr = "Pointers add power to C++.\n";//p被赋值为字符串常量的地址

    cout << ptr;

    return 0; }      在这个程序中,构成整个字符串常量的字符会被存储在字符串表中,ptr被赋值为指向该表中这个字符串的指针的值。  既然在使用字符串常量的时候,会自动地生成一个指向字符串的指针,你可能会想到使用这点来修改字符串表中的常量。这可不是什么好主意,由于不少的C++编译器会对字符串表进行优化,一个字符串常量可能会在程序的多出被用到。所以,修改这个字符串会致使难以预料的后果!