概述
本文对C/C++指针中的知识点进行总结归纳:
1. C语言指针占多少字节
指针即为地址,指针几个字节跟语言无关,而是跟系统的寻址能力有关,譬如以前是16位地址,指针即为2个字节,现在一般是32位系统,所以是4个字节,以后64位,就为8个字节。
可以在自己的电脑上编程测试如下:
#include<stdio.h>
int main()
{
printf("**************************************n");
printf("测试各种数据类型在计算机中的数据长度n");
printf("**************************************n");
printf("char:");
printf("%dn",sizeof(char));
printf("int:");
printf("%dn",sizeof(int));
printf("float:");
printf("%dn",sizeof(float));
printf("double:");
printf("%dn",sizeof(double));
printf("short:");
printf("%dn",sizeof(short));
printf("long:");
printf("%dn",sizeof(long));
printf("**************************************n");
printf("测试C语言中指针占几个字节n");
printf("**************************************n");
int a=10;
char b='a';
float c=10.0;
int *p1=&a;
printf("a的地址为:0x%x,其字节数为:%dn",p1,sizeof(p1));
char *p2=&b;
printf("b的地址为:0x%x,其字节数为:%dn",p2,sizeof(p2));
float *p3=&c;
printf("c的地址为:0x%x,其字节数为:%dn",p3,sizeof(p3));
return 0;
}
2. 指针定义的两种风格
int *p;//方式1;
int* p;//方式2;
其实这两种定义指针方式各有优缺点:方式1提高便于程序阅读性,表明这是一个指针;
方式2可以这样理解,p是一种int*类型,这种理解在二级指针中可以更好的解释,例int** p,p是一种int**类型的,在指针赋值时便可以用来检测类型是否匹配,int** p;int* q,int a;q=&a;p=&q(解释:p是int**类型的,&q其实是&(int*)是匹配的,但是p=q就会出现问题,因为p是int**类型的,q是int*,两者不匹配),但是这样定义也存在问题:如下
int* p1,p2;//p1是指针(int*型),p2是变量(int型)
如果要使p1和p2都是指针应该如下定义:
int* p1,*p2;//p1和p2都是指针(int*类型)
但是方式1中连续定义两个指针一般如下定义:
int *p1,*p2
3. 定义指针变量
基类型 *指针变量名
定义指针变量是注意几个问题:
(1) 不能用一个整数给一个指针变量赋初值。如int *p=2000 是错误的,愿意是想将地址2000作为指针变量p的初值,但编译系统并不把2000认为是地址(字节编号),而认为是整数,因此认为语法是错误的,显示出错信息。
(2) 在定义指针变量时必须指定基类型。因为例如”使指针移动1个位置”或“使指针值加1”,这个1代表什么呢?如果指针是指向一个整型变量的,那么“使指针移动1个位置”意味着移动4个字节,“使指针加1”意味着使地址值加4个字节。如果指针是指向一个双精度型的变量,则增加的不是4而是8。因此必须规定指针变量所指向的变量的类型。
4. 引用指针变量
有两个与指针变量有关的运算符:
(1)& 取地址运算符
(2)* 指针运算符(或称间接访问运算符)
例如:int a;
int *p=&a
(1)&*p表示的含义:“&”和“*”两个运算符的优先级相同,单按从右向左方向结合,因此先进行*p的运算,它变量a,在执行&预算,因此,&*p与&a相同,即变量a的地址。
(2)*&a的含义:先进行&a运算表示得到a的地址,在进行*运算,即为a。即*&a与a等价,可以发现*和&在一起起到了抵消作用。
5. 指针的作用
(1) 指针作为函数参数:通过调用函数使变量的值发生变化,在主调函数中使用这些改变的值;
举例如下:
#include <iostream>
using namespace std;
int main()
{
void swap(int *p1,int *p2);
int *pointer_1,*pointer_2,a,b;
cin>>a>>b;
pointer_1=&a;
pointer_2=&b;
if(a<b)swap(pointer_1,pointer_2);
cout<<"max=n"<<a<<"min=n"<<b<<endl;
return 0;
}
void swap(int *p1,int *p2) :改为1:void swap(int *p1,int *p2)改为2 void swap(int x,int y)
{ { {
int temp; int *temp; int temp;
temp=*p1; *temp=*p1; temp=x;
*p1=*p2; *p1=*p2; x=y;
*p2=temp; *p2=temp; y=temp;
} } }
加红的这句有问题,由于*temp是指针变量temp所指向的变量。由于未对temp赋值,因此temp并无确定的值(它的值是不可预见的),也就是说,temp所指向的单元是不可预见的。在这样的情况下,对*temp赋值是危险的,可能会破坏系统的正常工作。
修改2为值传递,因为形参和实参是采取单向的“值传递”方式,只能从实参向形参传数据,形参值的改变无法回传给实参。为了使函数中改变的变量值能被main函数所用,不要采取把改变值的变量作为参数的方法,而应该用指针变量作为函数参数。
修改3:
void swap(int *p1,int *p2)
{
int *temp;
temp=p1;
p1=p2;
p2=temp;
}
实参和形参变量之间的数据传递是单向的“值传递”方式。指针变量作为函数参数也要遵循这一规则。调用函数时不会改变实参指针变量的值,但是可以改变实参指针变量所指向变量的值。
(2) 指向数组元素的指针
int a[10];
int *p;
p=&a[0];
p+1表示指向同一数组中的下一个元素(而不是简单的将p值加1)。p+1所代表的地址实际上是p+1*d,d是一个数组元素所占的字节数。
p+i,a+i都表示a[i]的地址,*(p+i),*(a+i)是p+i或a+i所指向的数组元素,即a[i].其实*(p+i),*(a+i)以及a[i]是等价的。
指向数组元素的指针变量也可以带下标,如p[i]与*(p+i)等价。
三种方式:下标法a[i],指针法*(a+i)以及用指针变量指向数组元素,前两种方法的效率是一样的,C++编译系统是将a[i]转换成*(a+i)处理的,第三种方法比前两种方法快,用指针变量直接指向元素,不必每次都重新计算地址,像p++这样的自加操作是比较快的,能够提高执行效率。
区别以下几种情况:
*p++:由于++和*同优先级,结合方向为自右而左,等价于*(p++),即先*p然后再p的值加1.
*(p++)和*(++p)作用不同:前者先取*p的值,然后使p加1.后者是先使p加1再取*p。若p的初值为a(&a[0]),输出*(p++)得到a[0]的值,而输出*(++p)得到a[1]。
(*p)++表示p所指向的元素值加1,即(a[0])++。注意:是元素值加1,而不是指针值加1。
如果p当期指向a[i],则
*(p--) 先对p进行“*”运算,得到a[i],在使p减1,p指向a[i-1]。
*(++p) 先使p自加1,再做*运算,得到a[i+1]。
*(--p) 先使p自减1,在做*运算,得到a[i-1]。
用指针变量作函数参数接受数组地址:用数组名作函数的参数与用指针变量作函数参数的情况相同,其实数组名按指针变量处理的但是有一点是需要注意的:实参数组名a代表一个固定的地址,或者说是指针型常量,因此要改变a的值是不可能的,如a++//语法错误,a是常量,不能改变。而形参数组名是指针变量,并不是一个固定的地址值。它的值是可以改变。在函数调用开始时,它接收了实参数组首元素的地址,但在函数执行期间,它可以被再赋值。
f(array[],int n)
{
cout<<array;
array=array+2;
cout<<*arr<<endl;
}
6. 多维数组与指针
int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}};
a是一个数组名,a数组包含3行,即3个元素:a[0],a[1],a[2]。而每一元素又是一个一维数组,它包
含4个元素(即4个列元素),例如,a[0]所代表的一维数组又包含4个元素:a[0][0],a[0][1],a[0][2],a[0][3].可以认为二维数组是数组的数组,即数组a是有3个一维数组所组成。
从二维数组的角度来看,a代表二维数组首元素的地址,现在的首元素不是一个整型变量,而是由4个整型元素所组成的一维数组,因此a代表的是首行的起始地址(即从0行的起始地址,&a[0]),a+1代表a[1]行的首地址,即&a[1]。请思考,0行1列的地址怎么表示?当然可以直接写为&a[0][1],也可以用a[0]+1来表示。进一步分析,预得到a[0][1]的值,用地址法怎么表示呢?既然a[0]+1是a[0][1]元素的地址,那么,*(a[0]+1)就是a[0][1]元素的值。而a[0]又是和*(a+0)无条件等价的,因此可以用*(*(a+0)+1)表示a[0][1]元素的值。依此类推,*(*(a+i)+j)和*(a[i]+j)都表示a[i][j]的值。
7. 指向由m个元素组成的一维数组的指针变量
可以定义一个指针变量,它不是指向一个整型元素,而是指向一个包含m个元素的一维数组。这时,
如果指针变量p先指向a[0](即p=&a[0]),则p+1不是指向a[0]][1],而是指向a[1],p的增值以一维数组的长度为单位。定义为 int (*p)[4]表示p是一个指针变量,它指向包含4个整型元素的一维数组。注意*p两侧的括号是必不可少,如果写成*p[4],由于方括号[]的优先级别高,因此p先与[4]结合,是数组,然后在与前面的*结合,*p[4]是指针数组。
8. 函数与指针
指针变量也可以指向一个函数。一个函数在编译时被分配给一个入口地址,这个函数入口地址就称为
函数的指针。可以用一个指针变量指向函数,然后通过该指针变量调用此函数。定义形式为:函数类型 (*指针变量)(函数形参表)如int (*p)(int,int),可以跟函数原型相比较,int max(int ,int)可以看出:只是用(*p)取代了max,其它都一样。调用时p=max;//将函数名max赋给p;m=p(a,b)//函数调用,此句和m=max(a,b)等价。
9. 返回指针值的函数
一个函数可以带回一个整型值、字符值等,也可以带回指针型的数据,即地址。概念与以前类似,只是带回的值的类型是指针类型而已。返回指针值的函数简称为指针函数。
定义指针函数的一般形式为
类型名 *函数名(参数列表);
10. 指针数组
如果一个数组,其元素均为指针类型数据,该数组称为指针数组,也就是说,指针数组中的每一个元
素相当于一个指针变量,它的值都是地址。一维指针数组的定义形式为
类型名 *数组名[数组长度];
例如 int *p[4]//说明,由于[]比*优先级高,因此p先与[4]j结合,形成p[4]形式,这显然是数组形式,它有4个元素。然后在与p前面的“*”结合,“*”表示此数组是指针类型的,每个数组元素(相当于一个指针变量)都可指向一个整型变量。
有关指针的数据类型的小结
表格 1 有关指针的数据类型
定义 | 含义 |
int i; | 定义整型变量 |
int *p | p为指向整型数据的指针变量 |
int a[n] | 定义整型数组a,它有n个元素 |
int *p[n] | 定义指针数组p,它有n个指向整型数据的指针元素组成 |
int (*p)[n] | p为指向含n个元素的一维数组的指针变量 |
int f() | f为带回整型函数值的函数 |
int *p() | p为带回一个指针的函数,该指针指向整型数据 |
int (*p)() | p为指向函数的指针,该函数返回一个整型值 |
int **p | p是一个指向指针的指针变量,它指向一个指向整型数据的指针变量 |
11. 函数指针作为函数参数实现回调
指针可以指向函数地址,可以将函数指针作为函数参数,则可以实现回调功能。以下代码片断演示了如何将一个函数指针作为比较的规则。
int compare(int a, int b)
{
if (a == b) return 0;
return (a > b ? 1 : -1);
}
int result(int a, int b, int (*pfun)(int, int))
{
return (*pfun)(a, b);
}
int a = 3, b = 4;
int (*pf)(int, int) = &compare;
std::cout << result(a, b, pf) << std::endl;
上述代码中,函数指针pf指向了函数compare的地址,并且作为函数result的参数来充当比较参数a和b的规则,如果将pf指向另外的函数作为判断规则,就可以得到不同的结果。
12. C++中的指针问题
1、指向对象的指针
定义:对象空间的起始地址就是对象的指针。
说明:在建立对象时,编译系统就为每个对象分配一定的存储空间以存放其成员,不过注意,在一般情况下不同对象的数据存储单元中存放的数据成员是不相同,而不同对象的函数代码却是相同的,也就是说,它们的函数代码是共享的。这时我们可以定义一个指针变量用来存放对象的指针。
定义指向类对象的指针变量的一般形式是:
类名 *对象指针名;
如对于与个Time类对象,我们可以有:
Time t;
Time *p;
p=&t;
我们就可以通过对象指针访问对象和对象的成员,假如所定义的类中有数据成员hour、minute、sec,成员函数有gettime(),则
(*p).hour 即为p指向对象中的hour成员,相当于t.hour
(*p).gettime() 即为p指向对象中的成员函数gettime(),相当于t.gettime()
也可以用如下形式:
p->hour 和 p->gettime()和上面是等价的。
2、指向对象成员的指针
(1)指向对象数据成员的指针
在C中我们学过指向普通变量的指针变量,在C++中定义指向对象数据成员的指针变量的方法和定义指向普通变量的指针变量方法相同,其一般形式为:
数据类型名 *指针变量名;
如:
int *p;
p=&t.hour; //将对象t的数据成员hour的地址赋给p,p指向t.hour
(2)指向对象成员函数的指针
定义指向对象成员函数的指针变量和定义指向普通函数的指针变量不同。
在定义指向普通函数的指针变量时我们可以这样定义:
void (*p)();
p=fun;
(*p)(); //调用fun函数
然而编译系统要求在将函数地址赋给指针变量时必须满足三个条件:
函数参数类型及个数要匹配
函数返回值的类型要匹配
所属的类要匹配
显然在上面的p与类是无关的。为了满足第三条,我们可以为指针指定类,故定义指向对象成员函数的指针变量一般形式为:
数据类型(类名::*指针变量名)(参数表列);
可以让指针指向公用的成员函数,如:
void (Time::*p)(); //定义指向Time类对象成员函数的指针变量p
p=&Time::gettime; //把Time类的公用成员函数gettime()地址赋给指针变量p
(t.*p)(); //调用Time类对象t的成员函数gettime()
注意:因为成员函数不存放在对象空间中,多个同类对象共享此成员函数代码,所以在将成员函数的入口地址赋给指针变量是应写成:
指向对象成员函数的指针变量=&类名::成员函数名;
在成员函数名后有"()",如果写成p=&Time::gettime()是错误的。
3、this指针
在每个成员函数中都包含了一个特殊的指针,称为this,它是指向本类对象的指针,它的值是当前被调用成员函数所在对象的起始地址。之所以有这个指针,是因为为了保证同类的不同对象的成员函数引用的是指定对象中的数据成员,它是系统自动实现的。
如定义一个求体积的函数
int box::vol()
{return(height*width*length);}
假如已经定义了对象t,当调用成员函数t.vol()时,编译系统就把对象t的起始地址赋给this指针,于是在成员函数引用数据成员时,根据指针this就可以引用到对象t的数据成员。所以C++把上面的函数处理成
int box::vol()
{return(this->height*this->width*this->length);}
由于this的值是当前被调用成员函数所在对象的起始地址,所以可以写成
int box::vol()
{return((*this).height*(*this).width*(*this).length);}
所以在调用成员函数t.vlo()时,实际的调用方式为t.vol(&t),不过对象t的地址传给this指针是由系统自动完成的,不需人为加上。
data& add() {
_d += 1;
return *this; // 返回调用该成员函数的类对象本身的引用
}
data d(3);
d.add().add().add(); // 对对象d连续多次调用add()成员函数
在上述代码片断中,d.add()的返回结果是d对象本身,因而可以接下来直接再次调用add成员函数,同时使该对象的_d成员的值增加1;在接下来的两次对add成员函数的调用都会使_d成员值增加1。
看到这个一定要想起运算符重载是的写法,跟这个类似。
4、指向对象的常指针
将指向对象的指针变量声明为const型且进行处始化,这样指针值始终保持为其初值,不能改变其指向。
如:
Time t1(8,8,8),t2;
Time *const p=&t1; //常指针p指向对象t1
p=&t2; //试图改变p的指向,非法
由上面我们可以看出定义指向对象的常指针的一般形式为:
类名 *const 指针变量名=对象的起始地址;
注意:指向对象的常指针变量的值不能改变,但可以改变它所指向对象中的数据成员的值。
常指针一般用作函数的参数,这样就不允许在函数执行过程中改变指针变量的值,使其始终保持指向原来的对象。
5、指向常对象的指针变量
首先回顾一下指向常变量的指针变量,其一般形式为:
const 类型名 *指针变量名;
如:
const char *p;
如果一个变量已经被声明为常变量,那么只能用指向常变量的指针变量去指向它,而不能用一般的指针变量。另外,指向常变量的指针变量除了可以指向常变量外,还可以指向未被声明为const的变量,但也不能通过该指针改变其值。例如:
char c='a' //定义字符变量c,未声名为const
const char *p; //定义指向常变量的指针变量p
p=&c; //p指向字符变量c
*p='b'; //非法
c='b'; //合法
说明:上例中,指针变量p指向字符变量c,并不说把c也声明成常变量,而只是说在通过指针变量引用c时,c具有常变量的特征,其值是不能改变的,但c仍然是一个普通变量。
在指向常变量的指针中,关于函数形参的指针类型有几点值得注意:
如果函数形参是非const型指针变量,实参只能用指向非const型指针;如果函数形参是指向const型变量的指针,实参则可以是const型的或非const型的指针变量。换句话说就是指向常变量的指针可以指向const和非const型的变量,而指向非const型变量的指针只能指向非const的变量。
表2 用指针变量作形参时形参和实参的对应关系表
形参 | 实参 | 合法与否 | 改变指针所指对象的值 |
指向非const型变量的指针 | 非const变量的地址 | 合法 | 行 |
指向非const型变量的指针 | const变量的地址 | 非法 | / |
指向const型变量的指针 | const变量的地址 | 合法 | 不行 |
指向const型变量的指针 | 非const变量的地址 | 合法 | 不行 |
下面说到正题,指向常对象的指针变量和指向常变量指针变量类似。
如果一个对象已经被声明为常对象,只能用指向常对象的指针变量指向它,而不能用一般的指针变量指向它。
如果定义了一个指向常对象的指针变量且使它指向一个非const的对象,其指向的对象不能通过指针来改变。
如果定义了一个指向常对象的指针变量,是不能通过它改变所指向的对象的值的,但是指针变量本身的值是可以改变的。
如:
Time t1(8,8,8),t2; //定义对象
const time *p=&t1; //定义指向常对象的指针p,并使它指向t1
p=&t2; //合法,指针p指向对象t2
所以当我们希望在调用函数时对象的值不被修改,我们就可以把形参定义为const型的指针变量,同时用对象的地址作实参(对象可以是const或非const型);当要求对象不仅在调用函数中不被改变,而且在程序执行过程中也不发生改变,我们就把它定义为const型。所以指向常对象的指针最常用于函数的参数,以保护形参指针所指向的对象在函数执行过程中不被修改。
6、利用指针实现多态
多态(polymorphism)是面向对象程序设计语言的一个重要特性,C++中的虚拟函数(virtual function)是实现这一特性的重要支撑手段。但是利用虚拟函数实现多态只能通过指向对象的指针或者对象的引用两种方法。
// 定义一个描述形状的抽象基类
class shape {
public:
virtual void draw() = 0;
};
// 定义一个派生的三角形类
class triangle {
public:
triangle(const int (&a)[2], const int (&b)[2], const int (&c)[2]) {
p1[0] = a[0], p1[1] = a[1];
p2[0] = b[0], p2[1] = b[1];
p3[0] = c[0], p3[1] = c[1];
}
void draw() {
std::cout << “to draw triangle” << std::endl; // 根据三个顶点信息画三角形
}
private:
int p1[2], p2[2], p3[2]; // 三角形的三个顶点
};
// 定义另外一个派生的圆类
class circle {
public:
circle(const int (&a)[2], int r) {
c[0] = a[0], c[1] = a[1], radius = r;
}
void draw() {
std::cout << “to draw circle” << std::endl; // 根据圆心和半径画圆
}
private:
int c[2]; // 圆心坐标
int radius; // 圆半径
};
shape *ps[2];
int a[2] = { 10, 10 }, b[2] = { 20, 20 }, c[2] = { 30, 30 };
int r = 50;
ps[0] = new triangle(a, b, c); // 动态创建一个三角形对象
ps[1] = new circle(a, r); // 动态创建一个圆对象
for (int i = 0; i < 2; i++)
ps[i]->draw(); // 利用指针实现多态,调用正确的绘制方法
delete ps[0]; // 释放动态创建的三角形对象
delete ps[1]; // 释放动态创建的圆对象
13. 指针常量与常量指针
用const关键字修饰的变量只能进行读操作,而不能更改变量的值。
指针常量是指指针所指向内存地址中的数据是用const修饰的,即内存数据只能读取,而不能更改,但是指针变量的值却可以更改,即可以重新指向新的内存地址。
int i = 3, j = 4;
const int *p = &i; // 定义一个指针常量,并赋初始值为变量i的地址
*p = 1; // 这条语句将发生编译错误,不能修改指针常量指向内存中的值
p = &j; // 可以更改指针常量所指向的内存地址
常量指针则是指指针变量的值是一个常量,即不能重新指向其它的内存地址,但是却可以更改所指向内存地址中的数据。
int* const p = &i; // 定义一个常量指针,不赋初始值为变量i的地址
*p = 1; // 可以更改所指向内存地址中的数据
p = &j; // 这条语句将发生错误,不能再指向其它的地址
如果指针变量名前的关键字即为const,则该指针变量就是一个常量指针,否则它是一个指针常量。另外,也可以这样定义一个指针常量:
int const *p = &i; // 定义一个指针常量
这样,只要const关键字在*之前,指针变量就是一个指针常量,const关键字在*之后,它即为一个常量指针。
当然,如果综合前面两种情况,可以定义一个指向常量的常量指针:
const int* const p = &i; // 指针变量p是一个指向常量的常量指针
这样,指针变量p既不能更改所指向内存地址中的数据,也不能再指向其它的内存地址。
数组名实际上是一个常量指针,即数组名不能再被赋予新的地址了,但是可以以指针的方式是用数组名来更改数组中的元素。数组名是第一个元素的地址。
int a[3] = { 1, 2, 3 };
*a = 2; // 更改第一个元素的值为2
*(a + 1) = 3; // 更改第二个元素的值为3
*(a + 2) = 4; // 更改第三个元素的值为4
容易记住的方法:如果const在p前,则为常量指针,指向不能变,可以变内容,其余情况为指针常量,内容不能变,可以变指向。
14. 一维数组的动态分配与释放
内存的动态分配与释放离不开指针的使用,可以借助指针实现一维数组的动态分配与释放。
int *pa = 0; // 定义一个指针,并置空
pa = new int[3]; // 动态分配可以保存3个int数据的内存空间,并将地址赋予pa
pa[0] = 1, pa[1] = 2, pa[2] = 3; // 通过指针为元素赋值 // 对元素做一些其它操作
delete[] pa; // 释放内存
15. 二维数组的动态分配与释放
二维及二维以上数组的动态分配比一维数组要显得复杂,主要在于要从低到高依次为每一维分配内存空间保存维的相应信息。
// 以下代码片断分配2行3列的二维数组
int **ppa = 0; // 定义一个二级指针保存成功分配二维数组的地址
ppa = new int*[2]; // 分配第一维的大小:行
for (int i = 0; i < 2; i++) // 为每一行分配列元素
ppa[i] = new int[3]; // 可以使用p[行][列]方式访问二维数组中的元素
for (int i = 0; i < 2; i++) // 回收内存
delete[] ppa[i]; // 释放每一行列元素所占内存
delete[] ppa; // 释放行所占内存
16. 使用指针时,容易出现的错误:
1.指针未初始化。
指针的初始化,不是指指针的定义,而是指针变量存储的数值是个无效的数值。比如定义float a;这个a会分配一个地址,但初始值是一个乱七八糟的数据。同样,float *a;也会为a分配一个地址,初始值也是乱七八糟的数据。初始化可以将a = NULL,这样在以后的程序中可以增加if(a == NULL)来判断指针是否有效,否则不行。或者为指针分配或者指定空间。如 float *a = new float;或者float b; float *a = &b;都可以为指针指向一块内存以实现初始化。
说明:指针变量可以有空值,即该指针变量不指向任何变量,可以这样表示:p=NULL;实际上NULL代表整数0,也就是使p指向地址为0的单元。这样可以使指针不指向任何有效的单元。实际上系统已先定义了NULL:#define NULL 0,NULL是一个符号常量。应注意:p的值等于NULL和p未被赋值是两个不同的概念。前者是有值的(值为0),不指向任何变量,后者虽未对p赋值但并不等于p无值,只是他的值是一个无法预料的值,也就是p可能指向某一个未指定的单元。这种情况是很危险的。因此,在引用指针变量之前应对它赋值。
2.指针越界
指针越界是个比较难以捕捉的错误。如果测试不全面,不容易被发现。对于为指针分配的空间大小,程序员一定要时刻注意。
3.指向局部变量的指针
指针是记录某块内存起始地址的变量,要使指针有效,则必须确保这块内存有效。用new分配的内存空间,只要不delete,则一直有效。但是对于指向某个变量地址的指针,程序员必须清楚该变量的作用域。如果离开了变量的作用域,该变量的内存空间就会被系统自动回收,再使用指针时,将会发生错误。这是程序中最容易出现的错误。
4.指针指向的转移
有些初涉C++的程序员,常常会写出这样的程序:
char *pChar = new char;
char chs;
pChar = &chs;
delete pChar;
他们的目的是想将chs内容传递给pChar指针指向的内存。但这样写,将会使pChar先前指向的空间编程垃圾地址,因为地址无法再获取了。俗称野指针。将会导致内存泄漏。而且,在调用delete pChar时,也会发生异常错误。因为不是new的空间是不能使用delete删除的。因为pChar已经转到指向chs这个变量的地址了。
参考文献:
C++中的指针用法汇集 http://developer.51cto.com/art/201104/255962.htm
谭浩强《C++程序设计》
C++中的指针1 2 http://blog.chinaunix.net/uid-9381613-id-85524.html
最后
以上就是时尚鞋子为你收集整理的C/C++指针知识点汇总的全部内容,希望文章能够帮你解决C/C++指针知识点汇总所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复