当前位置: 首页 > news >正文

【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 752个学生:1002 77 76 89 69 663个学生:1003 89 88 78 75 674个学生: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 752个学生:1002 77 76 89 69 663个学生:1003 89 88 78 75 674个学生: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

相关文章:

  • GenBI 可视化选谁:Python Matplotlib?HTML ?Tableau?
  • Day31 第八章 贪心算法 part04
  • 进程间通信 —— 共享内存
  • 数字电子电路基础第三章——门电路(一)
  • MySQL--DQL、DML、DDL、DCL概念与区别
  • 硬编码(三)经典变长指令一
  • 2011-2019年各省电信业务总量数据
  • [STM32]从零开始的STM32 DEBUG问题讲解及解决办法
  • 利用three.js在Vue项目中展示重构的stl模型文件
  • Grafana接入Zabbix数据源
  • java23种设计模式-状态模式
  • 超越期望:提供超越标准的客户服务
  • Flume
  • 【Kubernetes】 Scheduler 的逻辑:从 Predicates/Priorities 到 Filter/Score
  • 微深节能 高炉废渣车天车精确定位系统 格雷母线
  • 使用CSS3DRenderer/CSS2DRenderer给模型上面添加html标签的一个demo
  • 3.1部署filebeat:5044
  • 2025年光电科学与智能传感国际学术会议(ICOIS 2025)
  • 可视化的决策过程:决策树 Decision Tree
  • 良田S500L高拍仪对接
  • 做ppt设计师哪个网站好/2021年年度关键词
  • 电脑培训班的课程/南京seo优化公司
  • 福建建设执业资格网站报名系统/seo建站是什么意思
  • 网站建设实习小结/手机端搜索引擎排名
  • 如何做中介网站/网站首页制作网站
  • 给素材网站做签约设计不想做了/1元购买域名