您的位置:首页 > 编程语言 > C语言/C++

C++中关于指针的理解(一点一点完善中)

2016-02-21 21:58 260 查看
一、指针

1. 指针的值是一个地址,通过间接寻址运算符*来区分地址与指针所指地址保存的值区分开。

一个变量的地址称为该变量的指针。

如果有一个变量是用来专门存放另一变量地址(即指针)的,则称它为指针变量。

要区分指针和指针变量!!!

*实际上是运算符重载(C++中提及),表示指向。

例如: int *p,i;

p = &i;

p的值是i的地址,*p的值为i的值。

指针的两个属性:内容和位置。

其中位置可以存储在另一个变量中,这样便成为了指向指针的指针。

a、指针变量的定义

基类型 *指针变量名;

可以这么说:指针变量名是指向基类型的指针变量。

所定义的指针变量是指向基类型的指针变量,或者说是指针变量只能存放基类型数据的地址。

要注意:不能用一个整数给一个指针变量赋初值。

b、引用指针变量

与指针变量有关的运算符:

&——取地址运算符

*——指针运算符,也称间接访问运算符

c、指针作为函数参数

最经典的例子是swap函数:

#include <iostream>
using namespace std;
int main()
{
void swap( int *p1,int *p2 );
int *po1,*po2,a,b;
cin >> a>>b;
po1 = &a;
po2 = &b;
if ( a < b )
swap( po1,po2 );
cout<<"max="<<a<<"min="<<b<<endl;
return 0;
}
void swap( int *p1,int *p2 )
{
int temp;
temp = *p1;
*p1 = *p2;
*p2 = temp;
}


在上例中,如果写成这样,就不对

void swap( int x,int y )
{
int temp;
temp = x;
x = y;
y = temp;
}


因为实参与形参值的传递是单项的值传递方式,只能从实参传向形参,形参的改变无法传给实参。

即调用函数时不会改变实参指针变量的值,但可以改变实参指针变量所指向变量的值。

为了动态的分配和回收内存空间,需要用到new和delete函数。

new:从内存中获得存储对象所需要的内存空间。

例如:p = new int;

只是程序请求足够空间来存储一个整数,这部分内存的地址存储在p中,可以简洁的通过指针对p指向的内存赋值,也可以使用赋值语句q = p将存储在p中的地址付给另一个指针。

delete p;

与delete有关的两个问题:

(1)、悬挂引用问题

执行上述语句后所释放的内存空间的地址仍然在p中,但对程序而言,这个内存空间已经不存在了,这就是悬挂引用问题。

为了避免悬挂引用问题,必须将一个特定地址赋给指针,可以将空地址赋给指针。

eg : p = 0;

意思是p变为空或者p是空的。

(2)、内存泄露问题

eg: p = new int;

p = new int;

在上面两条语句执行后,第一个p所指单元变得不可访问,导致了内存泄露问题。

应该是:

p = new int;

delete p;

p = new int;

二、指针与数组

在C++中,数组必须提前声明,但是数组大小是很难预测的,这个问题可以通过指针来解决。

例如:

int a[5],*p;
for ( sum = a[0],i = 1;i < 5; i++ )
sum +=a[i];
//就等同于

for ( sum = *a,i = 1;i < 5; i++ )
sum +=*(a+i);//表达式a+i就等于内存地址 a + i*sizeof(int)
//就等同于

for ( sum = *a,p =a + 1;p <a + 5; p++ )
sum +=*p;


数组也可以动态声明:

p = new int
;

分配了足够的空间来存储n个整数。

指针p也可以看成一个数组型变量,这样就可以使用数组符号。

例如:

for ( sum = p[0],i = 1;i < n;i++ )
sum+=p[i];
//=
for ( sum = *p,i = 1;i < n;i++ )
sum+=*(p+i);
//=
for ( sum =*p,q = p+1;q < p + n; q++ )
sum+=*q;


如果p指向的数组现在不需要了,就必须使用一下语句将其释放:

delete [] p;

上面[]代表p指向一个数组,此外delete只用于使用new赋值的指针。

注意:

字符串是十分重要的数组类型,与指针有紧密的联系。

要注意许多预定义的函数操作字符串都是假定字符串一空字符’\0’结尾的,在使用时要注意这个问题。

三、指针与复制构造函数

1. 这个部分我想说的主要是在C++中进行对象的复制时,如果没有写复制构造函数的情况。

首先,如果类中包含指针变量,且要对对象进行复制。

class Node {
char *name;
int age;
Node(char *n="",int a = 0)
{
name = strdup(n);
age = a;
}
};

Node node1(“roger”,20),node2(node1);
//上面的node2(node1); 等同于 node2 = node1;

strcpy( node2.name,"wendy" );
node2.age = 30;
cout<<node1.name<<""<<node1.age<<""<<node2.name<<""<<node2.age;
//结果是 wendy 30 wendy 20


这就是Node定义中没有提供复制构造函数Node ( const Node& )的结果。

当执行node2(node1)时,复制构造函数是必不可少的。

如果没有定义复制构造函数,编译器会自动生成一个,但是编译器生成的复制构造函数只是逐个对成员进行复制。

因为Node中的数据成员name是一个指针,所以编译器自动生成的复制构造函数将node1.name的字符串地址复制给node2,而不是复制字符串的内容,即node1.name和node2.name指向同一个字符串,在本例中为roger,在后面进行name的变化时,roger变为wendy,同时node1.name和node2.name同时变化。

造成了错误。

为了防止错误的产生,必须定义一个合适的复制构造函数。

class Node{
char *name;
int age;
Node ( char *n = 0,int a = 0 )
{
name = strdup(n);
age = a;
}
Node ( const Node& n )//复制构造函数
{
name = strdup( n.name );
age = n.age;
}
};


使用上述复制构造函数后,声明node2(node1)生成了另一个roger副本,即node2.name与node1.name不指向同一个字符串,而是内容相同的两个字符串,这样改变node1/2的任意name对另一个不会造成影响。

2.赋值运算符也会引起同样的问题

如果用户没有提供赋值运算符的定义,下面的赋值操作

node1 = node2;

就会逐个对成员进行复制,引起上面的问题,解决方法是重载赋值运算符:

Node& operator= (const Node& n)
{
if ( this != &n )
{                   //no assignment to itself
if ( name != 0 )
freee(name);
name = strdup( n.name );
age = n.age;
}
return *this;
}
//this指针指向对象本身


四、指针与析构函数

这部分我想讲的是含有指针数据成员的Node类型的局部对象会发生什么。

局部对象在定义它们的区域之外是无效的,会被销毁,所占用内存也将被释放。

但如果对象数据成员中含有指向字符串的指针,即指针数据成员占用的内存将被释放,而字符串占用的内存却没有释放。

在对象销毁之后,以前可以通过指针访问的字符串不能访问了,(如果没有把字符串赋给其他变量的话),则字符串所占的内存再也无法释放,从而导致内存泄露。

只要是对象具有指向动态分配空间的数据成员,都存在这个问题。

为了解决这个问题,类定义中应该包含析构函数的定义。

当销毁对象,程序从定义对象的块中退出或调用delete时,析构函数抖动调用。

析构函数不带参数,也没有返回值,所以每个类中只能有一个析构函数。

对于上面的类Node。可以定一下面的析构函数:

~Node (){
if ( name != 0 )
free( name );
}


五、指针与引用变量

六、函数指针

double f( double x )
{
return 2*x;
}

double sum ( double (*f) (double),int n ,int m )
{
double result = 0;
for ( int i =n; i <= m; i++ )
result += f(i);
return result;
}


其中double (*f) (double);

意味着f是一个指向函数的指针,该函数带有一个double参数,返回一个double值。

但是必须带()

例如: double *f(double);

指的是声明的函数返回一个指向double的指针。

PS:未完待续
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: