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

本文探讨了C++中指针与数组之间的紧密联系,包括如何通过指针访问数组元素,以及指针与数组间的相互转换。同时,还介绍了字符串常量在C++中的处理方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

必备技能 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;

int main ()
{
    int i;
    char str[80] = "This Is A Test";
    cout << "Original string: " << 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 << "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++;
    }

    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++编译器会对字符串表进行优化,一个字符串常量可能会在程序的多出被用到。因此,修改这个字符串会导致难以预料的后果!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值