第七讲 指针与引用 (没咋听懂)
第七讲 指针与引用
一 指 针
1 指针变量的定义与使用
指针的定义格式 格式: 目标数据对象类型 * 指针变量名称; 例一:定义 p 为指向整数的指针:
int * p;
例二:定义 p 为指向结构体类型的指针:struct POINT{ int x, y; }; POINT * p;
多个指针变量的定义 例三:
int * p, * q;
例四:typedef int * PINT; PINT p, q;
取址操作符“&” 获取数据对象的地址,可将结果赋给指针变量 示例:
int n = 10; int * p; p = &n; int * q; q = p;
引领操作符“*” 获取指针所指向的目标数据对象 例一: int m, n = 10; int * q = &n; m = *q; 使得 m 为 10 例二(接上例): *q = 1;使得 n 为 1
指针的意义与作用 a.作为函数通信的一种手段 使用指针作为函数参数,不仅可以提高参数传递效率, 还 可以将该参数作为函数输出集的一员,带回结果 b.作为构造复杂数据结构的手段 使用指针构造数据对象之间的关联,形成复杂数据结构 c.作为动态内存分配和管理的手段 在程序执行期间动态构造数据对象之间的关联 d.作为执行特定程序代码的手段 使用指针指向特定代码段,执行未来才能实现的函数
2 指针与函数
数据交换函数
例:编写程序互换两个整型数据对象的值,要求使用函数实现数据对象值的互换
#include <iostream>
using namespace std;
void Swap( int * x, int * y );
int main()
{
int m = 10, n = 20;
#ifndef NDEBUG
cout << "main (before swapped): m = " << m << "; n = " << n << endl;
#endif
Swap( &m, &n ); /* 调用Swap函数互换目标数据对象的值 */
#ifndef NDEBUG
cout << "main (after swapped): m = " << m << "; n = " << n << endl;
#endif
return 0;
}
void Swap( int * x, int * y )
{
int t;
if( !x || !y ){
cout << "Swap: Parameter(s) illegal." << endl;
exit(1);
}
#ifndef NDEBUG
cout << "Swap (before swapped): *x = " << *x << "; *y = " << *y << endl;
#endif
t = *x;
*x = *y;
*y = t;
#ifndef NDEBUG
cout << "Swap (after swapped): *x = " << *x << "; *y = " << *y << endl;
#endif
}
常量指针与指针常量
常量指针:指向常量的指针 性质:不能通过指针修改目标数据对象的值,但可以改变指针值,使其指向其他地方 示例一:
int n = 10; const int * p = &n;
典型使用场合:作为函数参数,表示函数内部不能修改指针所指向的目标数据对象值 示例二:void PrintObject( const int * p );
指针常量:指针指向的位置不可变化 性质:不可将指针指向其他地方,但可改变指针所指向的目标数据对象值 示例三:int n = 10; int * const p = &n;
指针常量和其他常量一样,必须在定义时初始化 常量指针常量:指向常量的指针常量(指针的双重只读属性) 性质:指针值不可改变,指向的目标数据对象值也不可改变 示例四:const int n = 10; const int * const p = &n;
典型使用场合:主要作为函数参数使用
返回指针的函数
指针类型可以作为函数返回值 函数内部返回某个数据对象的地址 调用函数后将返回值赋值给某个指针 特别说明: 不能返回函数内部定义的局部变量地址 程序示例
int global = 0;
int * ReturnPointer()
{
return &global;
}
3 指针与复合数据类型
指针与数组 数据对象地址的计算指针关系运算 可以测试两个指针是否相等 例一:设 p、 q 为指针,则 p == q 测试两个指针是否指向同一个目 标数据对象 空指针: NULL 指针值 0:表示指针不指向任何地方,表示为 NULL 例二:设 p 为指针,则 p = NULL 表示 p 不指向任何目标数据对象 例三(测试指针 p 是否有意义): if( p != NULL ) 等价于 if( p ) 使用指针前一定要测试其是否有意义!
作为函数参数的指针与数组
指针与数组的可互换性
多维数组参数的传递
指针与结构体 指向结构体的指针 指针作为结构体类型的成员
二 字符串
字符数组
字符数组的定义:与普通数组定义格式相同 示例:
char s[9] = { 'C', 'P', 'P', '-', 'P', 'r', 'o', 'g' ,‘/0’};
字符指针
字符串整体
C标准字符串库:
"cstring"
C++字符串类:
"string"
三 动态存储管理
C格式:
malloc/free
malloc 函数的一般用法 首先定义特定类型的指针变量: char * p; 调用 malloc 函数分配内存:
p = (char *)malloc(11);
参数表示所需要分配的存储空间大小,以字节为单位 例:若要分配能够保存 10 个字符的字符串,分配 11 个字节(字 符串结束标志也要分配空间) 将返回值转换为 char * 类型赋值给原指针,使 p 指向新分配空间 的匿名目标数据对象free 函数的一般用法 传递一个指向动态分配内存的目标数据对象的指针 示例一:
char * p; p = (char *)malloc(11); free(p);
示例二:int * p = ( int * )malloc( 10 * sizeof( int ) ); free( p );
示例二分配能够容纳 10 个整数的连续存储空间,使 p 指向该空间的基地址,最后调用 free 函数释放 p 指向的整个空间 特别说明:有分配就有释放 free 函数释放的是 p 指向的目标数据对象的空间,而不是 p 本身的存储空间 调用 free 函数后, p 指向的空间不再有效,但 p 仍指向它 为保证在释放目标数据对象空间后,不会再次使用 p 访问,建议按照下述格式 书写代码:free( p ); p = NULL;
C++格式:
new/delete
new/new[ ] 操作符
动态创建单个目标数据对象 分配目标对象: int * p; p = new int; *p = 10; 分配目标对象: int * p; p = new( int ); *p = 10; 分配目标对象并初始化: int * p; p = new int(10); // 将 *p 初 始化为 10 分配目标对象并初始化: int * p; p = new(int)(10); 动态创建多个目标数据对象 分配数组目标对象: int * p; p = new int[8]; // 分配 8 个元素 的整数数组
delete/delete[ ] 操作符
释放单个目标数据对象 释放目标对象: int * p; p = new int; *p = 10; delete p; 释放多个目标数据对象 释放数组目标对象: int * p; p = new int[8]; delete[] p; 不是delete p[ ]!
空悬指针问题 所有权的重叠:指针赋值操作导致两个指针数据对象指向同样的目 标数据对象,即两个指针都声称“自己拥有目标数据对象的所有权” 示例:
int *p, *q; q = ( int* )malloc( sizeof(int) ); p = q;
产生原因:如果在程序中通过某个指针释放了目标数据对象,另一 指针并不了解这种情况,它仍指向不再有效的目标数据对象,导致 空悬指针 示例:free( p ); p = NULL; // q 为空悬指针,仍指向原处
解决方案 确保程序中只有惟一一个指针拥有目标数据对象,即只有它负责目标数据对象的存储管理,其它指针只可访问,不可管理;若目标数据对象仍有存在价值,但该指针不再有效,此时应进行所有权移交 在一个函数中,确保最多只有一个指针拥有目标数据对象,其它指针即使存在,也仅能访问,不可管理 如果可能,在分配目标数据对象动态内存的函数中释放内存,如main函数分配的内存在 main 函数中释放退一步,如果上述条件不满足,在分配目标数据对象动态内存的函数的主调函数中释放内存,即将所有权移交给上级函数级级上报,层层审批
四 引 用
1 引用类型
引用的定义 定义格式: 数据类型& 变量名称 = 被引用变量名称; 示例: int a; int & ref = a;
引用的性质 引用类型的变量不占用单独的存储空间 为另一数据对象起个别名,与该对象同享存储空间
特殊说明 引用类型的变量必须在定义时初始化 此关联关系在引用类型变量的整个存续期都保持不变 对引用类型变量的操作就是对被引用变量的操作
2 引用作为函数参数
引用的最大意义:作为函数参数 参数传递机制:引用传递,直接修改实际参数值 使用格式: 返回值类型 函数名称( 类型 & 参数名称); 函数原型示例:
void Swap( int & x, int & y );
函数实现示例:void Swap( int & x, int & y ){
int t; t = x; x = y; y = t; return;
}
函数调用示例:int main(){
int a = 10, b = 20; Swap( a, b ); return 0;
}
常量引用:仅能引用常量,不能通过引用改变目标对象值;引用本身 也不能改变引用对象
引用作为函数返回值时不生成副本 函数原型示例:
int & Inc( int & dest, const int & alpha );
函数实现示例:int & Inc( int & dest, const int & alpha ){
dest += alpha; return dest;
}
函数调用示例:引用类型返回值可以递增int main(){
int a = 10, b = 20, c; Inc( a, b ); c = Inc(a, b)++; return 0;
}