1. 使用指针对字符串排序
以下代码有问题!编译的时候没问题,但是输入后回车,会出错,具体原因不知道!
#include <iostream>
#include <string>
using namespace std;
int main()
{
string text;
const string separators = " .,\"\n";
const int max_words = 1000;
string words[max_words];
string* pwords[max_words];
cout << endl << "Enter a string terminated by #: " << endl;
getline(cin, text, '#');
int start = text.find_first_not_of(separators);
int end = 0;
int word_count = 0;
while(start != string::npos && word_count < max_words)
{
end = text.find_first_of(separators, start + 1);
if(end == string::npos)
end = text.length();
words[word_count] = text.substr(start, end - start);
pwords[word_count] = &words[word_count];
word_count++;
start = text.find_first_not_of(separators, end + 1);
}
int lowest = 0;
for(int j = 0; j < word_count - 1; j++)
{
lowest = j;
for(int i = j + i; i < word_count; i++)
{
if(*pwords[i] < *pwords[lowest])
lowest = i;
}
if (lowest != j)
{
string* ptemp = pwords[j];
pwords[j] = pwords[lowest];
pwords[lowest] = ptemp;
}
}
for (int z = 0; z < word_count; z++)
cout << endl << *pwords[z];
cout << endl;
return 0;
}
以上代码具体原因不知道,暂且先放下,但是代码内容基本正确,可以参考。
如果想使输出结果更整齐:比如说大量的相同首字母的单词分组输出,输出效果会更加整齐
//分组输出
char ch = (*pwords[0])[0]; //第一个单词的第一个字母
int words_in_line = 0; //一行的单词计数
for (int i = 0; i < word_count; i++)
{
if(ch != (*pwords[i])[0]) //判断是否是新的首字母,只要显示的单词第一个字母不同于ch中的字符,就开始一个新组,同时输出一个换行符
{
cout << endl; //开始新的输出行
ch = (*pwords[i])[0]; //保存新的首字母
words_in_line = 0; //重置一行的单词计数
}
cout << *pwords[i] << " ";
if (++words_in_line == 6) //每6行就输出一个换行符
{
cout << endl;
words_in_line = 0; //新的单词输出行
}
}
注意,我们引用了第一个string对象的第一个字符,表达式*pword[0]解除了pwords数组中第一个元素的指针的引用,给出了该指针指向的单词。
而表达式(*pwords[0])[0]给出了该单词的第一个字母,因为圆括号外的方括号包含了单词中的字母的索引。
由于方括号的优先级高于间接运算符*,因此用圆括号分开。
1. 常量指针和指向常量的指针
指向常量的指针。指针指向的内容不能修改,但可以把指针设置为其他内容:
const char* pstring = “Some tet that cannot be changed”; |
当然,也使用与其他类型的指针:
const int value=20; const int* pvalue=&value; |
value是一个常量,不能修改。pvalue是一个指向常量的指针,可以用于存储value的地址。
不能再不是常量的指针中存储value地址(因为这意味着可以通过指针修改常量),但可以把不是常量的变量的地址赋予pvalue。在后一种情况中,通过指针修改变量是非法的。
一般情况下,总是用这种方式加强常量的不变性,相对减弱常量的不变性是不允许的。
常量指针。存储在指针中的地址不能修改。
像这样的指针只能指向初始化时指定的地址。但是,地址的内容不是常量,可以修改。
int value=20; int* const pvalue=&value; |
这个语句声明,指针pvalue时const,只能指向value。不能指向另一个变量。但是value的内容不是const,所以可以随时修改。
但是如果value声明为const,就不能用&value初始化pvalue,指针pvalue只能指向不是常量的int类型的变量。
指向常量的常量指针。因为存储在指针中的地址和该指针指向的内容都声明为常量,所以两者都不能修改。
const int value=20; |
value现在是一个常量,不能修改它。但是仍可以用value的地址来初始化一个指针:
const int* const pvalue=&value; |
pvalue现在是一个指向常量的常量指针,不能修改pvalue指向的内容,也不能修改它包含的地址上的值。
注意:
一般情况下,这并不仅限于char和int类型指针,可以应用于任意类型。
2. 指针和数组
指针的算术运算:只能对指针进行加减运算,但可以比较指针,得到逻辑结果。
pvalue++; pvalue +=1; |
都是可以的,指针加上一个整数,结果是一个地址。减去一个整数,结果也是一个地址。
这个可以运用于数组的操作,因为数组的存储连续的,每个数组元素的地址之间相差4位。
使用数组名的指针表示法
data[3]等价于*(data+3) |
即,data[0]等价于*data,因为指针指向的是数组的第一元素,而数组的第一个元素可以表示为data[0]。
代码:把数组名用作指针
下面代码对数组使用指针,在一个面向数值的程序中计算质数。
质数是只能被1和它本身整除的整数。
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
const int max = 100; //集合中质数个数
long primes[max] = {2, 3, 5}; //质数集合的前三个数
int count = 3; //质数个数
long trial = 5; //下个测试的数
bool isprime = true; //判断一个数是否为质数
do
{
trial += 2; //下一个值的检测
int i = 0;
//循环中的算法依据:任何不是质数的整数都可以被更小的质数整除。
do
{
isprime = trial % *(primes + i) > 0; //不能整除
} while (++i < count && isprime); //当i的值达到count或者isprime为flase(可以被整除,即trial不是质数),就跳出循环,如果trial不能被primes数组中的所有质数整除,isprime就是true,循环在i达到count时结束。
if (isprime) //判断是否被整除,如果不能被整除,那么就说明是质数,将trial中的值存储在primes[count]中,再递增count。
*(primes + count++) = trial;
} while (count < max);
//一旦找到max个质数,就显示它们,一行显示5个质数,字符宽度设置为10个字符
for (int i = 0; i < max; i++)
{
if (i % 5 == 0) //当i的值为0、5、10等值时,就另起一行
cout << endl;
cout << setw(10) << *(primes + i); //设置每个字符之间宽为10个字符
}
cout << endl;
return 0;
}
实际上,如果当前整数只能被小于或等于该整数的平方根的质数整除,就需要测试,因此以上代码不高效。
对多位数组使用指针
通常方式使用数组名,带有两个索引值
beans[i][j] |
指针表示法中使用数组名
从内层开始确定这个表达式的含义。
beans表示数组中第一行的地址,beans+i表示数组的第i行。表达式*(beans+i)是第i行上第一个元素的地址,*(beans+i)+j是第i行上第j个元素的地址。整个表达式*(*(beans+i)+j)就是该元素的值。
C样式字符串的操作
<cctype>头文件中声明的函数,可以分析和转换单个字符。
<cstring>头文件中声明的一些函数,可用于分析和转换非空字符串的函数。(不要把<cstring>和<string>混淆,后者定义了string类型。)
连接字符串——strcat()函数和strncat()函数
函数strcat()带两个参数,分别是char*类型和const char*类型的字符串,该函数把后一个参数追加到前一个参数中,在这个过程中修改第一个参数。它会改写第一个字符串尾部的’\0’,并把’\0’加到组合字符串的最后,单用户应确保第一个参数指向的字符串有足够的空间容纳结果。第一个参数也是函数的返回值(因为指向的第一个参数,也就是指向了整个字符串)。
函数strncat()带有三个参数,前两个参数与strcat()相同。第三个参数是一个整数,指定把第二个字符串的多少个字符加到第一个字符串上。如果指定的字符个数多于字符串中的字符个数,就追加整个字符串。除此之外,该函数使用条件和返回值与strcat()相同。
使用:
char name[50] = “Bing”; char surname[] = “Crosby”; |
下面的语句把一个空格追加到name字符串上:
strcat(name, ” ”); |
接着,追加surname:
strcat(name, surname); |
结果是name包含字符串“Bing Crosby”。因为函数总是返回第一个参数。
故可以用下面的语句完成上面两个操作:
strcat(strcat(name, “ ”), surname); |
语句较难理解。
验证第一个参数中是否有总够的空间容纳组合字符串,可以使用strlen()函数,这个函数返回其唯一参数的字符串的长度。
if(sizeof name/sizeof name[0] > (strlen(name) + strlen(surname) + 1)) std::cout << strcat(strcat(name, “ ”), surname); |
1. 动态内存分配
在程序中使用固定的变量集合是非常受限制的,而且常常比较浪费。
使用动态内存分配,意味着在程序运行时(运行期间)为正在处理的数据分配存储它所需要的内存空间,而不是在编译它时(编译期间)分配空间。
自由存储区
在大多数情况下,程序执行时计算机上总是有未使用的内存。在C++中,这些未使用的内存称为自由存储区,有时称为堆(heap)。使用一个特殊的C++运算符,就可以把自由存储区中的空间分配给给定类型的新变量,并返回所分配空间的地址。
这个运算符就是new,同它对应的运算符是delete,它可以释放以前用new分配的内存。
注意:在使用new为变量分配内存空间时,就是在自由存储区创建该变量。在变量占用的内存没有用运算符delete释放前,该变量将一直存在。
运算符new和delete
用法:
double* pvalue = 0; pvalue = new double; |
注意,所有的指针都应初始化。
可以初始化用new创建的变量。
pvalue = new double(999.0); |
当不再需要动态分配内存的变量时,就可以用delete运算符,释放它在自由存储区中占的内存:
delete pvalue; |
上面这个语句,将确保该内存可以在以后由另一个变量使用。如果没有使用delete,后来又在指针pvalue中存储了另一个地址,就不能释放原来的内存空间,也不能使用它包含的变量,因为不能再访问该地址了。
注意:delete运算符释放了内存,但没有改变指针。运行完上面的语句后,pvalue仍包含已分配给它的内存地址,但该内存现在已经自由了,可以立即分配给其他实体(比如另一个程序)。为了避免使用包含垃圾地址的指针,除非要重新给它赋值或它已超出作用域,否则就应在释放内存时重新设置改指针,如下:
delete pvalue; pvalue = 0; |
数组的动态内存分配
new:
pstring=new char[20]; |
delete:
delete [] pstring; |
注意:这里的方括号非常重要,表示要删除的是一个数组。从自由存储区中删除数组时,应总是包含方括号,否则结果是不可预料的。还要注意不能在这里指定维数,只写上[]就可以了。
动态内存分配的危险:
A. 内存泄露
在使用new分配内存空间时,由于在使用完该内存后没有释放它,从而会出现内存泄露。
指针和变量作用域
如果指针包含自由存储区中某个内存块的地址,在该指针超出作用域时,就不能释放该内存块了。
B. 自由存储区的碎片
内存碎片在频繁地分配和释放内存块的程序中会出现。
如果穿件并释放许多不同大小的变量,所分配的内存就有可能散布在小的自由内存块。每个小内存块都不足以容纳程序需要的新动态变量。
这个情况现在比较少见,现代的计算机,虚拟内存提供了非常大的内存。在发生这个问题时,解决方法是避免分配较小的内存块,或者自己分配较大的内存块,并管理内存的使用。
转换指针
运算符:reinterpret_cast<>()
上面这个运算符允许强制转换任何指针类型,其限制是如果要强制转换的指针类型声明为const,就不能把它强制转换为不是const的类型。
reinterpret_cast<指针类型>(表达式) |
例如:
float value=2.5f; float* pvalue=&value; long* pnumber= reinterpret_cast<long*>(pvalue); |
这个过程的逆过程——存储long值,并把它解释为float类型——肯定会生成一个格式不正确的浮点数。
强调:reinterpet_cast<>()运算符是很危险的,因为它提供了把一种类型的值随意解释为另一种类型的方式。应该避免使用这个运算符。