📄 05章 指针与字符串.txt
字号:
C++大学教程(第5章 指针与字符串-01)
教学目标
●能够使用指针
●能用指针按引用调用向函数传递参数
●了解指针、数组与字符串之间的密切关系
●了解指针在函数中的使用
●能够声明和使用字符串数组
5.1 简介
本章介绍“C++编程语言一个最强大的特性——指针。指针是C++中最难掌握的问题之一。第3章介绍了引用可以用于实现按引用调用。指针使程序可模拟按引用调用,生成与操作动态数据结构,即能够伸缩的数据结构,如链表、队列、堆栈和树。本章介绍基本的指针概念,而且强调了数组、指针与字符串之间的密切关系.并包括一组很好的字符串操作练习。
第6章介绍结构中的指针使用。第9章和第10章介绍如何用指针和引用进行面向对象编程。第15章介绍动态内存管理技术以及生成和使用动态数据结构的例子。
把数组和字符串看成指针是从C语言演变而来的。本书后面会介绍把数组和字符串当作成熟的对象。
5.2 指针变量的声明与初始化
指针变量的值为内存地址。通常变量直接包含特定值,而指针则包含特定值变量的地址。因此可以说,变量名直接(directly)引用数值,而指针间接(indirectly)引用数值(如图5.1)。通过指针引用数值称为间接引用。
指针和任何其他变量一样,应先声明后使用。下列声明:
int *countPtr, count;
声明变量countPtr的类型为int*(即指向整型值的指针),或者说成"countPtr是int的指针"或"countPtr指向整数类型的对象"。变量count声明为整数,而不是整型值的指针。声明中的*只适用于countPtr。
声明为指针的每个变量前面都要加上星号(*)。例如,下列声明:
float *xPtr,*yPtr;
表示xPtr和yPtr都是指向float值的指针。声明中以这种方式使用*时,它表示变量声明为指针。指针可以声明为指向任何数据类型的对象。
常见编程错误5.1
假设对指针的声明会分配到声明中逗号分隔的指针变量名列表中的所有指针变量名,从而将指针声明为非指针。声明为指针的每个变量前面都要加上星号(*)。
编程技巧5.1
尽管不是必需的,但在指针变量名中加上Ptr字样能更清楚地表示这些变量是指针,需要相应的处理。
图5.1 直接和间接引用变量
指针应在声明时或在赋值语句中初始化。指针可以初始化为0、NULL或—个地址。数值为0或NULL的指针不指任何内容。NULL是头文件<iostream.h>(和另外几个标准库头文件)中定义的符号化常量。将指针初始化为NULL等于将指针初始化为0,但C++中优先选择0。指定0时,它变为指针的相应类型。数值0是惟一可以不将整数转换为指针类型而直接赋给指针变量的整数值。5.3节将介绍将变量地址赋给指针。
测试与调试提示5.1
初始化指针以防止其指向未知的或未初始化的内存区。
5.3 指针运算符
&(地址)运算符是个一元运算符,返回操作数的地址。例如,假设声明:
int y = 5;
int *yPtr;
则下列语句:
yPtr = &y;
将变量y的地址赋给指针变量yPtr。变量yPtr“指向”y。图5.2显示了执行上述语句之后的内存示
意图。图中从指针向所指对象画一个箭头.表示“指向关系”。
图5.3显示了指针在内存中的表示,假设整型变量y存放在地址600000,指针变量yPtr存放在
地址500000。地址运算符的操作数应为左值,(即要赋值的项目,如变量名).地址运算符不能用于
常量、不产生引用的表达式和用存储类regtster声明的变量。
”*”运算符通常称为间接运算符(indirection operator)或复引用运算符(dereferencing operator),
返回操作数(即指针)所指对象的同义词、别名或浑名。例如(图5.2再次引用),下列语句:
cout << * yPtr << endl;
指向变量y的值(5),如同下列语句:
cout << y << endl;
图5.2 指针指向内存中整数变量的示意图
这里使用*的方法称为复引用指针(dereferencing a pointer)。注意复引用指针也可以用于赋值语句左边,例如下列语句:
*yPtr = 9;
将数值9赋给图5.3中的y。复引用指针也可用于接收输入值,例如:
cin>> *yPtr;
复引用的指针是个左值。
yptr y
500000 600000 600000 5
图 5.3 指针在内存中的表示
常见编程错误5.2
如果指针没有正确地初始化或没有指定指向内存中的特定地址,则复引用指针可能造成致命的运行时错误,或者意外修改重要数据。虽然运行完程序,但得到的是错误结果。
常见编程错误5.3
复引用非指针是个语法错误。
常见编程错误5.4
复引用0指针通常是个致命的运行时错误。
图5.4的程序演示了指针运算符。本例中通过<<用十六进制整数输出内存地址(十六进制整数见附录“数值系统”)。
可移植性提示 5. 1
输出指针的格式与机器有关,有些系统用十六进制整数,而有些系统用十进制整数。
注意a的地址和aPtr的值在输出中是一致的,说明a的地址实际赋给了指针变量aptr。&和*运算符是互逆的,如果两者同时作用于aPtr,则打印相同的结果。图5.5显示了前面所介绍的运算符的优先级和结合律。
1 // Fig. 5.4: fig05_04.cpp
2 // Using the & and * operators
3 #include <iostream.h>
4
5 int main()
6{
7 int a; // a is an integer
8 iht *aPtr; // aPtr is a pointer to an integer
9
10 a = 7;
11 aPtr = &a; // aPtr set to address of a
12
13 cout << "The address of a is "<< &a
14 << "\nThe value of aPtr is" << aPtr;
15
16 cout << "\n\nThe value of a is "<<a
17 << "\nThe value of *aPtr is" << *aPtr;
18
19 cout << "\n\nShowing that * and & are inverses of"
20 << "each other.\n&*aPtr =" << &*aPtr
21 << "\n*&aPtr =" << *&aPtr << endl;
22 return 0;
23 }
输出结果:
The address of a is Ox0064FDF4
The value of aPtr is 0x0064FDF4
The value of a is 7
The value of *aPtr is 7
Showing that * and & are inverses of each other.
&*aPtr = 0x0064FDF4
*&aPtr = 0x0064FDF4
图 5.4 &与*指针运算符
------------------------------------------------------------------------------
运算符 结合律 类型
------------------------------------------------------------------------------
() [] 从左向右 括号
++ -- + - static_cast<type>() 从右向左 一元
& *
* / % 从左向右 乘
+ - 从左向右 加
<< >> 从左向右 插入/读取
< <= > >= 从左向右 关系
== != 从左向右 相等
&& 从左向右 逻辑AND
|| 从左向右 逻辑或
?: 从右向左 条件
= += -= *= /= %= 从右向左 赋值
, 从左向右 逗号
------------------------------------------------------------------------------
图 5.5 运算符的优先级和结合律
5.4 按引用调用函数
C++用三种方式向函数传递数值:按值调用(call-by-value)、用引用参数按引用调用(call-by-reference reference argument)和用指针参数按引用调用(call-by-reference pointer argument)。第3章比较了按引用调用与按值调用,本章主要介绍用指针参数按引用调用。
第3章曾介绍过,return可以从被调用函数向调用者返回一个值(或不返回值而从被调用函数返回控制)。我们还介绍了用引用参数将参数传递给函数,使函数可以修改参数的原有值(这样可以从函数“返回”多个值),或将大的数据对象传递给函数而避免按值调用传递对象的开销(即复制对象所需的开销)。指针和引用一样,也可以修改调用者的一个或几个变量,或将大的数据对象指针传递给函数而避免按值调用传递对象的开销。
在C++中,程序员可以用指针和间接运算符模拟按引用调用(就像C语言程序中的按引用调用一样)。调用函数并要修改参数时,传递该参数地址,通常在要修改数值的变量名前面加上地址运算符(&)。第4章曾介绍过,数组不能用地址运算符(&)传递,因为数组名是内存中数组的开始位置(数组名等同于&arrayName[0]),即数组名已经是个指针。向函数传递参数地址时,可以在函数中使用间接运算符形成变量名的同义词、别名或浑名,并可用其修改调用者内存中该地址的
值(如果变量不用const声明)。
图5.6和5.7的程序是计算整数立方函数的两个版本cubeByValue和cubeByReference。图5.6按值调用将变量number传递给函数cubeByValue。函数cubeByValue求出参数的立方,并将新值用return语句返回main,井在main中将新值赋给number。可以先检查函数调用的结果再修改变量值。例如,在这个程序中,可以将cubeByValue的结果存放在另一变量中,检查其数值,然后再将新值赋给number。
1 // Fig. 5,6: fig0506,cpp
2 // Cube a variable using call-by-value
3 #include <iostream.h>
4
5 int cubeByValue( int ); // prototype
6
7 int main()
8 {
9 int number = 5;
10
11 cout << "The original value of number is "<< number;
12 number = cubeByValue( number );
13 cout << "\nThe new value of number is" << number << endl;
14 return 0;
15 }
16
17 int cubeByValue( int n )
{
18
19 return n * n * n; // cube local variable n
2O }
输出结果:
The original value of number is 5
The new value of number is 125
图5.6 按值调用求出参数的立方
图5.7的程序按引用调用传递变量nunber(传递number的地址)到函数cubeByReference。函
数cubeByReference取nPtr(int的指针)作为参数。函数复引用指针并求出nPtr所指值的立方,从
而改变main中的number值。图5.8和5.9分别分析了图5.6和1.7所示程序。
1 // Fig. 5.7: fig05_07.cpp
2 // Cube a variable using call-by-reference
3 // with a pointer argument
4 #include <iostream.h>
5
6 void cubeByReference( int* ); // prototype
7
8 int main()
9 {
10 int number = 5;
11
12 cout << "The original value of number is "<< number;
13 cubeByReference( &number );
14 cout << "\nThe new value of number is "<< number << endl;
15 return O;
16 }
17
18 void cubeByReference( int *nPtr )
19 {
20 *nPtr = *nPtr = *nptr * *nptr; // cube number in main
21 }
输出结果:
The original value of number is 5
The new value of number is 125
图 5.7 用指针参数按引用调用求出参数的立方
常见编程错误5.5
要复引用指针以取得指针所指的值时不复引用指针是个错误。
接收地址参数的函数要定义接收地址的指针参数。例如,cubeByReference的函数首部如下所示:
void cubeByReference(int *nPtr)
这个函数首部指定函数cubeByReferenee接收整型变量的地址(即整型指针)作为参数,在nPtr中局部存放地址,不返回值。
cubeByReference的函数原型包含括号中的int*。和其他变量类型一样,不需要在函数原型中包括指针名。参数名仅用于程序中的说明,编译器将其忽略。
在需要单下标数组参数的函数首部和函数原型中,可以用cubeByReference参数表中的指针符号。编译器并不区分接收指针的函数和接收单下标数组的函数。当然,函数必须“知道”何时接收数组或要进行按引用调用的单个变量。编译器遇到形如int b[]的单下标数组函数参数时,编译器将参数变为指针符号int* const b(b是指向整数的常量指针),const见第51节介绍。声明函数参数为单下标数组的两种形式可以互换。
编程技巧5.2
除非调用者显式要求被调用函数修改调用者环境中参数变量的值,否则按值调用将参数传递给函数。这是最低权限原则的另一个例子。
图 5.8典型的按值调用分析
图 5.9典型的用指针参数按引用调用分析
5.5 指针与常量限定符
const限定符可以使程序员告诉编译器特定变量的值不能修改。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -