代码改变世界

读书笔记:C++ Primer系列(10)—— 数组与指针(2)

2013-12-30 16:56  Keiven_LY  阅读(262)  评论(0)    收藏  举报

题记:

指针用于指向某个对象。与迭代器一样,指针提供对其所指向的对象的间接访问。与迭代器不同之处在于:指针用于指向单个对象,而迭代器只能用于访问容器内的元素。

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=&pi; 
    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=&pi;//error,pdu是一个普通指针,不可以指向一个const对象

                         const double *pdu2=&pi;//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;  
}