首页 > 技术文章 > char型字符串(数组)与string型字符串 指针与引用

audi-car 2015-04-20 22:21 原文

一、常指针:

int *const p;    //指针不可改变,但是指针指向的数据可以改变。

指向常量的指针:

const int *p;    //指针可以改变,但是指针指向的数据不可以改变。

指向常量的常指针:

const int *const p;    //指针不可改变,且指针指向的数据也不可改变。

引用就是别名,定义引用的同时必须要对引用进行初始化。

二、利用引用返回多个值:

引用就是别名,引用必须要初始化。

#include "stdafx.h"
#include <iostream>
#include <cmath>
using namespace std;
void func(int &a,int &b,int &c);
int main()
{
    int a=1;
    int b=2;
    int c=3;
    cout<<"主函数,计算前。。。"<<endl;
    cout<<a<<"\t"<<b<<"\t"<<c<<endl;
    func(a,b,c);
    cout<<"主函数,计算后。。。"<<endl;
    cout<<a<<"\t"<<b<<"\t"<<c<<endl;
    return 0;
}
void func(int &a,int &b,int &c)
{
    cout<<"func函数,计算前。。。"<<endl;
    cout<<a<<"\t"<<b<<"\t"<<c<<endl;
    a=a+1;
    b=(b)*(b);
    c=(c)*(c)*(c);  //
    cout<<"func函数,计算后。。。"<<endl;
    cout<<a<<"\t"<<b<<"\t"<<c<<endl;
}

三、删除空指针是不会报错的。

int *p;
p=new int;
delete p;
p=0;        //将指针赋值为空,假如没有这句,程序将崩溃。
delete p;

四、删除堆中指针后,只是释放了指针所指向的内存单元,指针仍然存在,且依然指向删除前的内存地址

int *p=new int(11);
cout<<"删除指针p前其指向的内存地址:"<<p<<endl;
delete p;
cout<<"删除指针p后其指向的内存地址:"p<<endl;

程序输出结果:

所以,为了保证程序的安全可靠,需要在delete p后加上p=0

五、类的对象的this指针

对象的this指针存储该对象的内存地址。

#include "stdafx.h"
#include <iostream>
//#include <string>
//#include <limits>
using namespace std;

class A
{
private:
    int i;
public:
    void set(int x){this->i=x;cout<<"this指针的指向的内存地址为:"<<this<<endl;}
    //也可以直接写成i=x;
};

int _tmain(int argc, _TCHAR* argv[])
{
    A a;
    a.set(11);
    cout<<"对象的内存地址为:"<<&a<<endl;
    return 0;
}

//运行结果如下图所示

六、使用引用时容易犯错的地方

临时变量生存期的问题

#include "stdafx.h"
#include <iostream>
using namespace std;

class A
{
private:
    int x;
public:
    A(int i){x=i;}
    void get(){cout<<x<<endl;}
    ~A(){}
};

A &func();

int _tmain(int argc, _TCHAR* argv[])
{
    A &ra=func();
    ra.get();
    return 0;
}

A &func()
{
    A a(11);  //a为临时对象,func函数一旦结束,其生存周期也结束,立即销毁
    return a; 
}

可以看到最后返回的是一个随机数,这就是临时变量被提前删除的缘故

若将func()函数前面的&去掉,则可正确返回a对象的成员变量的值。

此时函数按值返回,对象a的副本生命周期会一直持续到main()函数结束。

这是因为,作为引用的临时变量(即A &ra=func();),其生存周期不会短于该引用。

七、按值传递传递的是变量的副本,按址传递,顾名思义肯定是传送的地址了。

八、关于二维(数组)指针的一些理解问题

int ** Ptr <==> int Ptr[ x ][ y ];
   int *Ptr[ 5 ] <==> int Ptr[ 5 ][ x ];
   int ( *Ptr )[ 5 ] <==> int Ptr[ x ][ 5 ];
   这里 x 和 y 是表示若干的意思。(转)

测试代码:

#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
    int (*p)[2]=new int[2][2]; 
    cout<<"指针数组的初始地址:"<<p<<endl;
    int i,j; 
    int a[2][2]={1,2,3,4}; 
    cout<<"二维数组的初始地址:"<<a<<endl<<endl;;
    cout<<"自定义赋值初始化指针数组!!!"<<endl;
    for (i=0;i<2;i++)  
        for (j=0;j<2;j++)  
        {   p[i][j]=i+j;  }
        //cout<<endl<<"使用二维数组初始化数组指针!!!"<<endl;
        //p=a; //使用二维数组初始化数组指针 
        cout<<"赋值后指针数组的地址:"<<p<<endl;
        for (i=0;i<2;i++) 
        {  
            for (j=0;j<2;j++)    
                cout<<p[i][j]<<"\t";  
            cout<<endl; 
        } 
        delete[] p;
        p=0; 
        return 0;
}

程序输出:自定义赋值初始化指针数组!

下面对以上代码稍作修改,使用二维数组赋初值给指针数组。

#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
 int (*p)[2]=new int[2][2]; 
 cout<<"指针数组的初始地址:"<<p<<endl;
 int i,j; 
 int a[2][2]={1,2,3,4}; 
 cout<<"二维数组的初始地址:"<<a<<endl<<endl;;
 /*cout<<"自定义赋值初始化指针数组!!!"<<endl;
 for (i=0;i<2;i++)  
 for (j=0;j<2;j++)  
 {   p[i][j]=i+j;  }*/
 cout<<endl<<"使用二维数组初始化数组指针!!!"<<endl;
 p=a; //使用二维数组初始化数组指针 
 cout<<"赋值后指针数组的地址:"<<p<<endl;
 for (i=0;i<2;i++) 
 {  
  for (j=0;j<2;j++)    
   cout<<p[i][j]<<"\t";  
  cout<<endl; 
 } 
 //delete[] p;
 //p=0; 
 return 0;
}

程序输出:(delete[] p必须注释掉否则程序会崩溃。)

九、复制构造函数

#include <stdafx.h>
#include<iostream>
using namespace std;
class A
{
private:
    int m,n;
public:
    A(int i,int j){m=i;n=j;}  //构造函数
    A(A&temp){m=temp.m;n=temp.n;} //复制构造函数
    void print(){cout<<m<<"\t"<<n<<endl;}
};
int main()
{
    A a(2,3);
    a.print();




    A b(a);  //or A b=a;也可以
    b.print();
    return 0;
}

浅复制:(被复制对象的变量与复制对象的变量享有同样的内存空间)

#include <stdafx.h>
#include<iostream>
using namespace std;
class A
{
private:
    int *x;
public:
    A(){x=new int;*x=5;}
    ~A(){delete x;x=0;}
    A (const A&temp){x=temp.x;}
    void display(){cout<<*x<<"\t"<<&x<<endl;}
    void set(int i){*x=i;}
};
int main()
{
    A *a=new A;
    a->display();

    A b(*a);
    b.display();

    a->set(10);
    a->display();
    b.display();

    b.set(15);
    a->display();
    b.display();

    //delete a;

    return 0;
}

深复制:(被复制对象的变量与复制对象的变量拥有各自独有的内存空间)

#include <stdafx.h>
#include<iostream>
using namespace std;
class A
{
private:
    int *x;
public:
    A(){x=new int;*x=5;}
    ~A(){delete x;x=0;}
    A (const A&temp){x=new int;*x=*(temp.x);}
    void display(){cout<<*x<<"\t"<<&x<<endl;}
    void set(int i){*x=i;}
};
int main()
{
    A *a=new A;
    a->display();

    A b(*a);
    b.display();

    a->set(10);
    a->display();
    b.display();

    b.set(15);
    a->display();
    b.display();

    delete a;

    return 0;
}

十、char型字符串(数组)与string型字符串

如:char man[100];//当使用cin来输入字符串时,遇到空格(即空字符0 or ‘\0’)的话,字符串输出时就从此截断。

比如输入hello world,那么输出只有hello。

C++提供了一种简单高效的方法来解决上述问题,char man[]={“hello world”},或者char man[]="hello world",此时再输出就正常了。

char型字符串时C语言时的风格,在C++时代产生了C++风格的字符串,string型字符串。使用时必须添加string头文件。

由于string是一个类,那么string str;//str就可以看成是string类的一个对象,该对象可以调用string类的各种成员函数。

1、char型字符串有以下3种形式

char c[12]="study"    //字符数组

“study”                    //未命名字符串

char *p="study"        //指向未命名字符串的指针

以上3种类型只有第一种所指向的字符串可以改变。

#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;

char *get(char *str);

int main()
{
    char name[20];
    char *name1;
    cout<<"请输入您的名字:"<<endl;
    cin>>name;
    name1=get(name);
    cout<<"您的名字是:"<<name1<<endl;
    delete []name1;
    name1=get("Jack");
    cout<<"您的名字是:"<<name1<<endl;
    delete []name1;
    char *name2="Mike";
    name1=get(name2);
    cout<<"您的名字是:"<<name1<<endl;
    delete []name1;
    return 0;
} 
char *get(char *str)
{
    char *p=new char[strlen(str)+1];  //这里如果用sizeof函数,程序会崩溃
    strcpy(p,str);
    cout<<p<<endl;
    return p;
}

2、C的结构体与C++的类的唯一区别在于前者的成员变量默认是公有的,而类的成员变量默认是私有的。

对于公有成员变量,我们可以像使用成员函数那样使用成员变量。

推荐阅读