680 likes | 846 Views
手机移动设备嵌入式 C++. 第三章. 对 象(续). 回顾. 类对象 构造函数 析构函数 拷贝构造函数. 目标. 指针的概念 指针的使用 指针的运算 指向类成员的指针 指向类静态成员的指针 对象指针、 数组指针、 常指针 等 对象的其它知识. 内存. 0. …. 2000. 2001. 2002. 2003. 2005. …. 指针的概念. 内存中每个字节有一个编号 ----- 地址. 程序中 : int i; float k;. i. 编译或函数调用时为其分配内存单元. k.
E N D
手机移动设备嵌入式C++ 第三章 对 象(续)
回顾 • 类对象 • 构造函数 • 析构函数 • 拷贝构造函数
目标 • 指针的概念 • 指针的使用 • 指针的运算 • 指向类成员的指针 • 指向类静态成员的指针 • 对象指针、数组指针、常指针等 • 对象的其它知识
内存 0 …... 2000 2001 2002 2003 2005 …... 指针的概念 内存中每个字节有一个编号-----地址 程序中: int i; float k; i 编译或函数调用时为其分配内存单元 k 变量是对程序中数据 存储空间的抽象
…... 整型变量i 2000 10 2001 2002 2003 变量i_pointer 2004 2005 变量地址(指针) 指针变量 2006 地址存入 指针变量 指向 …... 变量值 变量 指针的概念 • 指针:一个变量的地址 • 指针变量:专门存放变量地址的变量叫~ 指针 变量的内容 变量的地址 2000 指针变量
*i_pointer i_pointer 10 2000 &i_pointer i …... 整型变量i 2000 10 2001 2002 2003 变量i_pointer 2004 2000 指针变量 2005 2006 …... 指针的概念 含义:取指针所指向变量的内容 单目运算符 优先级: 2 结合性:自右向左 含义: 取变量的地址 单目运算符 优先级: 2 结合性:自右向左 两者关系:互为逆运算 理解 &与*运算符 含义 i_pointer &i &(*i_pointer) i *i_pointer *(&i) i_pointer=&i=&(*i_pointer) i=*i_pointer =*(&i) i_pointer-----指针变量,它的内容是地址量 *i_pointer----指针的目标变量,它的内容是数据 &i_pointer---指针变量占用内存的地址
指针的定义 指针是一个变量,在程序中使用时,必须先声明,后使用。在指针声明的同时也可以进行初始化。 指针的定义 指针的定义指出了指针的存储类型和数据类型,定义的语法形式如下: 存储类型数据类型 *指针变量名 例如: int *p1; static int *p2; char *da; 上面定义了名为p1,p2和da的三个不同类型指针。
指针的定义(续) 具有相同存储类型和数据类型的指针可以在一行中说明,它们也可以和同类型的普通变量在一起说明。 例如: int *p1, *p2, *p3; char m, *da; 在上例中,第一行声明了三个int型指针p1,p2,p3;第二行声明了一个char型变量m和一个指针da。 注意:当在一行中定义多个同一类型的指针时,用逗号隔开各指针变量标识符,并且每个变量前都要加上星号“*”。
指针的使用 指针的使用 定义了一个指针后,在使用此指针前,必须首先给它赋一个合法的值。否则,程序中对指针的使用就有可能导致系统崩溃。可以在定义指针的同时,通过初始化来给指针赋值,也可以在使用之前给指针赋值。下面首先来了解指针的初始化。 由于指针是保持地址的变量,所以初始化时赋予它的初值必须是地址量。指针初始化的一般形式是: 存储类型数据类型 *指针名=初始地址值; 例如: int a,*pa=&a; 将变量a 的内存地址作为初始值赋予int型指针pa。
指针的使用(续) 上面这种写法与下面的写法是等价的: int a; int *pa=&a; 当把一个变量的内存地址作为初始值赋给指针时,该变量必须在指针初始化之前已经说明过。其道理很简单,变量只有在说明之后才被分配一定的内存地址。此外,该变量的数据类型必须与指针的数据类型一致。
指针的使用(续) 下面的例子是把一个指针初始化为空指针。 int *px=0; 这个语句将指针px的值初始化为0。值为0的指针叫作空指针。 为了使用安全起见,一般来说,在定义指针时,最好初始化,哪怕是初始化为空指针。
指针的使用(续) 如果在定义指针时,指针初始化为0或者根本没有初始化。那么在使用此指针前,就必须给它赋有意义的值。 例如: int n,*p1; //定义指针p1时没有初始化 p1=&n; //给指针p1赋值为int型变量n的地址 或者: int n,*p1=0; //定义指针p1时初始化为0 p1=&n; //给指针p1赋值为int型变量n的地址 都是正确的。
指针的使用(续) 也可以向一个指针赋初值为另一个指针变量,即把另一个已经初始化的指针赋予一个指针。这时,这两个指针指向同一变量的内存地址。如下例所示:例如: int n; int *p1=&n; //指针p1的值初始化为变量n的地址 int *p2=p1; //指针p2的值初始化为指针变量p1 上面这种写法与下面的写法是等价的: int n; int *p1=&n; //指针p1的值初始化为变量n的地址 int *p2=&n; //指针p2的值初始化为变量n的地址
指针的使用(续) 也可以定义一个指向数组的指针。由于数组名表示的是该数组的首地址,所以如果定义一个指针指向数组,则可如下例声明: 例如: int a[10],*pa=a; 这个语句定义了一个指针pa,并把pa初始化为指向数组int a[10]的指针,即指针pa指向数组的第一个元素。这时,不需要使用取地址运算符“&”。 上述声明方式与下面的语句等价: int a[10],*pa=&a[0]; “&a[0]”表示数组a中第一个元素所在的内存地址值。
指针的使用(续) 如果说明了一个指针,并使其值为某个变量的地址,则可以通过这个指针间接地访问在这个地址中存储的值。 利用指针来访问变量值需要使用间接访问运算符“*”。 例如: int a=1,*pa=&a; cout<<*pa; 用cout语句输出的是变量a的值1,即“*pa”就是代表变量a。上面的“cout<<*pa;”语句与“cout<<a;”语句的作用相同。
指针的使用(续) 同样,在利用指针访问变量的值时,也可以通过指针给变量间接赋值。 例如: int a=1,*pa=&a; *pa=2; cout<<a; 用cout语句输出的是变量a的值2,而不是1。
说明 经过上面部分的学习已知,在C++语言中有两个有关指针的特别运算符: • & 运算符:为取地址运算符,&x的值为x的地址。 • * 运算符:指针运算符,或指向运算符,也称间接运算符,*p代表p所指向的变量。 • 指针要初始化
px *px &px 说明 由于引进了指针的概念,读者在程序中要注意区分下面三种表示方法所具有的不同意义。例如,有一个指针px , • px ----- 指针变量,它的内容是地址量。 • *px --- 指针的目标变量,它的内容是数据。 • &px -- 指针变量占用的存储区域的地址。 *px和&px的意义
指针运算 指针运算是以指针变量所持有的地址值为运算量进行的运算。因此,指针运算的实质是地址的计算。 由于指针是持有地址量的变量这一特性,指针的运算与普通变量的运算在种类上和意义上都是不同的。指针运算的种类是有限的,它只能进行算术运算、关系运算和赋值运算。
指针的算术运算 指针的算术运算 指针的算术运算是按C++语言地址计算规则进行的,这种运算与指针指向的数据类型有密切关系,也就是C++语言的地址计算与地址中存放的数据长度有关。 设px和py是指向具有相同数据类型的一组若干数据的指针,n是整数,则指针可以进行的算术运算有如下几种: px+n, px-n, px++, ++px, px--, --px, px-py 注意:p1+p2 无意义
加减运算 1. 指针与整数的加减运算:(px+n,px-n) 指针作为地址量加上或减去一个整数n,其意义是指针当前指向位置的前方或后方第n个数据的位置。由于指针可以指向不同数据类型,即数据长度不同的数据,所以这种运算的结果值取决于指针指向的数据类型。
加减运算 由此,对于某种数据类型的指针p来说: p+n的实际操作是:(p)+n*sizeof(数据类型); p-n的实际操作是:(p)-n*sizeof(数据类型); 其中,(p)表示指针p中的地址值,而不是&p,sizeof(数据类型)的长度单位为字节。 在C++语言中,指针加减运算一般用在对数组元素进行操作的场合。通过对指向数组的指针进行加减运算,可以使指针指向数组中不同的元素。此时也必须注意越界问题。
a数组 p a[0] p+1,a+1 a[1] a[2] a[3] a[4] p+i,a+i a[5] a[6] a[7] a[8] p+9,a+9 a[9] 加减运算示例 例 p指向float数,则 p+1 (p)+1 4 例 p指向int型数组,且p=&a[0]; 则p+1 指向a[1] 1 例 int a[10]; int *p=&a[2]; p++; *p=1; 例 int a[10]; int *p1=&a[2]; int *p2=&a[5]; 则:p2-p1=3;
指针的关系运算 指针的关系运算 在两个指向相同类型变量的指针之间可以进行各种关系运算。两指针之间的关系运算表示它们指向的地址位置之间的关系。比如: int a; int *p=&a,*q=p; 若上面声明的两个指针作p==q运算,其结果为1(true),也即指针p、q指向同一个变量。两指针相等的概念是两指针指向同一位置。
指针的关系运算 假设数据在内存中的存储逻辑是由前向后,那么指向后方的指针大于指向前方的指针。对于两指针p和q之间的关系表达式: p<q 若p指向位置在q指向位置的前方,则该表达式的结果值为1,反之为0。 • 指向不同数据类型的指针之间的关系运算是没有意义的, • 指针与非0整数之间的关系运算也是没有意义的。 但是指针可以和零之间进行等于或不等于的关系运算,即: p==0 或 p!=0 它们用于判断指针p是否为一空指针。
指针的关系运算示例 若p1和p2指向同一数组,则 • p1<p2 表示p1指的元素在前 • p1>p2 表示p1指的元素在后 • p1==p2 表示p1与p2指向同一元素
指针的赋值运算 指针的赋值运算 当向指针变量赋值时,赋的值必须是地址常量或变量,不能是普通整数。指针赋值运算常见的有以下几种形式。 • 把一个变量的地址赋予一个指向相同数据类型的指针,例如: char a, *p; p=&a; • 把一个指针的值赋予相同数据类型的另外一个指针,例如: int *p, *q; p=q;
指针的赋值运算 • 把数组的地址赋予指向相同数据类型的指针。例如: char a[10], *pa; pa=a; • 此外,还经常使用下列赋值运算: int *p, *q, n; p=q+n; p=q-n; p+=n; p-=n;
指针的运算示例 #include<iostream.h> int strlen(char a[]); void main(){ char s[]="abcdefg"; char *p; int n=strlen(s); for(p=s+n-1;p+1!=s;p--) cout<<*p; cout<<endl; } int strlen(char a[]) { char *p=a; int i=0; while (a[i++]!='\0') p++; return p-a; }
面向对象元素与指针 指向类成员的指针 : • 指向数据成员的指针: <类型说明符><类名>::*<指针名>
示例 #include <iostream.h> class Test { private: int a; public: Test(int i) {a = i;} int c; int fun(int b){return a * c + b;} }; void main(){ int Test::*pc = &Test::c; Test object1(1); object1.*pc = 8; cout<<"object1.c is"<<object1.c<<endl; }
静态类成员指针 • 静态类成员是属于该类的全局对象和函数。 • 指向静态成员的指针,声明的方式和普通指针完全一样,只是赋值的时候,还得加上类的限定符:
示例 #include<iostream.h> class myclass { public: // 构造函数,每定义一个对象, 静态数据成员i加1 myclass() { ++i; } static int i; // 声明静态数据成员i }; int myclass::i=0; // 静态数据成员i初始化, 不必在前面加static void main() { int *count=&myclass::i; // 声明一个int型指针,指向类的静态成员 myclass ob1,ob2,ob3,ob4; cout<<"myclass::i= "<<*count<<endl; // 通过指针直接访问静态数据成员 }
指向成员函数的指针 • 指向数据成员的指针: <类型说明符>(<类名>::*<指针名>)(<参数表>)
示例 #include <iostream.h> class Test { private: int a; public: Test(int i) {a = i;} int c; int fun(){return a+ c;} }; void main(){ Test object1(1); int (Test::*pfun) (); pfun = &Test::fun; object1.c = 8; int x=1; cout<<"object1.*pfun is"<<(object1.*pfun)()<<endl; }
对象指针 1.用指针访问单个对象成员 声明对象指针的一般语法形式为: 类名* 对象指针名; 当用指向对象的指针来访问对象成员时, 要用“->”操作符。
示例 #include<iostream.h> class exe{ public: void set_a(int a){ x=a; } void show_a(){ cout<<x<<endl; } private: int x; }; int main() { exe ob,*p; // 声明类exe的对象ob和类exe的对象指针p ob.set_a(2); ob.show_a(); // 利用对象名访问对象的成员 p=&ob; // 将对象ob的地址赋给对象指针p p->show_a(); // 利用对象指针访问对象的成员 return 0; }
对象指针 2. 用对象指针访问对象数组 上例的main()改写为: int main() { exe ob[2],*p; ob[0].set_a(10); ob[1].set_a(20); p=ob; p->show_a(); p++; p->show_a(); return 0; }
this指针 • This 指针是指向对象的指针,它隐含在类成员函数中,用来指向成员函数所属类的正在被操作的对象。 • 编译器会把对象的地址赋给成员函数中的this指针。确保访问到的数据成员属于这个对象 Date::Date(int y,int m,int d) { this -> year = y; this -> month = m; this -> day = d; } 注:静态成员函数没有this 指针
this的用法 class person{ private: int age; public: void display(); }; void Person::display(){ this -> age = 25; // 与age = 25一样 cout << this -> age; // 与cout << age一样 cout << endl; }; int main(){ Person Jack; Jack.display(); return 0; }
对象数组 定义:对象数组是指数组元素为对象的数组,该数组中若干个元素必须是同一个类的若干个对象 。 对象数组定义格式如下: <类名><数组名>[<大小>] 比如:Date dates1[7];
示例 class Date { public: Date(int m, int d, int y); void Printf(); private: int m_month, m_day, m_year; }; Date dates[4]={ Date (4,22,2006),Date (4,23,2006), Date (4,24,2006),Date (4,25,2006)}
指针与数组 对于二维数组,又是如何用指针访问其中的每个数组元素的呢? 例如,定义了一个二维数组a: int a[3][5]; a是以一个3*5的二维数组,它有三行,每一行都有其起始地址。 C++语言规定以a[0],a[1],a[2]分别表示第0行,第1行,第2行的起始地址,即该行第0列元素的地址。注意a[0],a[1],a[2]并不是一个元素,而是一行首地址,正如同一维数组名是数组起始地址一样,a[0]的值等于&a[0][0],a[1]的值等于&a[1][0],a[2]的值等于&a[2][0]。在同一行中是同样的类推方式,比如:a[0]+1的值等于&a[0][1],a[1]+2的值等于&a[1][2],……。
数组指针 数组指针与指针数组 在C++语言中,数组指针就是一个指向数组的指针;指针数组就是其元素为指针的数组。在学习C++语言时,要注意对它们进行区分,不能等同起来。 1. 数组指针 数组指针是一个指向一维数组的指针变量,定义数组指针的格式为: 数据类型 (*指针名)[常量表达式];
数组指针 例如: int (*p)[5]; 上面这个语句定义了一个数组指针p,它指向一个包含5个元素的一维数组,数组元素为整型。注意,*p两侧的圆括号不能省略,它表示p先与星号“*”结合,是指针变量。如果省略了圆括号,即写成*p[5]的形式,由于方括号的优先级比星号高,则p先与方括号[ ]结合,是数组类型,那么语句int *p[5];是定义了一个指针数组。
指向对象数组的指针 指向对象数组的指针定义格式如下: <类名>(*PL)[4]; 其中,PL是指向数组的指针名,该指针是一个指向一维对象数组的指针,该数组中有4个元素,每个元素是指定<类名>的对象
指针数组 2. 指针数组 指针数组就是其元素为指针的数组。它是指针的集合,它的每一个元素都是指针变量,并且它们具有相同的存储类型和指向相同的数据类型。 说明指针数组的语法格式为: 数据类型 *指针数组名[常量表达式]; 其中,数据类型式指数组中各元素指针所指向的类型,同一指针数组中各指针元素指向的类型相同;指针数组名也即数组的首地址,是一个标识符;常量表达式指出这个数组中的元素个数。 例如:下面定义了几个指针数组。 int *p1[6]; float *p2[3][4];
void指针 void指针 一般来说,只能用指向相同类型的指针给另一个指针赋值,而在不同类型的指针之间进行赋值是错误的。比如: int a,b; int *p1=&a,*p2=p1; //正确 而: int a; int *p1=&a; double *p2=p1; //错误 上述语句中的两个指针p1,p2指向的类型不同,因此,除非进行强制类型转换,否则它们之间不能相互赋值。但是void指针是一个特例。
void指针 C++语言允许使用空类型(void)指针,即不指定指针指向一个固定的类型,它的定义格式为: void *p; 表示指针变量p不指向一个确定的类型数据,它的作用仅仅是用来存放一个地址。 void指针它可以指向任何类型的C++数据。也就是说,可以用任何类型的指针直接给void指针赋值。不过,如果需要将void指针的值赋给其他类型的指针,则需要进行强制类型转换。比如: int a; int *p1=&a; void *p2=p1; int *p4=(int *)p2;