题记:
指针用于指向某个对象。与迭代器一样,指针提供对其所指向的对象的间接访问。与迭代器不同之处在于:指针用于指向单个对象,而迭代器只能用于访问容器内的元素。
10.1 指针的定义与初始化
事实上,指针保存的是某个对象的地址
程序清单—01
1 string str("Hello World !"); 2 string *pStr=&str; 3 cout<<*pStr<<endl; //输出指向的对象 4 cout<<pStr<<endl;//输出指向对象的地址
第一行语句定义了一个字符串str;
第二行定义了一个指向字符串类型的指针pStr,并将该指针初始化指向str
程序结果:
一个有效的指针必然是以下三种状态之一:保存一个特定对象的地址(“&”,取地址符);指向某个对象后面的另一个对象;初始值为0值(不指向任何对象)
程序清单—02
int temp=10; int *pInt1=0; // ok,指针pInt1初始化为0值,不指向任何对象 int *pInt2=&temp; //ok,指针pInt2初始化为整型temp的地址,即指向temp int *pInt3;//ok,但没有初始化,比较容易出错 pInt=pInt2; //ok,指针pInt1和pInt2都指向变量temp pInt2=0;//此时,指针pInt2不指向任何对象
10.2 指针操作
10.2.1 指针的简单操作
string s1="Hello World !"; string s2=“China”;
string *sp1=&s1; //sp1指向对象s1 string sp2=&s2;//sp2指向对象s2
sp1=sp2;//指针sp1与sp2均指向了对象s2
程序清单—03
//修改指针的值 #include <iostream> using std::cin; using std::cout; using std::endl; int main() { int val=10; int *ip1=&val,*ip2=0; ip2=ip1; cout<<*ip2<<endl; system("pause"); return 0; }
程序清单—04
//修改指针所指向的对象 #include <iostream> using namespace std; int main() { int val=10; int *ip1=&val; *ip1=110; cout<<*ip1<<endl; system("pause"); return 0; }
程序清单—05
//指向指针的指针 #include <iostream> using namespace std; int main() { int val=1024; int *pi=&val; int **ppi=π cout<<val<<endl<<*pi<<endl<<**ppi<<endl; system("pause"); return 0; }
10.2.2 指针访问数组
int arr[]={1,2,3,4,5};
int *ip=arr; //定义一个指向整型的指针,且指向整型数组arr的受地址,即等同于 int *ip=&arr[0];
int *ipp=&arr[3];//指针ipp指向数组arr的第4个元素
int *ip2=ip+3;//指针ip2指向arr[3],这里所加的整数不能超出数组长度
ptrdiff_t n=ip2-ip;//结果为3
注:两个指针相减的结果是标准库类型ptrdiff_t的数据类型,其定义在cstddef头文件中
const size_t array_size=5;
int arr[array_size]={1,2,3,4,5};
int *ip1=arr; //ip1指向arr[0]
int *ip2=ip1+array_size;//ip2指向arr数组的超出末端的位置
注:C++允许计算数组或对象的超出末端的地址,但不允许对此地址进行解引用操作。
程序清单—06
#include <iostream> using namespace std; int main() { const size_t array_size=5; int arr[array_size]={1,2,3,4,5}; for(int *pstart=arr,*pend=pstart+array_size;pstart!=pend;++pstart) { cout<<*pstart<<' '; } system("pause"); return 0; }
10.2.3 指针与const限定符
1.const指针
int errNum=0;
int *const ip=&errNum; //这里定义了一个const指针ip
注:const指针本身不能修改,即不能const指针指向其他对象;但const指针所指向的对象的值可以修改
2.指向const对象的指针
const int *ip2 ;//这里定义了一个指向const对象的指针ip2
- 指向const对象的指针定义中,const限定了指针所指向的对象类型,而并非指针本身,意味着,指针本身可以修改,而所指向的对象不可以修改。
- 把一个const对象的地址赋给一个普通的、非const对象的指针是不合法的;
- 不能使用void指针保存const对象的地址,而不许使用const void *类型的指针保存const对象的地址;
- 允许将非const对象的地址赋给指向const对象的指针
const double pi=3.14;
double *pdu=π//error,pdu是一个普通指针,不可以指向一个const对象
const double *pdu2=π//ok
const int numb=30;
const void *ip1=&numb;//ok
void *ip2=&numb;//error,不能用void指针保存const对象的地址
3.指向const对象的const指针
const double pi=3.14
const double *const pdu3=&pi;
注:指向const对象的const指针定义中,既不能修改指针所指向的对象的值,也不能修改指针的指向(即指针存放的地制止)
10.3 C风格字符串标准库函数
要使用C风格字符串的标准库函数,必须包含相应的C头文件: #include<cstring> ,相当于C语言提供的#include<string.h>
表10-1 C风格字符串的标准库函数
永远不要忘记字符串结束符null
char ca[]={'c','+','+'};
cout<<strlen(ca)<<endl; //ca不是以null结束的,出现错误!!
P117 习题4.25 编写程序比较两个string类型的字符串,然后编写另一个程序比较两个C风格字符串的值
程序清单—07
//两个string类型的字符串的比较 #include <iostream> #include <string> using namespace std; int main() { string s1(10,'a'),s2(10,'b'); if(s1>s2) cout<<"s1>s2"<<endl; else if(s1==s2) cout<<"s1=s2"<<endl; else cout<<"s1<s2"<<endl; system("pause"); return 0; }
程序清单—08
//两个string类型的C风格字符串的比较 #include <iostream> #include <string> using namespace std; int main() { const char *str1="I love Jiangsu"; const char *str2="I love China"; int temp=strcmp(str1,str2); if(temp>0) cout<<"第一个字符串大"<<endl; else if(temp<0) cout<<"第二个字符串大 "; else cout<<"同样大"<<endl; system("pause"); return 0; }
10.4 动态数组
数组类型的变量有三个重要限制:
- 数组的长度是固定不变的;
- 数组在编译时必须知道其长度;
- 数组只有在定义它的语句块内存在。
注1:实际程序往往需要在运行时动态地分配数组(动态分配的数组不必在编译时知道其长度,可以在运行时才确定其长度),并且动态分配的数组将一直存在,知道程序显式释放它为止。
注2:每一个程序在执行时都占用一块可用的内存空间,用于存放动态分配的对象,此内存空间称为程序的自由存储区或堆。
注3:C语言采用标准库函数malloc和free来创建和释放动态分配的对象,C++采用new和delete来创建和释放动态分配的对象。
1.动态数组的定义
动态分配数组只需指定类型和长度,不必为数组对象命名,new表达式返回指向新分配数组的第一个元素的指针。如:
int *ip = new int[10];
上述语句动态分配了一个含有10个整型元素的数组,并返回指向该数组的第一个元素的指针,此返回值初始化了指针ip
2.初始化动态数组
动态分配数组时,如果数组元素是类类型,将使用该类的默认构造函数实现初始化;如果数组元素是内置类型,则无初始化。如:
int *ip = new int[10];
string *pstr=new string[10];
上述两条语句都分配了含有10个对象的数组,其中第一个数组是整型(术语内置类型),无初始化;第二个数组是string类型,分配了保存对象的内存空间后,将调用string类型的默认构造函数来初始化数组的每一个元素。
也可以使用跟在数组长度后的一对圆括号,对数组进行初始化
int *ip = new int[10]();//圆括号要求编译器对数组进行初始化,这里将数组元素都设置为0
3.const对象的动态数组
如果程序在自由存储区中创建了内置类型的const对象,则必须为这个数组提供初始化
const int *ip2 = new const int[10];//error,未初始化const数组
const int *ip3= new const int[10]();//ok,待圆括号的初始化
const string *pstr1=new const string[10];//ok,调用string类型的默认构造函数进行初始化
4.动态空间的释放
动态分配的内存最后必须进行释放,否则,内存会被逐渐耗尽。
C++使用delete[]来释放指针所指向的动态分配的数组。如:delete[] ip;
P138 习题4.28: 编写程序由标准输入设备读入的元素数据建立一个int型vector对象,然后动态的创建一个与该vector对象大小一致的数组,将vector对象所有元素都赋值给新的数组。
程序清单—09
#include <iostream> #include <vector> using namespace std; int main() { vector<int> ivec; int val; cout<<"请输入一些数字"<<endl; cin>>val; while(cin>>val) { ivec.push_back(val); if(val==-1) break; } int *ip=new int[ivec.size()]; int *op=ip; for(vector<int>::iterator iter=ivec.begin();iter!=ivec.end();iter++,op++) { *op=*iter; } delete [] ip; system("pause"); return 0; }
P122 习题4.32:编写程序用int型数组初始化vector对象
程序清单—10
#include <iostream> #include <vector> using namespace std; int main() { const size_t arr_size=10; int arr_int[arr_size]; cout<<"input "<<arr_size<<" numbers"<<endl; for(size_t ix=0;ix<arr_size;ix++) { cin>>arr_int[ix]; } cout<<"vector对象元素如下:"<<endl; vector<int> ivec(arr_int,arr_int+arr_size); for(vector<int>::iterator iter=ivec.begin();iter<ivec.end();iter++) { cout<<*iter<<' '; } system("pause"); return 0; }