项目 4-2 字符串倒置
我们曾经在前面提到过只有当两个指针指向同一个对象的时候,指针的比较才是有意义的。然后我们又学习了指针和数组之间的关系,这样我们就可以用指针的比较来简化某些算法了。在下面的项目中,我们就将看到这样的一个例子。下面的这个程序完成对存储在数组中的字符串进行倒置的功能。它不是把一个字符串从尾部到首部复制到另外一个字符串中,而是在存储该字符串的数组上完成该字符串的倒置。这个程序使用了两个指针变量来完成这个功能。一个指针初始化为字符串的首地址,另外一个指针初始化为字符串的最后一个字符的地址。通过建立一个循环,交换这两个指针指向位置的字符来完成字符串的倒置。只要起始指针小于结束的指针,循环就一直进行。当起始指针的值等于或者大于结束指针的值的时候,就完成了对字符串的倒置,循环也就结束了。
步骤:
1. 创建一个名称为StrRev.cpp的文件
2. 在文件中增加下面的代码
/*
工程 4-2
原地对一个字符串进行倒置
*/
#include <iostream>
#include <cstring>
using namespace std;
int main
{
char str[] = "this is a test";
char *start, *end;
int len;
char t;
我们要进行倒置的字符串保存在str中。我们将使用指针start, end来访问该字符串。
3. 增加下面的几行代码,完成原始字符串的显示,获取字符串的长度,并完成start,end两个指针的初始化。
cout << "Original: " << str << "/n";
len = strlen(str);
start = str;
end = &str[len-1];
注意,end指针指向的是字符串中的最后一个字符,而不是字符串的结束标记。
4. 增加下面的代码完成字符串的倒置
while ( start < end )
{
// swap chars
t = *start;
*start = *end;
*end = t;
//移动指针
start++;
end--;
}
上面代码的工作原理:只要start指针指向的内存位置小于end指针指向的位置,循环就一直继续。在循环内部,把start和end指针指向的位置的字符进行交换。start指针是通过自增来改变其值的,end指针是通过自减来改变其值的。当end指针大于或者等于start指针的值的时候,字符串的倒置就完成了。因为start和end是指向同一个数组的,所以它们之间的比较有意义的。
5. 下面是完整的程序
/*
工程 4-2
原地对一个字符串进行倒置
*/
#include <iostream>
#include <cstring>
using namespace std;
int main( )
{
char str[] = "this is a test";
char *start, *end;
int len;
char t;
cout << "Original: " << str << "\n";
len = strlen(str);
start = str;
end = &str[len-1];
while ( start < end )
{
// swap chars
t = *start;
*start = *end;
*end = t;
//移动指针
start++;
end--;
}
cout << "Reversed:" << str << "\n";
return 0;
}
上面程序的输出如下:
Original: this is a test
Reversed:tset a si siht
指针数组
像其它类型的数据一样,我们也可以把指针用数组的方式组织起来。例如,下面的语句声明了10个整型指针的数组:
int *pi[10];
这里,pi是10个整型指针的数组。把一个变量var的地址赋值给数组的第三个指针元素的代码如下:
int var;
pi[2]=&var;
请记住,pi是一个指针数组,数组中可以保存的值都是整型值的地址,而不是整型值本身。可以用下面的语句来获取变量的值:
*pi[2]
和其他的数组一样,指针数组也可以进行初始化。最常用的一个对指针数组进行初始化的方法就是为其装载指向字符串的指针。下面就是一个使用二维的字符型指针数组来实现一个小字典的程序:
// 使用一个二维的指针数组来实现一个小字典
#include <iostream>
#include <cstring>
using namespace std;
int main ()
{
char * dictionary [][2] =
{
"pencil", "A writing instrument.",
"keyboard", "An input device.",
"rifle","A shoulde-fired firearm",
"airplan", "A fixed-wing aircraft.",
"network", "An interconnnected group of computers."
"",""
};
char word[80];
int i;
cout << "Enter word:";
cin >> word;
for ( i = 0; *dictionary[i][0]; i++ )
{
if ( !strcmp ( dictionary[i][0], word ) )
{
cout << dictionary[i][1] << "\n";
break;
}
}
if ( !*dictionary[i][0] )
{
cout << word << " not found.\n";
}
return 0;
}
程序的输出可能如下:
Enter word:network
An interconnnected group of computers.
在创建数组dictionay的时候,我们用一组单词和对它们的解释来初始化这个数组。回忆前面讲过C++把程序中所有的字符串常量都存储在字符串表中,数组中只需要保存指向这些字符串的指针即可。这个程序把word和字典中的字符串进行比较。如果相同,则显示出对其的解释。如果没有匹配到相同的单词,则打印错误信息。
请注意,字典最后是以两个空字符串结尾的。*运算获取指针指向位置的字符。如果这个字符是null,那么这个表达式的值就是假值,循环也就结束了。相反,表达式的值就为真值,循环继续进行。
空指针的约定
在声明一个指针之后,还没有给它赋值之前,指针的值是任意的。如果在给指针赋值之前就使用指针,就很有可能导致程序崩溃。然而,我们没有办法来确保不使用未经初始化的指针。但是C++程序员可以采用一种方法来帮助减少这种错误的发生。大家约定俗成的,如果一个指针的值为null(0),那就认为这个指针不指向任何东西。因此,如果所有未使用的指针的值都是null,并且我们在程序中避免使用null值的指针,那么就可以避免地使用未经初始化的指针的错误。这是一个我们都应该遵循的很好的实践。
任何类型的指针都声明的时候可以初始化为null。 例如,下面的代码片段初始化p为null:
float * p = null; // p现在是一个空的指针
我们可以使用if语句来检查空指针:
if ( p ) // p为非空指针的时候,判断成立
if ( !p ) //p为空指针的时候,判断成立。
练习:
1. 程序中使用到的字符串常量是存储在______ 。
2. 下面的语句创建的是什么?
float * fpa[18];
3. 按照约定俗成的规矩,空指针意味着没有被使用的指针,对吗?