【C++】Points
《C++程序设计基础教程》——刘厚泉,李政伟,二零一三年九月版,学习笔记
文章目录
- 1、指针的定义与初始化
- 1.1、指针的定义
- 1.2、指针的初始化
- 2、指针的使用
- 2.1、指针运算符
- 2.2、指针变量的运算
- 3、指针与数组
- 3.1、指针与一维数组
- 3.2、指针与二维数组
- 4、指针与字符串
- 5、指针与函数
- 5.1、指针作为函数参数
- 5.2、指针作为函数返回值
- 6、引用
- 6.1、引用的定义
- 6.2、引用的使用
- 7、应用实例
1、指针的定义与初始化
指针就是内存中的地址,它可能是变量的地址,也可能是函数的入口地址
变量可以理解为一种“直接访问”方式
指针则是提供了一种“间接访问”方式
1.1、指针的定义
指针是一种变量类型,它存储了另一个变量的内存地址。
指针变量所指向的那个空间为目标变量。
指针的定义通常使用星号(*)符号。
数据类型 *指针变量名;
编译器不允许指针变量里存放普通的数值和字符。
定义指针的时候给出的数据类型,不是指针变量的类型,而是指针所指向的空间里存放的数据的类型,即目标变量的类型。
32 位系统为例,每个指针变量在内存中占有4个字节的空间。
#include <iostream>
using namespace std;
int main()
{
int a = 3;
int *p = &a; // 定义指针变量 p,指向已存在的变量a
cout << p << endl; // a 变量的地址
cout << &a << endl; // a 变量的地址
cout << *p << endl; // a 变量的值
return 0;
}
output
0x75d81ff694
0x75d81ff694
3
1.2、指针的初始化
定义的时候必须初始化,初始化必须是个地址,地址必须合法,目标变量的类型与指针变量定义时的类型必须相符;
注意事项:
- 避免未初始化的指针:未初始化的指针可能导致程序崩溃或未定义行为。
- 释放动态分配的内存:使用 new 分配的内存必须在不再需要时使用 delete 释放,以避免内存泄漏。
- 指针运算:指针可以进行一些基本的算术运算,如指针加法和减法,但这些操作通常用于数组和动态内存管理。
#include <iostream>
using namespace std;
int main() {
int value = 10;
int *ptr = &value; // 定义并初始化指针
cout << "Value: " << value << endl;
cout << "Address of value: " << &value << endl;
cout << "Value pointed by ptr: " << *ptr << endl;
cout << "Address pointed by ptr: " << ptr << endl;
// 修改指针指向的值
*ptr = 20;
cout << "New value: " << value << endl;
// 动态分配内存
int *dynamicPtr = new int;
*dynamicPtr = 30;
cout << "Value pointed by dynamicPtr: " << *dynamicPtr << endl;
// 释放动态分配的内存
delete dynamicPtr;
// 指向空(nullptr)
int *nullPtr = nullptr;
if (nullPtr == nullptr) {
cout << "nullPtr is pointing to nullptr" << endl;
}
// 指向数组的指针
int arr[] = {1, 2, 3, 4, 5};
int *ptrarr = arr; // ptrarr 指向数组的第一个元素
for(int i=0; i<5; i++)
{
cout << *(ptrarr+i) << " ";
}
return 0;
}
outpu
Value: 10
Address of value: 0x670edffb44
Value pointed by ptr: 10
Address pointed by ptr: 0x670edffb44
New value: 20
Value pointed by dynamicPtr: 30
nullPtr is pointing to nullptr
1 2 3 4 5
2、指针的使用
2.1、指针运算符
(1)取地址运算符 (&)
功能:获取变量的内存地址。
示例:int a = 5; int *ptr = &a;
在此例中,&a 返回变量 a 的内存地址,该地址随后被赋给指针 ptr。
(2)解引用(取内容、取值)运算符 (*)
功能:访问指针所指向的内存地址中的数据。
示例:int a = 5; int *ptr = &a; int b = *ptr;
在这里,*ptr
访问了 ptr 指向的内存地址(即 a 的地址)中的数据,即 5,然后该值被赋给变量 b。
#include <iostream>
using namespace std;
int main() {
int a = 3, b=13;
int *ptr = &a;
cout << "a的地址:" << ptr << " " << &a << endl;
cout << "ptr的地址:" << &ptr << endl;
cout << "a的值:" << *ptr << " " << a << endl;
a = 4;
cout << "a修改后的值:" << *ptr << " " << a << endl;
*ptr = 5;
cout << "a修改后的值:" << *ptr << " " << a << endl;
ptr = &b;
cout << "b的地址:" << ptr << " " << &b << endl;
cout << "ptr的地址:" << &ptr << endl;
cout << "b的值:" << *ptr << " " << b << endl;
return 0;
}
output
a的地址:0xa859bffb9c 0xa859bffb9c
ptr的地址:0xa859bffb90
a的值:3 3
a修改后的值:4 4
a修改后的值:5 5
b的地址:0xa859bffb98 0xa859bffb98
ptr的地址:0xa859bffb90
b的值:13 13
访问变量 a 的值有两种方式,一种是直接访问 cout <<a;
一种是间接访问 cout<<*ptr;
2.2、指针变量的运算
(1)赋值运算
int a[] = {1,2,3,4,5};
int *p = a;
int *q = &a[0];
指针也是可以相互赋值的
#include <iostream>
using namespace std;
int main() {
int a =5;
int *p = &a;
int *q = p;
cout << *q << endl;
return 0;
}
output
5
注意 *p
出现在赋值 =
左边和右边的意义是不同的
右边取值,左边表示把赋值号右边的值赋值到新地址空间里
(2)指针算术运算符
包括加法 (+) 和减法 (-)。
功能:在指针所指向的数组元素间进行位置移动。
示例:假设 int arr[] = {1, 2, 3, 4, 5}; int *ptr = arr;
,则 ptr + 1 将指针移动到数组的下一个元素,即 2 的位置。同样,ptr - 1 会将指针移回前一个元素,即 1 的位置。但需注意,指针算术是基于指针类型的大小来计算的。
两个指针变量相减,有意义,相加没有意义
#include <iostream>
using namespace std;
int main() {
int a[10] = {0};
int *p = &a[1];
int *q = &a[7];
cout << q-p << endl;
return 0;
}
output
6
(3)指针比较运算符
包括等于 (==)、不等于 (!=)、大于 (>)、小于 (<)、大于等于 (>=) 和小于等于 (<=)。
功能:比较两个指针是否指向相同的内存地址或它们之间的相对位置关系。
示例:int arr1[] = {1, 2}; int arr2[] = {3, 4}; int *ptr1 = arr1; int *ptr2 = arr2;
在此情境下,ptr1 != ptr2 将返回 true,因为 ptr1 和 ptr2 指向不同的内存地址。
(4)成员访问运算符 (->)
功能:通过指针访问对象的成员变量或成员函数。
示例:假设有一个结构体 struct MyStruct { int x; }; MyStruct obj; MyStruct *ptr = &obj;
,则 ptr->x
可以用来访问 obj 的成员变量 x。
(5)空指针常量 (nullptr)
尽管 nullptr
本身不是一个运算符,但它是一个字面量,用于表示空指针。在C++11及更高版本中推荐使用 nullptr 替代 NULL 或 0,以提高代码的可读性和类型安全性。
3、指针与数组
3.1、指针与一维数组
(1)数组名作为指针
在大多数情况下,数组名会被编译器解释为指向数组第一个元素的指针。例如,如果有一个数组 int arr[10];
,那么 arr 实际上是指向 arr[0] 的指针。
(2)数组访问
你可以使用下标来访问数组元素,如 arr[i]
。
你也可以使用指针算术来访问数组元素,如 *(arr + i)
。
(3)数组名与指针的区别
类型不同:
数组名表示的是整个数组的类型,而指针表示的是指向某种类型的指针。例如,int arr[10];
中 arr 的类型是 int[10],而 int* ptr = arr; 中 ptr 的类型是 int*。
常量性:
数组名是一个常量表达式,不能被修改(即你不能让数组名指向另一个地址)。例如,arr = anotherArray; 是非法的。
指针可以被修改以指向不同的地址。
#include <iostream>
using namespace std;
int main() {
char a[10] = "";
for(int i=0; i<10; i++)
*(a+i) = 97+i;
char *p = a;
for(int i=0; i<10; i++)
cout << p[i];
cout << endl;
for(int i=0; i<10; i++)
cout << *(p+i);
cout << endl;
for(int i=0; i<10; i++)
cout << a[i];
cout << endl;
for(int i=0; i<10; i++)
cout << *(a+i);
cout << endl;
return 0;
}
output
abcdefghij
abcdefghij
abcdefghij
abcdefghij
eg 利用指针操作数组里面的元素
#include <iostream>
using namespace std;
int main() {
int a[10] = {0};
int *p = a;
int *q = &a[9];
for(int i=0; p<=q; p++)
*p = i++;
cout << p << " " << &a[9] << endl; // p 指向了数组后面一个内存单元
cout << *p << endl; // 此时 p 已经是野指针了
cout << *(p-1) << endl;
p = a; // 重新赋值下
for(int i=0; i<10; i++)
cout << p[i]<< " ";
for(int j=0; p<q; p++, q--) // 实现数组元素交换顺序
{
int tmpt;
tmpt = *p;
*p = *q;
*q = tmpt;
}
cout << endl;
for(int i=0; i<10; i++)
cout << *(a+i)<< " ";
return 0;
}
output
0xa763fffae8 0xa763fffae4
-668723975
9
0 1 2 3 4 5 6 7 8 9
9 8 7 6 5 4 3 2 1 0
3.2、指针与二维数组
二维数组本质上是一个数组的数组,即每个元素本身也是一个数组。当使用指针处理二维数组时,情况会变得稍微复杂一些,因为你需要处理两个级别的指针:一个是指向数组(即行)的指针,另一个是指向数组内元素(即列)的指针。
(1)指向数组元素的指针
#include <iostream>
using namespace std;
int main() {
int a[3][4] = {{1,2,3,4}, {5,6,7,8},{9,10,11,12}};
int *p = &a[2][2];
for(int i=0; i<3; i++)
for(int j=0; j<4; j++)
cout << a[i][j]<<" ";
cout << endl;
for(; p<=&a[2][3]; p++)
cout << *p <<" ";
cout << endl;
p = &a[2][2];
for(; p>=&a[0][0]; p--)
cout << *p <<" ";
return 0;
}
output
1 2 3 4 5 6 7 8 9 10 11 12
11 12
11 10 9 8 7 6 5 4 3 2 1
(2)指向一维数组的指针
指向一维数组的指针简称数组指针
数据类型 (*指针变量名)[数组长度]
int a[3][4];
int (*p)[4] = a;
访问数组的方式有直接访问 a[i][j]
,也可以间接访问 *(*(p+i)+j)
或者 *(*(a+i)+j)
+i
行指针、+j
列指针
eg 6-4 求矩阵的转置
#include <iostream>
#include<iomanip>
using namespace std;
int main() {
int a[3][3] = {{1,2,3}, {4,5,6},{7,8,9}};
//int (*p)[3] = &a[0];
int (*p)[3];
p = a;
for(int i=0; i<3; i++)
{
for(int j=i; j<3; j++)
{
int tmp;
tmp = *(*(p+i)+j);
*(*(p+i)+j) = *(*(p+j)+i);
*(*(p+j)+i) = tmp;
}
}
for(int i=0; i<3; i++){
for(int j=0; j<3; j++)
cout << setw(5) << a[i][j] << " ";
cout << endl;
}
return 0;
}
output
1 4 7
2 5 8
3 6 9
注意这里的 for(int j=i; j<3; j++)
的初始值,第 i 次循环,第 i 行和第 i 列 完成交换
行指针可以定义为 int (*p)[3] = &a[0];
,也可以定义为 int (*p)[3]; p = a;
上面的例子换成指向数组元素的指针实现
#include <iostream>
#include<iomanip>
using namespace std;
int main() {
int a[3][3] = {{1,2,3}, {4,5,6},{7,8,9}};
int *p = &a[0][0];
for(int i=0; i<3; i++)
{
for(int j=i; j<3; j++)
{
int tmp;
tmp = *(p + (i*3+j));
*(p + (i*3+j)) = *(p + (j*3+i));
*(p + (j*3+i)) = tmp;
}
}
for(int i=0; i<3; i++){
for(int j=0; j<3; j++)
cout << setw(5) << a[i][j] << " ";
cout << endl;
}
return 0;
}
output
1 4 7
2 5 8
3 6 9
4、指针与字符串
字符串是个常量指针,像数组一样
C风格字符串是以空字符(‘\0’)结尾的字符数组。
在C++中,它们通常通过字符指针来操作。
#include <iostream>
using namespace std;
int main() {
char arr[] = "hello world!";
char *s1 = arr;
const char *s2 = "hello world!"; // 不要 const 就是变量,编译系统会有警告 or 报错
for(int i=0; i<12; i++)
cout << *(s1+i) << " ";
cout << endl;
for(int i=0; i<12; i++)
cout << *(s2+i) << " ";
cout << endl;
while(*s2 != '\0')
{
cout << *s2 << " ";
s2++;
}
return 0;
}
字符串是个常量指针
所以 const char *s2 = "hello world!";
这里的 const 很有必要,不然会 warning 或者报错
output
h e l l o w o r l d !
h e l l o w o r l d !
h e l l o w o r l d !
#include <iostream>
using namespace std;
int main() {
const char *string = "I love China!";
cout << *string << endl;
cout << string << endl;
cout << string + 7 << endl;
int a[] = {1,2,3,4,5,6};
cout << *a << endl;
cout << a << endl;
cout << a+3 << endl;
return 0;
}
字符串是个常量指针,字符串可以直接 cout
(因为存在 ‘\0’ 结束符),不用循环打印
output
I
I love China!
China!
1
0x59227ff770
0x59227ff77c
5、指针与函数
当函数调用时的实参是一个指针时,要求形参必须定义为与其类型相同的指针
5.1、指针作为函数参数
指针作为参数的好处在于,避免函数里重新开辟大量临时空间,因此往往将一些占用空间较大的变量通过指针传递到函数里
eg:建立表格,4个学生,根据学号索引,5 门成绩,要求求出每个学生的平均分以及每门课程所有学生的平均分,根据学号打印学生的成绩
#include <iostream>
#include<iomanip>
using namespace std;
#define row 5
#define col 7
void infoinput(float (*p)[col])
{
for(int i=0; i<row-1; i++)
{
cout << "第" << i+1 << "个学生:";
for(int j=0; j<col-1; j++)
cin >> *(*(p+i)+j);
}
}
void avgstud(float *p)
{
float sum = 0;
int i = 0;
for(i=1; i<col-1; i++) // 第一位为学号
sum += *(p+i);
*(p+i) = sum / (col-2);
}
void avgcour(float (*p)[col], int cour)
{
float sum = 0;
int i;
for(i=0; i<row-1; i++)
sum += *(*(p+i) + cour);
*(*(p+i) + cour) = sum / (row-1);
}
void infoout(float (*p)[col], int num)
{
cout << "学号为" << num << "的学生得分为:";
for(int i=0; i<row-1; i++)
{
if(*(*(p+i) + 0) == num) // 1 == 1.0 是 ture 的
{
for(int j=1; j<col; j++)
cout << *(*(p+i)+j) << " ";
}
}
cout << endl;
}
void printinfo(float (*p)[col])
{
cout << "当前信息表如下:"<< endl;
for(int i=0; i < row; i++)
{
for(int j=0; j < col; j++)
cout << setw(10) << *(*(p+i) + j) << setw(10);
cout << endl;
}
cout << endl;
}
int main()
{
float info[row][col] = {0.0};
float (*p)[col] = &info[0];
// 输入学生信息
infoinput(p);
printinfo(p);
// 计算每个学生所有课程的平均分
for(int i=0; i<row-1; i++)
avgstud(p[i]);
printinfo(p);
// 计算每门课程所有学生的平均分
for(int i=1; i<col; i++)
avgcour(p, i);
printinfo(p);
int num;
cout << "请输入需要查询的学号:";
cin >> num;
infoout(p, num);
return 0;
}
/*
1001 67 78 69 89 75
1002 77 76 89 69 66
1003 89 88 78 75 67
1004 78 67 77 80 69
*/
注意一维和二维数组传递操作时候的差异
output
第1个学生:1001 67 78 69 89 75
第2个学生:1002 77 76 89 69 66
第3个学生:1003 89 88 78 75 67
第4个学生:1004 78 67 77 80 69
当前信息表如下:
1001 67 78 69 89 75 0
1002 77 76 89 69 66 0
1003 89 88 78 75 67 0
1004 78 67 77 80 69 0
0 0 0 0 0 0 0
当前信息表如下:
1001 67 78 69 89 75 75.6
1002 77 76 89 69 66 75.4
1003 89 88 78 75 67 79.4
1004 78 67 77 80 69 74.2
0 0 0 0 0 0 0
当前信息表如下:
1001 67 78 69 89 75 75.6
1002 77 76 89 69 66 75.4
1003 89 88 78 75 67 79.4
1004 78 67 77 80 69 74.2
0 77.75 77.25 78.25 78.25 69.25 76.15
请输入需要查询的学号:1004
学号为1004的学生得分为:78 67 77 80 69 74.2
上面用指针来实现函数参数传递,下面替换成数组来传递
#include <iostream>
#include<iomanip>
using namespace std;
#define row 5
#define col 7
void infoinput(float p[][col])
{
for(int i=0; i<row-1; i++)
{
cout << "第" << i+1 << "个学生:";
for(int j=0; j<col-1; j++)
cin >> p[i][j];
}
}
void avgstud(float p[])
{
float sum = 0;
int i = 0;
for(i=1; i<col-1; i++) // 第一位为学号
sum += p[i];
p[i] = sum / (col-2);
}
void avgcour(float p[][col], int cour)
{
float sum = 0;
int i;
for(i=0; i<row-1; i++)
sum += p[i][cour];
p[i][cour] = sum / (row-1);
}
void infoout(float p[][col], int num)
{
cout << "学号为" << num << "的学生得分为:";
for(int i=0; i<row-1; i++)
{
if(p[i][0] == num) // 1 == 1.0 是 ture 的
{
for(int j=1; j<col; j++)
cout << p[i][j] << " ";
}
}
cout << endl;
}
void printinfo(float p[][col])
{
cout << "当前信息表如下:"<< endl;
for(int i=0; i < row; i++)
{
for(int j=0; j < col; j++)
cout << setw(10) << p[i][j] << setw(10);
cout << endl;
}
cout << endl;
}
int main()
{
float info[row][col] = {0.0};
// 输入学生信息
infoinput(info);
printinfo(info);
// 计算每个学生所有课程的平均分
for(int i=0; i<row-1; i++)
avgstud(info[i]);
printinfo(info);
// 计算每门课程所有学生的平均分
for(int i=1; i<col; i++)
avgcour(info, i);
printinfo(info);
int num;
cout << "请输入需要查询的学号:";
cin >> num;
infoout(info, num);
return 0;
}
注意一维和二维数组传递操作时候的差异
output
第1个学生:1001 67 78 69 89 75
第2个学生:1002 77 76 89 69 66
第3个学生:1003 89 88 78 75 67
第4个学生:1004 78 67 77 80 69
当前信息表如下:
1001 67 78 69 89 75 0
1002 77 76 89 69 66 0
1003 89 88 78 75 67 0
1004 78 67 77 80 69 0
0 0 0 0 0 0 0
当前信息表如下:
1001 67 78 69 89 75 75.6
1002 77 76 89 69 66 75.4
1003 89 88 78 75 67 79.4
1004 78 67 77 80 69 74.2
0 0 0 0 0 0 0
当前信息表如下:
1001 67 78 69 89 75 75.6
1002 77 76 89 69 66 75.4
1003 89 88 78 75 67 79.4
1004 78 67 77 80 69 74.2
0 77.75 77.25 78.25 78.25 69.25 76.15
请输入需要查询的学号:1004
学号为1004的学生得分为:78 67 77 80 69 74.2
eg 6-8 将3个元素按照从小到大排序,利用指针实现
#include <iostream>
using namespace std;
void exc(int *x, int *y)
{
if(*x > *y)
{
int z;
z = *x;
*x = *y;
*y = z;
}
}
int main()
{
int a=9, b=3, c=6;
int *i = &a;
int *j = &b;
int *k = &c;
exc(i,j);
exc(j,k);
exc(i,k);
cout << a << " " << b << " " << c << endl;
return 0;
}
output
3 6 9
5.2、指针作为函数返回值
节省临时拷贝变量的空间,可以采用指针作为返回值
eg:输入两个字符串,返回字符串 s1 中第一次出现字符串 s2的头字符位置
#include <iostream>
using namespace std;
char *subchar(char *s1, char *s2)
{
int len1=0, len2=0;
char *p1 = s1;
char *p2 = s2;
while(*p1 != '\0') // 求字符串长度
{
len1 ++;
p1++;
}
cout << len1 << endl;
while(*p2 != '\0') // 求字符串长度
{
len2 ++;
p2++;
}
cout << len2 << endl;
p1 = s1;
p2 = s2;
int match = 0;
for(int i=0; i<len1; i++) // 遍历字符串 s1
{
if(*p1 == *p2) // 如果字符相等,s2 指针后移一个字符
{
p2++;
}
else // 如果不相等,重置字符串 s2
{
p2 = s2;
}
p1++; // s1 每一次比较都后移一次
if(*p2 =='\0') // 如果 s2 指针遍历结束了,说明 s1 中有对应的子串,输出结果
return p1-len2;
}
return nullptr;
}
int main()
{
char s1[] = "alsdufioabcaeoiuiojadl";
char s2[] = "abc";
char *p1 = s1;
char *p2 = s2;
char *result = subchar(p1, p2);
cout << result - s1 + 1; // 首位置为 1
return 0;
}
output
22
3
9
指针作为函数返回值的时候注意事项
- 接收函数返回值的指针必须与返回值类型相符;
- 返回的地址必须是一个合法的地址,即实际存在的空间首地址;
6、引用
引用(Reference)是一种复合类型,它允许你为一个已存在的对象创建一个新的名字。
引用就是给变量取别名,因此引用是变量的同义词,是变量本身。
对引用的任何操作实际上就是对其所代表的变量的改动。
引用在函数参数传递、返回值以及操作复杂数据结构时非常有用。
6.1、引用的定义
引用运算符 &
数据类型 &引用名 = 变量名;
eg
#include <iostream>
using namespace std;
int main()
{
int a = 10;
int &ref = a; // ref 是 a 的引用
ref = 20;
cout << a << endl;
return 0;
}
output
20
引用在创建时必须被初始化,并且一旦被初始化后,它就不能再改变指向另一个对象。
6.2、引用的使用
1)单独使用
2)作为函数参数使用
3)作为函数返回值使用
eg 独立引用
-
引用型变量在定义的时候必须初始化
-
被引用的对象必须已经分配了空间,即不能为空或空指针
-
被引用的对象不能为地址,即指针变量、数组变量等不能被引用
eg
int a[] = {10};
int &b = a;
会报错
eg
int a[] = {10};
int *p = a;
int &q = p;
会报错
eg 6-10 引用变量的单独使用
#include <iostream>
using namespace std;
int main()
{
int a = 10;
int &b = a;
cout << "被引变量的地址:" << &a << endl;
cout << "引用型变量的地址:" << &b << endl;
b = 20;
cout << "b赋值20后,a的值为:" << a << endl;
a = 30;
cout << "a赋值30后,b的值为:" << b << endl;
return 0;
}
output
被引变量的地址:0xc0be9ffab4
引用型变量的地址:0xc0be9ffab4
b赋值20后,a的值为:20
a赋值30后,b的值为:30
引用变量在函数中使用时可以实现调用函数与被调函数间数据的“双向传值”
引用函数的返回值作为引用,则返回的不仅是一个值,还可以将函数调用结果当作“变量”来进行使用
eg
#include <iostream>
using namespace std;
int& findMax(int &a, int &b) {
return (a > b) ? a : b;
}
int main() {
int x = 5, y = 10;
int &maxRef = findMax(x, y);
cout << maxRef << endl; // 输出 10
maxRef = 20; // 这也会修改 y 的值
cout << y << endl; // 输出 20
return 0;
}
output
10
20
eg 6-11
#include <iostream>
using namespace std;
int k = 56;
void f1(int a, int &b)
{
cout << "in func f1 begin, a and b is " << a << " " << b << endl;
a += 10;
b += 10;
cout << "in func f1 end, a and b is " << a << " " << b << endl;
}
int &f2(int &a, int &b)
{
cout << "in func f2, a+b=" << a+b << endl;
if ((a+b) % 2 == 0)
return a;
else
return b;
}
int main()
{
int x=1,y=2,z=3,w=0;
f1(x,y);
cout << "func f1, x and y is " << x << " " << y << endl; // 1, 12
cout << "func f1, z k and w is " << z << " " << k << " " << w << endl; // 3,56,0
w = f2(z,k)++;
cout << "func f2, z k and w is " << z << " " << k << " " << w << endl; // 3,57, 56
w = f2(z,k)++;
cout << "func f2, z k and w is " << z << " " << k << " " << w << endl; // 4, 57, 3
return 0;
}
output
in func f1 begin, a and b is 1 2
in func f1 end, a and b is 11 12
func f1, x and y is 1 12
func f1, z k and w is 3 56 0
in func f2, a+b=59
func f2, z k and w is 3 57 56
in func f2, a+b=60
func f2, z k and w is 4 57 3
注意 f2
第一次调用的时候,返回的是引用变量 k
, k
赋值给 w
以后,然后自增
f2
第二次调用的时候,返回的是引用变量 z
, z
赋值给 w
以后,然后自增
函数的返回值为引用时,不能返回局部变量的引用,不然返回函数内部 new 分配的内存的引用
总结
-
引用在内部是通过指针实现的,但引用在语法上更安全、更简洁。
-
引用通常比指针更受欢迎,因为它们不容易出错,并且代码更易于阅读和维护。
-
当使用引用时,确保引用的对象在引用的生命周期内始终有效,以避免悬挂引用(dangling reference)问题。
7、应用实例
eg 6-12,给出一个浮点型数组,保留整数部分,求出小数部分的最大值
#include <iostream>
using namespace std;
// 分离小数的整数和小数部分
void split(double x, int *intp, double *fracp)
{
*intp = int(x);
*fracp = x - *intp;
}
// 保留数组的整数部分,求出小数部分的最大值
double f(double *p, int n)
{
int i, intp;
double fracp, maxfracp=0;
for(i=0; i<n ;i++)
{
split(*(p+i), &intp, &fracp);
if(fracp > maxfracp)
maxfracp = fracp;
*(p+i) = intp;
}
return maxfracp;
}
int main() {
int i;
double maxfr, a[6]={1.1, 2.2, 3.3, 9.9, 6.6, 5.0};
maxfr = f(a, 6);
for(i=0; i<6; i++)
cout << *(a+i) << " ";
cout << endl << maxfr;
return 0;
}
output
1 2 3 9 6 5
0.9
eg 6-13 n 个人,1,2,3 重复报数,报到 3 就淘汰,游戏直到剩下最后一个人才结束
#include <iostream>
using namespace std;
int main()
{
int n = 23; // 参与游戏的总人数
int a[n] = {0};
int zeronum = 0; // 淘汰的人数
int num = 0; // 0,1,2 循环计数
int index =0; // 循环报数
while(n - zeronum > 1) // 淘汰到只剩下一个人的时候退出
{
if(index==n) // 如果所有人循环完,第一个人重新续上
index = 0;
if (a[index] == -1) // 如果人被淘汰,跳过
{
index ++;
continue;
}
if(num==2) // 如果报数到了2,就会被淘汰
{
a[index] = -1; // 淘汰
num=0; // 报数置为0
index ++; // 下一位
zeronum++; // 淘汰数量 +1
continue;
}
num++; // 正常情况下报数 +1
index++; // 下一位游戏玩家
}
for(int i=0; i<n; i++)
if(a[i] != -1)
cout << i+1 << " "; //
return 0;
}
output
8
以 23 个人为例
第一轮
1 2 x 4 5 x 7 8 x 10 11 x 13 14 x 16 17 x 19 20 x 22 23
第二轮
x 2 x 4 x x 7 8 x x 11 x 13 x x 16 17 x x 20 x 22 x
第三轮
x 2 x 4 x x x 8 x x 11 x x x x 16 17 x x x x 22 x
第四轮
x 2 x x x x x 8 x x 11 x x x x x 17 x x x x 22 x
第五轮
x x x x x x x 8 x x 11 x x x x x x x x x x 22 x
第六轮
x x x x x x x 8 x x x x x x x x x x x x x 22 x
第七轮
x x x x x x x 8 x x x x x x x x x x x x x x x
以 30 个人为例
1 2 x 4 5 x 7 8 x 10 11 x 13 14 x 16 17 x 19 20 x 22 23 x 25 26 x 28 29 x
1 2 x x 5 x 7 x x 10 11 x x 14 x 16 x x 19 20 x x 23 x 25 x x 28 29 x
x 2 x x 5 x x x x 10 11 x x x x 16 x x 19 x x x 23 x 25 x x x 29 x
x 2 x x x x x x x 10 11 x x x x x x x 19 x x x 23 x x x x x 29 x
x 2 x x x x x x x x 11 x x x x x x x 19 x x x x x x x x x 29 x
x 2 x x x x x x x x x x x x x x x x 19 x x x x x x x x x 29 x
x x x x x x x x x x x x x x x x x x 19 x x x x x x x x x 29 x
x x x x x x x x x x x x x x x x x x x x x x x x x x x x 29 x
int n =30;
output
29
上述例子用指针的形式表示如下
#include <iostream>
using namespace std;
int main()
{
int n = 30; // 参与游戏的总人数
int a[n] = {0};
int *p = a;
int zeronum = 0; // 淘汰的人数
int num = 0; // 0,1,2 循环计数
int index =0; // 循环报数
while(n - zeronum > 1) // 淘汰到只剩下一个人的时候退出
{
if(index==n) // 如果所有人循环完,第一个人重新续上
index = 0;
if (*(p+index) == -1) // 如果人被淘汰,跳过
{
index ++;
continue;
}
if(num==2) // 如果报数到了2,就会被淘汰
{
*(p+index) = -1; // 淘汰
num=0; // 报数置为0
index ++; // 下一位
zeronum++; // 淘汰数量 +1
continue;
}
num++; // 正常情况下报数 +1
index++; // 下一位游戏玩家
}
for(int i=0; i<n; i++)
if(*(p+i) != -1)
cout << i+1 << " "; //
return 0;
}
eg 6-14 在一串名字中给出一个名字,求其在这串名字中的位置
#include <iostream>
using namespace std;
int search(char(*p)[31], int n, const char* pname)
{
int j;
for(int i=0; i<n; i++) // 遍历每个名字
{
for(j=0; j<31; j++)
{
if (*(*(p+i)+j)=='\0' && *(pname+j)=='\0') // 如果字符都遍历完了还没有退出,表示名字相等
return j;
if (*(*(p+i)+j) != *(pname+j)) // 名字中的字符不相等,比对下一个名字
break;
}
}
return 0;
}
int main()
{
int n = 10;
char name[n][31] = {"name1", "name2", "name3", "name4", "name5", "name6", "name7", "name8", "name9", "name10",};
const char *pname = "name5";
int result = search(name, 10, pname);
cout << result;
return 0;
}
output
5
可以看到 name5 在第五个位置
调用字符串比较接口 strcmp
试试
#include <iostream>
#include<string.h>
using namespace std;
int search(char(*p)[31], int n, const char* pname)
{
for(int i=0; i<n; i++) // 遍历每个名字
{
if(strcmp(*(p+i), pname)==0)
return i+1;
}
return 0;
}
int main()
{
int n = 10;
char name[n][31] = {"name1", "name2", "name3", "name4", "name5", "name6", "name7", "name8", "name9", "name10",};
const char *pname = "name5";
//char pname[] = {"name5"};
int result = search(name, 10, pname);
cout << result;
return 0;
}
output
5
或者 char pname[] = {"name5"};
,对应形参 int search(char(*p)[31], int n, char* pname)
eg 6-15 输入三个字符串,按由大到小输出
#include <iostream>
using namespace std;
void cmp(char *&p1, char *&p2)
{
for(int i=0; i<20; i++)
{
if (*(p1+i) == *(p2+i))
continue;
if (*(p1+i) > *(p2+i))
return;
if (*(p1+i) < *(p2+i))
{
char *tmp;
tmp = p1;
p1 = p2;
p2 = tmp;
return;
}
}
}
int main()
{
char a1[20] = "windows";
char a2[20] = "yesterday";
char a3[20] = "apple";
char * p1 = a1;
char * p2 = a2;
char * p3 = a3;
cmp(p1, p2);
cmp(p1, p3);
cmp(p2, p3);
cout << p1 << " " << p2 << " " << p3 << endl;
return 0;
}
output
yesterday windows apple
函数定义这里的引用 void cmp(char *&p1, char *&p2)
尤为关键
特别注意:少了引用,指针都是临时变量,退出函数的时候会释放掉,没有达到交换的目的,因为我们是对指针操作,而不是指针所指向空间的值操作
再来个数组为例
#include <iostream>
using namespace std;
void cmp(int *&p1, int *&p2)
{
for(int i=0; i<3; i++)
{
if (*(p1+i) == *(p2+i))
continue;
if (*(p1+i) > *(p2+i))
return;
if (*(p1+i) < *(p2+i))
{
int *tmp;
tmp = p1;
p1 = p2;
p2 = tmp;
return;
}
}
}
int main()
{
int a1[3] = {1,2,3};
int a2[3] = {0,1,2};
int a3[3] = {1,3,4};
int * p1 = a1;
int * p2 = a2;
int * p3 = a3;
cmp(p1, p2);
cmp(p1, p3);
cmp(p2, p3);
for(int i=0; i<3; i++)
cout << p1[i] << " ";
cout << endl;
for(int i=0; i<3; i++)
cout << p2[i] << " ";
cout << endl;
for(int i=0; i<3; i++)
cout << p3[i] << " ";
cout << endl;
return 0;
}
output
1 3 4
1 2 3
0 1 2