1.37k likes | 1.49k Views
程序设计是计算机学科的. 核心和灵魂. 第六章 复合数据类型. 程序设计基础. 2000H. 0x34. x. 0x12. 6.1 变量与赋值. 变量在内存中占用的存储空间的首地址称为变量的地址. 例: int x=0x1234; 则 x 的地址为 2000H. 根据变量的使用属性,变量分:. 值变量: 在其存储空间中存放的是该变量的值,按变量名存取变量值; 地址变量: 在其存储空间中存放的是地址,使用变量名时操作的是变量的地址;. 指针类型的变量是最常用的地址变量. 6.2 指针类型. 指针 : 一个变量的地址称为该变量的指针.
E N D
程序设计是计算机学科的 核心和灵魂 第六章 复合数据类型 程序设计基础
2000H 0x34 x 0x12 6.1变量与赋值 变量在内存中占用的存储空间的首地址称为变量的地址 例:int x=0x1234; 则 x 的地址为2000H
根据变量的使用属性,变量分: 值变量:在其存储空间中存放的是该变量的值,按变量名存取变量值; 地址变量:在其存储空间中存放的是地址,使用变量名时操作的是变量的地址; 指针类型的变量是最常用的地址变量
6.2指针类型 指针:一个变量的地址称为该变量的指针 例如:int a=1; &a就表示指针 指针变量:若一个变量专用于存放另一个变量的地址(指针),则该变量称为指针变量。 int *b;b表示的是指针变量 (注意,b表示指针变量而不是*b)
直接存取:直接根据变量名存取数据。 间接存取:通过指针变量存取相应变量的数据。 指针的对象:当把变量的地址存入指针变量后,我们就可以说这个指针指向了该变量。 变量的存取方法:直接存取和间接存取。
从以下几个方面介绍指针: 1.指针的定义 2.指针变量的引用 3.指针的地址 4.指针的类型 5.使用指针应注意的问题 6.指针的灵活性 7.二级指针 8. void指针 9.指针与常量 10.指针的运算 11.传址调用 12.引用
1.指针的定义 指针的定义指出了指针的存储类型和数据类型,定义的语法形式如下: 基类型 *指针变量1,…*指针变量n; 标识符命名的指针变量名。 指针标志。 指针指向对象的类型,即该指针指向的内存空间所存储的数据类型 例如: int * p, * q; /* p、q是指向整型变量的指针*/ float x ,y; pf=&x; /* pf和pfValue分别存放x和y的地址*/ pfValue=&y;
在定义指针变量时要注意以下几个问题: ⑴ 变量名前面的“*”不能省略,如果写成 int *pInt1, pInt2; 则pInt2被定义为整型变量,而非整型指针变量。 ⑵定义中的“*”表示其后的变量是指针变量,但千万不要认为“* pInt1 ”是指针变量,指针变量是pInt1而不是* pInt1。 ⑶指针变量只能指向定义时所规定类型的变量。这个规定的类型称为该指针变量的“基类型”。 如:上例中pInt1 、 pInt2只能指向整型变量,不能指向实型或字符型变量。
2.指针的引用 定义了一个指针后,在使用此指针前,必须首先给它赋一个合法的值。否则,程序中对指针的使用就有可能导致系统崩溃。 指针在说明的同时,也可以被赋予初值,称为指针的初始化。由于指针是保存地址的变量,所以初始化时赋予它的初值必须是地址量。
定义指针的目的是通过指针引用内存对象,指针的引用应按如下步骤进行:定义指针的目的是通过指针引用内存对象,指针的引用应按如下步骤进行: ⑴定义指针 int a=0, *p; 例6-1 ⑵指针指向对象 p=&a; ⑶通过指针引用对象 *p=*p+2; 指针操作的两种运算: 取地址运算 & &a表示取变量a地址的运算。 取值运算 * *p表示取指针p指向变量内容的运算 int a,*p; a 2000H 2 p=&a; /* p指向a。 */ *p=2; p 2000H
地址30606 32820 30 …… age age_ptr 30606 • int age = 30; • int* age_ptr; • age_ptr = & age ; • *age_ptr = 50 ; 50 age_ptr &age *age_ptr age *age_ptr = 50 age = 50; age++; (*age_ptr)++;
关于指针引用的说明 1)定义指针变量后,并未确定该变量指向何处。也就是说该变量的值是不确定的。在引用指针变量前必须首先让它指向一个变量,这一点非常重要。 即指针必须指向对象后,才能引用。 int a,*p; int *p; *p=2; /* Error! */ p=&a; /* p指向a。 */ *p=2; int a,*p; p=&a; 则:&*p p *&a a 2)&和 * 为互补运算
3)&只能用于可以求出地址的表达式 p = &45; // p = &i + &j // p = &i + 4; //允许,但比较危险 *只能用于表示地址的变量 int i; int *p = &i; *p = 45; //允许 *i = 34; //
例6-2:指出下面程序的输出结果。 #include <iostream> void main( ) { int value1 =10,value2=20,temp; int *ptr; ptr=&value1; temp=*ptr; //不是乘号,*ptr表示ptr指针指向的地址空间的数据, //即value1中的值10赋给temp *ptr=value2; //将value2即20复制到ptr指针指向的地址空间中, //相当于value1中的值被改为20 value2=temp; cout<<value1<<endl; cout<<value2<<endl; }
例6-3:指出下面程序的运行结果。 #include <iostream.h> void main( ) { int a=10,b=10,*ptr1,*ptr2; ptr1=&a; ptr2=&b; cout<<(*ptr1==*ptr2)<<endl; cout<<(ptr1==ptr2)<<endl; *ptr1=20; b=15; cout<<a<<endl; cout<<*ptr2<<endl; }
3.指针的地址 ...... 指针变量与其他变量一样在定义后就被分配了存储空间,因此指针变量也有内存地址。 p = &i; p = &j; 1234 int * p; 1238 2df4(2df8) 123c ...... ...... 2df0 int i = 156; 2df4 156 2df8 99 int j = 99; ......
例6-4:看下面程序的运行结果,说明原因。 #include <iostream.h> void main( ) { int a,*ptr1=&a,*ptr2=&a; cout<<ptr1<<' '<<ptr2<<endl; cout<<&ptr1<<' '<<&ptr2<<endl; } 运行结果为: 0x0063FDF0 0x0063FDF0 0x0063FDF8 0x0063FDF4
4.指针的类型 • 变量都有数据类型,那么指针变量的数据类型是什么呢? 任何一个指针变量本身数据值的类型都是unsigned long int。 • sizeof(int *) == sizeof(char *) ==sizeof(float *) == sizeof(char **)
5.使用指针应注意的问题 例6-5:指出下面程序中的错误。 (1)#include <iostream.h> (2)void main( ) (3){ (4) int a=10,*ptr1=a,*ptr2,*ptr3; (5) *ptr2=*ptr1; (6) ptr3=&ptr2; (7) a=ptr2; (8)}
7.指针的灵活性 例6-6:多个指针指向同一个变量。 #include<iostream.h> void main() { int *p1,*p2,i1,i2; cin>>i1>>i2; p1=&i1;p2=&i2; cout<<*p1<<","<<*p2<<endl; p2=p1; cout<<*p1<<","<<*p2<<endl; return; }
例6-7:交换指针所指向的变量。 #include<iostream.h> void main() { int *p1,*p2,*p,i1,i2; cin>>i1>>i2; p1=&i1;p2=&i2; cout<<*p1<<","<<*p2<<endl; p=p1;p1=p2;p2=p; cout<<*p1<<","<<*p2<<endl; cout<<i1<<","<<i2<<endl; return; }
例6-8:交换指针所指向的变量的值。 #include<iostream.h> void main() { int *p1,*p2,i1,i2,i; cin>>i1>>i2; p1=&i1;p2=&i2; cout<<i1<<","<<i2<<endl; i=*p1;*p1=*p2;*p2=i; //注意不要写成:i=p1; p1=p2; p2=i; cout<<i1<<","<<i2<<endl; return; }
例6-9:一个有问题的程序。 如果将上面的例6-7改写为: #include<iostream.h> void main() { int *p1,*p2,*p,i1,i2; cin>>i1>>i2; p1=&i1;p2=&i2; cout<<*p1<<”,”<<*p2<<endl; *p=*p1;*p1=*p2;*p2=*p; cout<<*p1<<”,”<<*p2<<endl; return; }
7.二级指针 由于指针是一个变量,在内存中也占据一定的空间,具有一个地址,这个地址也可以利用指针来保存。因此,可以声明一个指针来指向它,这个指针称为指向指针的指针,也即二级指针。声明二级指针的形式如下: 数据类型 **指针变量名 例如: int a,*q=&a; int **p=&q; 上述语句声明了一个二级指针pp,它指向指针p。
例6-10:给出下面程序的运行结果。 #include <iostream.h> #include <string.h> void main( ) { char **p; char a='x',*q=&a; p=&q; cout<<**p<<endl; } 输出结果是: x
例6-11:给出以下程序的运行结果。 #include<iostream.h> void main() { int a; int *p=&a,**pp=&p; //声明指针p指向变量a,指针pp指向指针p a=1; cout<<"a="<<a<<endl; cout<<"*p="<<*p<<endl; //输出指针p所指向的变量a的值 cout<<"p="<<p<<endl; //输出指针p的值,也即变量a的地址值 cout<<“*pp=”<<*pp<<endl; //输出指针pp指向的指针变量p的值 cout<<"**pp="<<**pp<<endl; //输出指针pp所指向的指针变量p所指向的变量a的值 }
8. void指针 void 指针(空指针类型)的特点: (1)void 指针单向兼容其他类型的指针,即可将其他类型的指针变量赋给void指针变量。但反过来则必须进行强制类型转换。 (2)void指针在没有赋值前不能作为右值被引用。 void *p1;int *p2;p1 = p2; void *p1;int *p2;p2 = p1;
例6-12:指出下面程序的错误。 #include <iostream.h> void main( ) { void *p; int a=10, *ptr=&a; *p=a; //错误,p不指向任何地址 ptr=p; //不能将void指针直接赋给其他类型指针 p=ptr; cout<<*(int *)p<<endl; }
9.指针与常量 指针本身存放的地址变, 指针的值也不变 指针所指向的内容不变,即指针的值不变 指针本身不变,即地址不变 const int *ptr=&x; *ptr=4; ptr=&y; int * const ptr=&x; *ptr=4; ptr=&y; const int * const ptr=&x; *ptr=4; ptr=&y; × √ × √ × × (1)关键字const放在指针类型前,就是声明一个指向常量的指针。 (2)关键字const放在“*”号和指针名之间,就是声明一个指针常量(也称常指针) (3)关键字const在上述两个地方都加,则是声明一个指向常量的指针常量,指针本身的值不可改变,它所指向的数据的值也不能通过指针改变。
10.指针的运算 指针的算术运算 指针的算术运算是按C++语言地址计算规则进行的,这种运算与指针指向的数据类型有密切关系,也就是C++语言的地址计算与地址中存放的数据长度有关。 指针是特殊类型的变量,其内容是变量的地址,因此,指针的运算及结果一定要符合地址逻辑。
int a, b, *p1, *p2; p1=&a; p2=&b; (1)指针加1、减1运算: (p1++,++p1,p1--,--p1) /*指向a后(或前)的整型单元*/ (2)指针与整数的加减运算:(p1+n,p1-n) /*含义指向a后(或前)的n个整型单元*/ (3)指针的相减运算: (p2-p1) /*a和b之间差的单元数*/ a 2000H 2004H b 2400H 结果 100H p1 2000H 2004H 2400H p2
⑴指针与整型值加减的结果是指针,表示使该指针指向该指针下移或上移存储单元个数(整型值)之后的内存地址。存储单元的大小就是该指针的数据类型所需的内存大小。 例如:ptr+n (指针ptr,n为整数)这个指针值代表的内存单元的地址是:ptr+n*d(其中d是指针所指向变量的数据类型所占内存字节数),即指针移动了n个元素。 ⑵指针与指针的加运算毫无意义,所以指针与指针无加运算 ⑶指针与指针的减运算要求相减的两个指针属于同一类型,其结果是整数,表示两个指针之间的数据的个数。其结果值的计算公式是: ptr1-ptr2=(ptr1的值- ptr2的值) /指针的数据类型所占的字节数
指针的关系运算 基类型相同的两个指针进行比较运算,用以确定它们所指向数据的前后顺序。其意义是两个指针的位置比较,结果是逻辑值。 p1 == p2 p2 > p1 p2 < p1 如两个指针相等说明它们指向同一个数据。当两个指针指向同一数组时,如果指针的值不同,那么取值大的指针所指的元素位于取值小的指针所指元素后 。
指针的赋值运算 指针赋值运算常见的有以下几种形式。 1)把一个变量的地址赋予一个指向相同数据类型的指针,例如: char a, *p; p=&a; 2)把一个指针的值赋予相同数据类型的另一个指针,例如: int *p, *q; p=q; 3)把数组的地址赋予指向相同数据类型的指针。例如: char a[10], *pa; pa=a; 4)此外,还经常使用下列赋值运算: int *p, *q, n; p=q+n; p=q-n; p+=n; p-=n;
参数传递机制 传值调用 int max ( int x , int y ) { int z ; z=x>y? x : y ; return (z) ; } void main (void) { int a ,b ,c ; cin>>a>>b ; c=max( a ,b ); cout<<c; } z 3 a b 2 2 X y 3 3
int main() { int x =2, y = 3; int temp; temp = x; x = y; y = temp; cout<<“x = ”<<x<<“, y = ”<<y<<endl; } void swap(int p,int q) { int temp = p; p = q; q = temp; } swap(x, y); 传值方式向函数传递参数时,先将实参赋值给形参,然后在被调函数内对形参进行操作。被调函数形参的任何变化都不会引起定义于调用函数中的实参的改变。
11.传址调用 传值调用传的是实参的地址,因此形参和实参指向的是同一块内存地址.即将实参本身传给函数。从而,函数对形参的操作,将直接改变实参的值 void swap(int *p,int *q) { int temp = *p; *p = *q; *q = temp; cout<<“*p=“<<*p<<“, *q = ”<<*q; } int main() { int x =2, y = 3; swap(&x, &y); cout<<“x = ”<<x<<“, y = ”<<y<<endl; }
说明: “地址传送”中的“地址”,指的就是变量的地址。 void swap(int *p,int *q) { int temp = *p; *p = *q; *q = temp; } swap(2, 3); 调用函数时,凡是采用“传址方式”的参数,不能将常数作为该参数。
12.引用 • 引用:给对象取一个别名,主要用于三个用途: • 独立引用 • 作为参数传递 1)独立引用: 一般形式:基类型 &引用名 = 标识符; 为“标识符”所代表的变量声明一个叫“引用名”的别名
num int num=50; int& ref=num; ref+=20; cout<<"num="<<num<<" ref="<<ref<<endl; 70 50 ref • 说明: • 引用变量不另外分配内存空间 • 独立引用在声明时必须初始化,引用的基类型必须与建立联系的变量类型一致。引用名一旦被声明成一个变量的别名,则不能与其他变量相联系了 • 一个引用只能指向一个对象,一个对象则可有多个别名 • 不允许创建引用数组:数组不能用引用生成一个别名,但数组元素可引用 • 空类型void不能引用 num=70 ref=70
2)引用做函数形参 按照引用调用,将实参传递给被调函数,实现时相当于以实参为初值,定义一引用类型变量(实参的别名),形参和实参为同一数据对象的标识。 使用引用调用和传址调用可以实现相同的功能,但引用调用的使用方式更方便、直观,其使用方法示例如下: void Swap(int& p, int& q) { int temp=p; p=q; q=temp; cout<<"p="<<p<<", q="<<q<<endl; } void main() { int x=2, y=3; swap(x, y); cout<<"x="<<x<<", y="<<y<<endl; } 形参的类型为引用 实参是相对应的类型而不必是引用类型
6.3数组 先看一个例子: 例:给出10个儿童的体重,要求计算平均体重,并打印出低于平均体重的数值。 用变量来解决问题: void main() { int w1, w2, w3, w4, w5, w6, w7, w8, w9, w10; int t; cin>>w1>>w2>>&w3>>w4>>w5>>w6>>w7>>w8>>w9>>w10;
t=(w1+w2+w3+w4+w5+w6+w7+w8+w9+w10)/10; if( w1 < t ) cout<< w1 ; if( w2 < t ) cout<< w2 ; if( w3 < t ) cout<< w3 ; if( w4 < t ) cout<< w4 ; if( w5 < t ) cout<< w5 ; if( w6 < t ) cout<< w6 ; if( w7 < t ) cout<< w7 ; if( w8 < t ) cout<< w8 ; if( w9 < t ) cout<< w9 ; if( w10 < t ) cout<< w10 ; } 如果有1万个甚至更多的儿童需要统计怎么办呢?
在日常生活中,往往需要对一组相同类型的数据进行处理,这时,需要很多相同类型的变量来存放这一组数据,为了方便起见,C++引入了数组来解决这种问题。在日常生活中,往往需要对一组相同类型的数据进行处理,这时,需要很多相同类型的变量来存放这一组数据,为了方便起见,C++引入了数组来解决这种问题。 用数组来解决问题: main() { int w[10]; /* 定义 1 个整型数组存放体重 */ int t = 0, i; for( i=0; i<10; i++ ) cin>>w[i] ; for( i=0; i<10; i++ ) t = t + w[i]; t = t/10; for( i=0; i<10; i++ ) if( w[i] < t ) cout<< w[i] ; }
7.二维数组的概念 8.二维数组的定义 9.二维数组初始化 10.二维数组引用 11.二维数组举例 12.多维数组 13.指针与数组 14.指针数组与数组指针 1.一维数组的概念 2.一维数组的定义 3.一维数组初始化 4.一维数组赋值 5.一维数组引用 6.一维数组举例
1.数组的概念 数组是一组有序的、类型相同的数据的集合,这些数据被称为数组的元素.有序表现在数组元素在内存中连续存放。 数组下标
每个数组都有一个名字,我们称之为数组名。 为标识数组中的每个元素,我们需要对它们进行编号,这种编号我们称之为数组元素的下标。 注意:C++语言规定下标从0开始。 由于有了下标,元素在数组中的位置(或排列顺序)就被唯一地确定下来。我们用数组名加上下标就可以准确地访问数组中的某个元素了。 如:w[0]代表数组w中的第一个元素 w[9]代表数组w中的第十个元素 说明:数组名代表数组的起始地址。 数组元素在内存中是连续存储的。
数组必须先说明后使用。说明的目的如下: ⒈说明数组的名字。 ⒉说明数组的类型。 ⒊说明数组的维数。 ⒋确定各下标的变化范围。 编译系统将根据说明,开辟内存单元按特有的顺序和相应的类型为各元素分配内存单元。