面向对象程序设计——Visual C++
第6章 指针和引用

思考题

1、如何获取一个变量的地址,试就一般变量、数组、指针变量分别进行说明。

获取一个变量的地址可以使用取地址运算符(&),它可以返回变量在内存中的地址。具体来说:

  1. 对于一般变量,可以使用取地址运算符获取它的地址。例如,定义一个整型变量a,可以使用&符号获取它的地址:int a=10;int *ptr=&a;
  2. 对于数组,可以使用数组名加上下标来获取数组元素的地址。例如,定义一个整型数组arr,可以使用&符号获取数组元素的地址:int arr[5]={1,2,3,4,5};int *ptr=&arr[0];
  3. 对于指针变量,可以直接使用指针变量名获取它的地址。例如,定义一个指向整型变量的指针变量p,可以使用&符号获取它的地址:int a=10;int *p=&a;

2、什么叫指针?说明指针中存储的地址和这个地址中的值之间的区别。

指针是一个变量,它存储了一个内存地址。
指针可以指向任何数据类型,包括基本数据类型、数组、结构体、函数等。
指针中存储的地址是一个无符号整数,它表示内存中的一个位置。这个地址中的值是指针所指向的变量或对象的值。
指针中存储的地址和这个地址中的值之间的区别在于,地址是一个无符号整数,它表示内存中的一个位置,而值是指针所指向的变量或对象的值,它可以是任何数据类型。
指针中存储的地址可以通过取地址运算符&获取,而这个地址中的值可以通过解引用运算符*获取。例如,定义一个指向整型变量的指针变量p,可以使用&符号获取它的地址,然后使用解引用运算符获取这个地址中的值:int a=10;int *p=&a;cout<<*p<<endl;输出结果为10。

3、试说明运算符“&”的三种不同的含义,如何区分它们?

运算符“&”有三种不同的含义:

  1. 取地址运算符:用于获取一个变量或对象在内存中的地址。例如,&a表示获取变量a在内存中的地址。
  2. 按位与运算符:用于对两个二进制数的每一位进行与运算。例如,a&b表示对a和b的每一位进行与运算。
  3. 引用运算符:用于定义引用类型变量。例如,int &ref=a表示定义一个引用类型变量ref,它引用变量a。 区分这三种含义的方法是根据上下文语境来判断。如果&符号出现在变量名前面,表示取地址运算符;如果&符号出现在表达式中间,表示按位与运算符;如果&符号出现在变量名后面,表示引用运算符。

4、指出指针变量与一维数组名之间的异同。

指针变量和一维数组名都可以表示内存中一段连续的数据。它们之间的异同如下:

  1. 相同点:
  • 都可以用来访问数组元素。
  • 都可以进行指针运算。
  • 都可以作为函数参数传递。
  1. 不同点:
  • 指针变量可以指向任何数据类型,而数组名只能表示一种数据类型。
  • 指针变量可以被赋值为任何地址,而数组名不能被赋值为其他地址。
  • 指针变量可以通过动态分配内存来创建,而数组名的大小必须在编译时确定。
  • 指针变量可以被修改指向其他地址,而数组名不能被修改指向其他地址。 总之,指针变量和一维数组名都有各自的优缺点,需要根据具体的情况选择使用哪种方式来表示内存中的数据。

5、什么是行指针?它与一般的元素指针有什么区别?对于二维数组a[M][N],有哪些形式的行指针?有哪些形式的元素指针?

行指针是指向二维数组中某一行的指针变量。它与一般的元素指针的区别在于,行指针只能指向二维数组中的某一行,而不能指向任意一个元素。
对于二维数组a[M][N],有以下形式的行指针:

  1. 指向第i行的指针:int (*p)[N]=&a[i];
  2. 指向第i行第j个元素的指针:int *p=&a[i][j];
  3. 指向第i行第j个元素的指针数组:int *ptr[N]=&a[i][0];
  4. 指向第i行的指针数组:
int *ptr[M];
for(int i=0;i<M;i++){ptr[i]=&a[i][0];}

对于二维数组a[M][N],有以下形式的元素指针:

  1. 指向第i行第j个元素的指针:int *p=&a[i][j];
  2. 指向第i行的指针:int *p=a[i];
  3. 指向第i列的指针:int *p=&a[0][i];

需要注意的是,行指针和元素指针的使用需要根据具体的情况选择,不能混淆使用。

6、什么是指针数组?什么是指向数组的指针?试说明它们之间的区别。

指针数组是一个数组,其中的每个元素都是一个指针变量。
指向数组的指针是一个指针变量,它指向一个数组的首地址。
它们之间的区别在于,指针数组中的每个元素都是一个指针变量,可以指向任何数据类型,而指向数组的指针只能指向一个数组,且只能指向数组的首地址。
指针数组可以用来表示多个指针变量,而指向数组的指针可以用来表示整个数组。
例如,定义一个指针数组:int *p[5];可以用来表示5个整型指针变量,而定义一个指向数组的指针:int(*p)[5];可以用来表示一个包含5个整型元素的数组。
需要注意的是,指针数组和指向数组的指针的使用需要根据具体的情况选择,不能混淆使用。

7、试比较值传递、地址传递和引用传递。试说明引用传递的优点。

值传递、地址传递和引用传递是三种不同的参数传递方式。它们之间的区别如下:

  1. 值传递:将实参的值复制一份传递给形参,形参的修改不会影响实参。
  2. 地址传递:将实参的地址传递给形参,形参可以通过指针来修改实参的值。
  3. 引用传递:将实参的引用传递给形参,形参可以直接修改实参的值,而不需要通过指针来访问。 引用传递的优点包括:
  4. 效率高:引用传递不需要进行值的复制,可以节省时间和空间。
  5. 安全性高:引用传递可以避免指针操作中的空指针和野指针问题,提高了程序的安全性。
  6. 可读性好:引用传递可以使代码更加简洁易读,不需要使用指针运算符。 例如,定义一个函数使用引用传递来交换两个整型变量的值:
void swap(int &x,int &y){
int temp=x;
x=y;
y=temp;
}
int a=10,b=20;
swap(a,b);

可以直接调用swap函数来交换a和b的值,而不需要使用指针或临时变量。

习题

1、定义指针pa和pb,使它们分别指向整型变量a和b。通过指针pa和pb完成下列操作:

  1. 输入变量a和b的值。
  2. 输出这两个变量的和、差、积、商(包括整数商和实数商,且要判断除数是否为0)。
  3. 调整指针的指向关系,使pa总是指向值较大的变量,而pb指向较小的变量。
#include <iostream>
using namespace std;

int main() {
    int a, b;
    int *pa, *pb;
    // 指针指向a和b
    pa = &a;
    pb = &b;
    // 输入a和b的值
    cout << "Enter the value of a: ";
    cin >> a;
    cout << "Enter the value of b: ";
    cin >> b;
    // 计算和、差、积、商并输出
    cout << "Sum: " << *pa + *pb << endl;
    cout << "Difference: " << *pa - *pb << endl;
    cout << "Product: " << *pa * *pb << endl;
    if (*pb != 0) {
        cout << "Integer quotient: " << *pa / *pb << endl;
        cout << "Real quotient: " << (double)*pa / *pb << endl;
    } else {
        cout << "Cannot divide by zero." << endl;
    }
    // 调整指针的指向关系
    if (*pa < *pb) {
        int *temp = pa;
        pa = pb;
        pb = temp;
    }
    // 输出指向关系调整后的结果
    cout << "After adjusting pointer: " << endl;
    cout << "pa points to " << *pa << endl;
    cout << "pb points to " << *pb << endl;
    return 0;
}

2、定义一个描述“商品信息”的结构类型和结构类型的变量,以及指向结构的指针类型和指针变量,通过指针变量完成结构变量的输入和输出。设结构类型含有商品编号、商品名称、计量单位、单价、生产厂家、供货日期(一个子结构,包括年、月、日)、数量等内容。

#include <iostream>
using namespace std;

struct Product {
    int id; // 商品编号
    string name; // 商品名称
    string unit; // 计量单位
    float price; // 单价
    string manufacturer; // 生产厂家
    struct Date {
        int year;
        int month;
        int day;
    } supply_date; // 供货日期
    int quantity; // 数量
};

int main() {
    // 定义结构变量并初始化
    Product product1 = {1, "可乐", "瓶", 2.5, "百事可乐公司", {2022, 1, 1}, 100};
    
    // 定义结构指针变量并指向结构变量
    Product* p_product = &product1;
    
    // 通过指针变量输出结构变量中的内容
    cout << "商品编号:" << p_product->id << endl;
    cout << "商品名称:" << p_product->name << endl;
    cout << "计量单位:" << p_product->unit << endl;
    cout << "单价:" << p_product->price << endl;
    cout << "生产厂家:" << p_product->manufacturer << endl;
    cout << "供货日期:" << p_product->supply_date.year << "-" << p_product->supply_date.month << "-" << p_product->supply_date.day << endl;
    cout << "数量:" << p_product->quantity << endl;
    
    // 通过指针变量输入结构变量中的内容
    cout << "请输入商品编号:";
    cin >> p_product->id;
    cout << "请输入商品名称:";
    cin >> p_product->name;
    cout << "请输入计量单位:";
    cin >> p_product->unit;
    cout << "请输入单价:";
    cin >> p_product->price;
    cout << "请输入生产厂家:";
    cin >> p_product->manufacturer;
    cout << "请输入供货日期(格式:年 月 日):";
    cin >> p_product->supply_date.year >> p_product->supply_date.month >> p_product->supply_date.day;
    cout << "请输入数量:";
    cin >> p_product->quantity;
    
    // 重新通过指针变量输出结构变量中的内容
    cout << "商品编号:" << p_product->id << endl;
    cout << "商品名称:" << p_product->name << endl;
    cout << "计量单位:" << p_product->unit << endl;
    cout << "单价:" << p_product->price << endl;
    cout << "生产厂家:" << p_product->manufacturer << endl;
    cout << "供货日期:" << p_product->supply_date.year << "-" << p_product->supply_date.month << "-" << p_product->supply_date.day << endl;
    cout << "数量:" << p_product->quantity << endl;
    
    return 0;
}

3、设某仓库共有N种商品,描述商品的结构类型如题2,试完成下列操作:

  1. 统计商品的总价值。
  2. 统计供货日期在某时段的商品总数和总价值。
  3. 将商品按名称排序后,再按供货日期输出。
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

const int MAX_PRODUCTS = 100;

struct Product {
    int id; // 商品编号
    string name; // 商品名称
    string unit; // 计量单位
    float price; // 单价
    string manufacturer; // 生产厂家
    struct Date {
        int year;
        int month;
        int day;
    } supply_date; // 供货日期
    int quantity; // 数量
};

// 根据商品名称和供货日期排序
bool compare(Product a, Product b) {
    if (a.name == b.name) {
        if (a.supply_date.year == b.supply_date.year) {
            if (a.supply_date.month == b.supply_date.month) {
                return a.supply_date.day < b.supply_date.day;
            }
            return a.supply_date.month < b.supply_date.month;
        }
        return a.supply_date.year < b.supply_date.year;
    }
    return a.name < b.name;
}

int main() {
    Product products[MAX_PRODUCTS]; // 定义商品结构数组
    int n; // 商品种类数
    float total_value = 0; // 商品总价值
    int total_quantity = 0; // 某时段商品总数
    float total_value_period = 0; // 某时段商品总价值
    int start_year, start_month, start_day; // 统计时段的起始日期
    int end_year, end_month, end_day; // 统计时段的结束日期

    // 输入商品信息
    cout << "请输入商品种类数:";
    cin >> n;
    for (int i = 0; i < n; i++) {
        cout << "请输入第" << i+1 << "种商品信息:" << endl;
        cout << "商品编号:";
        cin >> products[i].id;
        cout << "商品名称:";
        cin >> products[i].name;
        cout << "计量单位:";
        cin >> products[i].unit;
        cout << "单价:";
        cin >> products[i].price;
        cout << "生产厂家:";
        cin >> products[i].manufacturer;
        cout << "供货日期(格式:年 月 日):";
        cin >> products[i].supply_date.year >> products[i].supply_date.month >> products[i].supply_date.day;
        cout << "数量:";
        cin >> products[i].quantity;

        // 统计总价值
        total_value += products[i].price * products[i].quantity;
    }

    // 输出商品总价值
    cout << "商品总价值为:" << total_value << endl;

    // 输入统计时段的起始日期和结束日期
    cout << "请输入统计时段的起始日期(格式:年 月 日):";
    cin >> start_year >> start_month >> start_day;
    cout << "请输入统计时段的结束日期(格式:年 月 日):";
    cin >> end_year >> end_month >> end_day;

    // 统计某时段商品总数和总价值
    for (int i = 0; i < n; i++)
        if (products[i].supply_date.year >= start_year && products[i].supply_date.year <= end_year && products[i].supply_date.month >= start_month && products[i].supply_date.month <= end_month && products[i].supply_date.day >= start_day && products[i].supply_date.day <= end_day) {
            total_quantity += products[i].quantity;
            total_value_period += products[i].price * products[i].quantity;
        }
    }

    // 输出某时段商品总数和总价值
    cout << "供货日期在" << start_year << "年" << start_month << "月" << start_day << "日至" << end_year << "年" << end_month << "月" << end_day << "日的商品总数为:" << total_quantity << endl;
    cout << "供货日期在" << start_year << "年" << start_month << "月" << start_day << "日至" << end_year << "年" << end_month << "月" << end_day << "日的商品总价值为:" << total_value_period << endl;

    // 将商品按名称排序后,再按供货日期输出
    sort(products, products+n, compare);
    cout << "按名称排序后,再按供货日期输出:" << endl;
    for (int i = 0; i < n; i++) {
        cout << "商品编号:" << products[i].id << endl;
        cout << "商品名称:" << products[i].name << endl;
        cout << "计量单位:" << products[i].unit << endl;
        cout << "单价:" << products[i].price << endl;
        cout << "生产厂家:" << products[i].manufacturer << endl;
        cout << "供货日期:" << products[i].supply_date.year << "年" << products[i].supply_date.month << "月" << products[i].supply_date.day << "日" << endl;
        cout << "数量:" << products[i].quantity << endl;
        cout << endl;
    }

    return 0;
}

该程序首先定义了一个商品结构体类型 Product,其中包含了商品的各种属性。接着定义了一个商品结构体数组 products,以及一些用于统计总价值和某时段商品总数和总价值的变量。

在主函数中,程序首先通过 cin 输入了商品种类数和每个商品的信息,并计算出商品的总价值。接着输入统计时段的起始日期和结束日期,并在遍历商品结构体数组时统计出某时段商品总数和总价值。最后,程序对商品结构体数组进行排序并按名称和供货日期输出。

4、编写一个函数,统计一个英文句子中字母的个数,在主程序中实现输入输出。

#include <iostream>
#include <string>

using namespace std;

int count_letters(string sentence) {
    int count = 0;
    for (int i = 0; i < sentence.length(); i++) {
        if (isalpha(sentence[i])) {
            count++;
        }
    }
    return count;
}

int main() {
    string sentence;
    cout << "请输入英文句子:";
    getline(cin, sentence);
    int count = count_letters(sentence);
    cout << "句子中字母的个数为:" << count << endl;
    return 0;
}

5、编写一个函数,统计实数数组中正数(包括0)的个数和负数的个数,以及正数的平均值和负数的平均值。

#include <iostream>
using namespace std;

void countNumbers(double arr[], int size) {
    int positiveCount = 0, negativeCount = 0; // 记录正数和负数的个数
    double positiveSum = 0, negativeSum = 0; // 记录正数和负数的总和

    for (int i = 0; i < size; i++) {
        if (arr[i] >= 0) { // 如果当前数是正数或0
            positiveCount++; // 正数计数器加1
            positiveSum += arr[i]; // 把当前数加入正数总和
        }
        else { // 如果当前数是负数
            negativeCount++; // 负数计数器加1
            negativeSum += arr[i]; // 把当前数加入负数总和
        }
    }

    // 输出正数和负数的个数和平均值
    cout << "Positive count: " << positiveCount << endl;
    if (positiveCount > 0) { // 如果有正数
        double positiveAverage = positiveSum / positiveCount; // 计算正数平均值
        cout << "Positive average: " << positiveAverage << endl;
    }
    else { // 如果没有正数
        cout << "No positive number." << endl;
    }
    cout << "Negative count: " << negativeCount << endl;
    if (negativeCount > 0) { // 如果有负数
        double negativeAverage = negativeSum / negativeCount; // 计算负数平均值
        cout << "Negative average: " << negativeAverage << endl;
    }
    else { // 如果没有负数
        cout << "No negative number." << endl;
    }
}

int main() {
    double arr[] = {3, -2, 0, 5, -1, 2.5, -4, 1.5};
    int size = sizeof(arr) / sizeof(arr[0]); // 计算数组长度
    countNumbers(arr, size); // 调用函数统计正数和负数的个数和平均值
    return 0;
}

6、编写一个函数int index(char *s, char *t),返回一个子串t在另一个字符串s中出现的次数,如果该字串不出现,则返回0。

#include <iostream>
using namespace std;

int index(char *s, char *t) {
    int count = 0; // 记录子串出现的次数
    int sLen = strlen(s), tLen = strlen(t); // 计算两个字符串的长度

    for (int i = 0; i <= sLen - tLen; i++) { // 枚举s中所有可能的子串
        bool isMatch = true; // 标记当前子串是否匹配t
        for (int j = 0; j < tLen; j++) { // 比较当前子串和t中的每个字符
            if (s[i+j] != t[j]) { // 如果不相等,则当前子串不匹配t
                isMatch = false;
                break;
            }
        }
        if (isMatch) { // 如果当前子串匹配t
            count++; // 子串计数器加1
        }
    }

    return count; // 返回子串出现的次数
}

int main() {
    char s[] = "hello world, world hello!";
    char t[] = "world";
    int count = index(s, t); // 统计子串出现的次数
    cout << "Substring \"" << t << "\" appears " << count << " times in \"" << s << "\"." << endl;
    return 0;
}

7、编写一个函数int strcmp(const char *, const char *),实现两个字符串之间的比较。

#include <iostream>
using namespace std;

int strcmp(const char *s1, const char *s2) {
    while (*s1 != '\0' || *s2 != '\0') { // 比较两个字符串中每个字符是否相等
        if (*s1 < *s2) { // 如果s1中的字符小于s2中的字符
            return -1; // s1 < s2,返回-1
        } else if (*s1 > *s2) { // 如果s1中的字符大于s2中的字符
            return 1; // s1 > s2,返回1
        }
        s1++; // 指针s1和s2都向后移动一位
        s2++;
    }
    return 0; // 两个字符串相等,返回0
}

int main() {
    char s1[] = "hello";
    char s2[] = "world";
    int result = strcmp(s1, s2); // 比较两个字符串
    if (result < 0) {
        cout << "\"" << s1 << "\" < \"" << s2 << "\"" << endl;
    } else if (result > 0) {
        cout << "\"" << s1 << "\" > \"" << s2 << "\"" << endl;
    } else {
        cout << "\"" << s1 << "\" == \"" << s2 << "\"" << endl;
    }
    return 0;
}

8、编写一个函数char *strcpy(char *, const char*),实现两个字符串的复制。

#include <iostream>
using namespace std;

char* strcpy(char* dest, const char* src) {
    char* p = dest; // 定义指针p指向目标字符串的首地址
    while ((*dest++ = *src++) != '\0'); // 拷贝源字符串到目标字符串中
    return p; // 返回目标字符串的首地址
}

int main() {
    char s1[10]; // 定义一个长度为10的字符数组s1
    char s2[] = "hello"; // 定义一个字符数组s2,并初始化
    strcpy(s1, s2); // 复制s2中的字符串到s1中
    cout << "s1 = " << s1 << endl; // 输出目标字符串s1
    return 0;
}

9、编写一个函数,返回一维数组中最大元素的值。

函数原型为:float *max(float *p[], int n);

#include <iostream>
using namespace std;

float *max(float *p[], int n) {
    float *maxPtr = *p; // 定义指向最大值的指针
    for (int i = 1; i < n; i++) {
        if (*(p[i]) > *maxPtr) { // 如果当前元素大于最大值,更新指针
            maxPtr = p[i];
        }
    }
    return maxPtr; // 返回指向最大值的指针
}

int main() {
    float arr[] = { 1.1, 2.2, 3.3, 4.4, 5.5 }; // 定义一维数组arr,并初始化
    float *p[5]; // 定义一个指针数组p,元素为指向float类型的指针
    for (int i = 0; i < 5; i++) {
        p[i] = &arr[i]; // 将指针数组的元素指向数组arr的元素地址
    }
    float *maxPtr = max(p, 5); // 调用max()函数,返回指向最大值的指针
    cout << "The maximum element is: " << *maxPtr << endl; // 输出最大元素值
    return 0;
}

10、编写一个函数,将参数p中的元素颠倒次序。

函数原型为:float *mirror(float *p[], int n);

#include <iostream>
using namespace std;

float *mirror(float *p[], int n) {
    float *tmp; // 定义一个临时指针,用于交换指针数组中的元素
    for (int i = 0; i < n / 2; i++) { // 循环遍历指针数组中的前一半元素
        tmp = p[i]; // 将当前元素指针存储到临时指针中
        p[i] = p[n - i - 1]; // 将当前元素指针指向另一端对称位置的元素地址
        p[n - i - 1] = tmp; // 将另一端对称位置的元素指针指向临时指针中存储的地址
    }
    return *p; // 返回指向数组的指针
}

int main() {
    float arr[] = { 1.1, 2.2, 3.3, 4.4, 5.5 }; // 定义一维数组arr,并初始化
    float *p[5]; // 定义一个指针数组p,元素为指向float类型的指针
    for (int i = 0; i < 5; i++) {
        p[i] = &arr[i]; // 将指针数组的元素指向数组arr的元素地址
        cout << *p[i] << " "; // 输出指针数组元素指向的值
    }
    cout << endl;
    mirror(p, 5); // 调用mirror()函数,颠倒数组中元素的次序
    for (int i = 0; i < 5; i++) {
        cout << *p[i] << " "; // 输出指针数组元素指向的值,已经颠倒了次序
    }
    cout << endl;
    return 0;
}

11、定义一个二维字符串数组,输入若干个字符串,按升序排序后输出。要求设计一个通用的排序函数实现这一功能。

排序函数的原型为:void sort(char *p[], int n)

#include <iostream>
#include <string.h>
using namespace std;

void sort(char *p[], int n) { // 定义排序函数
    char *tmp; // 定义一个临时指针,用于交换指针数组中的元素
    for (int i = 0; i < n - 1; i++) { // 循环遍历指针数组
        for (int j = i + 1; j < n; j++) { // 比较当前元素和后面的元素
            if (strcmp(p[i], p[j]) > 0) { // 如果当前元素大于后面的元素
                tmp = p[i]; // 将当前元素指针存储到临时指针中
                p[i] = p[j]; // 将当前元素指针指向后面元素地址
                p[j] = tmp; // 将后面元素指针指向临时指针中存储的地址
            }
        }
    }
}

int main() {
    char strs[5][20]; // 定义一个二维字符串数组strs
    char *p[5]; // 定义一个指针数组p,元素为指向char类型的指针
    cout << "请输入5个字符串:" << endl;
    for (int i = 0; i < 5; i++) {
        cin >> strs[i]; // 从标准输入读入字符串
        p[i] = strs[i]; // 将指针数组的元素指向字符串数组的元素地址
    }
    sort(p, 5); // 调用sort()函数,对字符串数组排序
    cout << "排序后的结果为:" << endl;
    for (int i = 0; i < 5; i++) {
        cout << p[i] << " "; // 输出指针数组元素指向的字符串
    }
    cout << endl;
    return 0;
}

12、输入一个二维整型数组a[6][6]。用三种方法输出该二维数组中所有元素的平均值、最大元素和最小元素。

每一种方法都用一个函数实现,三个函数的原型为:

void fun1(int *p, int n);
void fun2(int a[][6], int n);
void fun3(int (*p)[6], int n);

下面是三种不同方法的实现代码:

方法一:使用一维数组,将二维数组转化为一维数组来处理。

void fun1(int *p, int n) {
    int sum = 0;
    int max_val = INT_MIN;
    int min_val = INT_MAX;
    for (int i = 0; i < n * n; i++) {
        sum += p[i];
        max_val = max(max_val, p[i]);
        min_val = min(min_val, p[i]);
    }
    double avg_val = sum * 1.0 / (n * n);
    cout << "平均值:" << avg_val << endl;
    cout << "最大值:" << max_val << endl;
    cout << "最小值:" << min_val << endl;
}

方法二:使用二维数组来处理,逐个遍历所有元素。

void fun2(int a[][6], int n) {
    int sum = 0;
    int max_val = INT_MIN;
    int min_val = INT_MAX;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            sum += a[i][j];
            max_val = max(max_val, a[i][j]);
            min_val = min(min_val, a[i][j]);
        }
    }
    double avg_val = sum * 1.0 / (n * n);
    cout << "平均值:" << avg_val << endl;
    cout << "最大值:" << max_val << endl;
    cout << "最小值:" << min_val << endl;
}

方法三:使用指向二维数组的指针来处理。

void fun3(int (*p)[6], int n) {
    int sum = 0;
    int max_val = INT_MIN;
    int min_val = INT_MAX;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            sum += *(*(p + i) + j);
            max_val = max(max_val, *(*(p + i) + j));
            min_val = min(min_val, *(*(p + i) + j));
        }
    }
    double avg_val = sum * 1.0 / (n * n);
    cout << "平均值:" << avg_val << endl;
    cout << "最大值:" << max_val << endl;
    cout << "最小值:" << min_val << endl;
}

13、设计一个程序,用指向函数的指针完成下述功能:

编写两个对一维数组(int a[N])排序的函数,其中一个按升序排序,另一个按降序排序。主函数中定义一个指向函数的指针,根据需要调用上面两个函数之一,将实参排序。

#include <iostream>
using namespace std;

const int N = 10;

// 按升序排序
void asc_sort(int a[]) {
    for (int i = 0; i < N - 1; i++) {
        for (int j = 0; j < N - 1 - i; j++) {
            if (a[j] > a[j + 1]) {
                swap(a[j], a[j + 1]);
            }
        }
    }
}

// 按降序排序
void desc_sort(int a[]) {
    for (int i = 0; i < N - 1; i++) {
        for (int j = 0; j < N - 1 - i; j++) {
            if (a[j] < a[j + 1]) {
                swap(a[j], a[j + 1]);
            }
        }
    }
}

// 根据传入的函数指针,调用对应的排序函数
void sort_arr(int a[], void (*sort_func)(int a[])) {
    sort_func(a);
}

int main() {
    int a[N] = {4, 3, 6, 1, 8, 9, 0, 2, 7, 5};
    cout << "排序前:";
    for (int i = 0; i < N; i++) {
        cout << a[i] << " ";
    }
    cout << endl;

    // 定义一个指向函数的指针,用来根据需要调用对应的排序函数
    void (*sort_func)(int a[]);
    int choice;
    cout << "请选择排序方式:1-升序,2-降序" << endl;
    cin >> choice;
    if (choice == 1) {
        sort_func = asc_sort;
    } else if (choice == 2) {
        sort_func = desc_sort;
    } else {
        cout << "无效选择!" << endl;
        return 0;
    }

    sort_arr(a, sort_func);

    cout << "排序后:";
    for (int i = 0; i < N; i++) {
        cout << a[i] << " ";
    }
    cout << endl;

    return 0;
}

在这个示例程序中,我们定义了两个排序函数asc_sort和desc_sort,分别用来按升序和降序排序。然后定义了一个sort_arr函数,用来根据传入的函数指针调用对应的排序函数。在主函数中,首先输入需要排序的数组a,然后根据用户的选择,将指向函数的指针指向对应的排序函数,并将其作为参数传入sort_arr函数中,完成排序操作。最后输出排序后的结果。

16、用new运算符产生一个一维数组,输入并输出数组中的各个元素的值,然后将数组元素颠倒排列后,再次输出各元素的值。

#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "请输入数组长度:";
    cin >> n;

    // 使用new运算符分配动态内存
    int *arr = new int[n];

    cout << "请输入" << n << "个整数:" << endl;
    for (int i = 0; i < n; i++) {
        cin >> arr[i];
    }

    cout << "原始数组元素为:" << endl;
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;

    // 将数组元素颠倒排列
    for (int i = 0; i < n / 2; i++) {
        int temp = arr[i];
        arr[i] = arr[n - i - 1];
        arr[n - i - 1] = temp;
    }

    cout << "颠倒排列后的数组元素为:" << endl;
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;

    // 释放动态内存
    delete[] arr;
    return 0;
}

该程序先提示用户输入数组的长度,然后使用new运算符分配一个长度为n的int类型一维数组的动态内存,并提示用户输入数组的元素值。接着输出原始数组元素值,然后使用一个for循环将数组元素颠倒排列,并输出颠倒排列后的数组元素值。最后使用delete[]运算符释放动态内存。

17、试编写一个函数,将链表head2中的所有结点全部连接到链表head1的结点之后,形成一个新的链表(头结点指针仍为head1)。

#include <iostream>
using namespace std;

struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};

ListNode* mergeList(ListNode *head1, ListNode *head2) {
    ListNode *p = head1;
    while (p->next != NULL) {
        p = p->next;
    }
    p->next = head2;
    return head1;
}

int main() {
    // 构造链表head1
    ListNode *head1 = new ListNode(1);
    ListNode *node1_1 = new ListNode(2);
    ListNode *node1_2 = new ListNode(3);
    head1->next = node1_1;
    node1_1->next = node1_2;

    // 构造链表head2
    ListNode *head2 = new ListNode(4);
    ListNode *node2_1 = new ListNode(5);
    ListNode *node2_2 = new ListNode(6);
    head2->next = node2_1;
    node2_1->next = node2_2;

    // 将head2连接到head1之后,形成新链表
    head1 = mergeList(head1, head2);

    // 输出新链表的元素值
    ListNode *p = head1;
    while (p != NULL) {
        cout << p->val << " ";
        p = p->next;
    }
    cout << endl;

    // 释放链表的动态内存
    p = head1;
    while (p != NULL) {
        ListNode *temp = p->next;
        delete p;
        p = temp;
    }
    return 0;
}

该程序首先定义了一个结构体ListNode,表示链表中的结点。然后实现了一个mergeList函数,该函数接受两个链表的头结点指针head1和head2,将head2中的所有结点连接到head1的结点之后,返回新链表的头结点指针head1。

在主函数中,首先构造了两个链表head1和head2,然后调用mergeList函数将head2连接到head1之后,形成新链表。最后输出新链表的元素值,并释放链表的动态内存。

18、试编写一个函数,把两个链表合并为一个链表,使得两个链表中的结点交替地在新链表中出现。若原来某个链表中具有较多的结点,则把多余的结点接在新链表的末尾。

#include <iostream>
using namespace std;

//定义链表结点结构体
struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};

ListNode* mergeList(ListNode* l1, ListNode* l2) {
    ListNode *p1 = l1, *p2 = l2;
    ListNode *newHead = new ListNode(-1);
    ListNode *cur = newHead;

    while (p1 && p2) {  // 两个链表都有结点时
        cur->next = p1;
        p1 = p1->next;
        cur = cur->next;
        cur->next = p2;
        p2 = p2->next;
        cur = cur->next;
    }

    if (p1) {  // l1比l2多
        cur->next = p1;
    }
    if (p2) {  // l2比l1多
        cur->next = p2;
    }

    return newHead->next;
}

该函数的输入参数为两个链表头指针,输出一个新链表的头指针。在该函数中,我们定义了两个指针 p1 和 p2,用于遍历两个链表,同时定义了一个新的头结点 newHead 和指向当前结点的指针 cur。我们将两个链表中的结点依次插入新链表中,直到其中一个链表为空,最后,若有一个链表比另一个链表多,则将多余的结点接在新链表的末尾。

19、建立一个无序链表,每一个结点包含:学号、姓名、年龄和C++成绩。要求编写实现如下操作的四个函数:建立链表、输出链表中各个结点的值、删除具有某个学号的结点、在某个位置插入结点、释放链表中结点所占用的动态存储空间。

为了实现这个题目,我们需要首先定义一个链表结构体,包含学号、姓名、年龄和成绩四个属性,以及一个指向下一个结点的指针。然后,我们可以实现以下四个函数:

  1. Node* createList(int n):建立一个包含n个结点的无序链表,每个结点的值从键盘输入。
  2. void printList(Node* head):输出链表中所有结点的值。
  3. Node* deleteNode(Node* head, int id):删除学号为id的结点。
  4. Node* insertNode(Node* head, int pos, Node* newNode):在位置pos上插入一个新的结点newNode。

最后,我们需要一个函数释放链表中结点所占用的动态存储空间。

#include <iostream>
#include <string>
using namespace std;

// 定义链表节点结构体
struct Student {
    int number;
    string name;
    int age;
    float score;
    Student* next;
};

// 建立链表函数
Student* createList() {
    Student* head = new Student;
    Student* p = head;
    int n;
    cout << "请输入学生人数:";
    cin >> n;
    for (int i = 1; i <= n; i++) {
        Student* q = new Student;
        cout << "请输入第" << i << "个学生的信息:" << endl;
        cout << "学号:";
        cin >> q->number;
        cout << "姓名:";
        cin >> q->name;
        cout << "年龄:";
        cin >> q->age;
        cout << "C++成绩:";
        cin >> q->score;
        p->next = q;
        p = q;
    }
    p->next = nullptr;
    return head;
}

// 输出链表函数
void printList(Student* head) {
    cout << "学号\t姓名\t年龄\tC++成绩" << endl;
    Student* p = head->next;
    while (p != nullptr) {
        cout << p->number << "\t" << p->name << "\t" << p->age << "\t" << p->score << endl;
        p = p->next;
    }
}

// 删除指定学号节点函数
void deleteNode(Student* head, int num) {
    Student* p = head;
    while (p->next != nullptr && p->next->number != num) {
        p = p->next;
    }
    if (p->next == nullptr) {
        cout << "没有该学号的学生" << endl;
    } else {
        Student* q = p->next;
        p->next = q->next;
        delete q;
        cout << "已删除学号为" << num << "的学生" << endl;
    }
}

// 插入节点函数
void insertNode(Student* head, int pos) {
    Student* p = head;
    int n = 0;
    while (p->next != nullptr && n < pos - 1) {
        p = p->next;
        n++;
    }
    if (n < pos - 1 || p->next == nullptr) {
        cout << "插入位置无效" << endl;
    } else {
        Student* q = new Student;
        cout << "请输入插入节点的信息:" << endl;
        cout << "学号:";
        cin >> q->number;
        cout << "姓名:";
        cin >> q->name;
        cout << "年龄:";
        cin >> q->age;
        cout << "C++成绩:";
        cin >> q->score;
        q->next = p->next;
        p->next = q;
        cout << "已在第" << pos << "个位置插入节点" << endl;
    }
}

// 释放链表函数
void freeList(Student* head) {
    Student *curr = head;
    while (curr != nullptr) {
        Student *temp = curr;
        curr = curr->next;
        delete temp;
    }
    head = nullptr;
    cout << "The linked list has been freed." << endl;
}

实验题

2、指出下列程序的错误,说明理由并改正。

void main(){
	int *p1, *p2;
	*p1=4;
	p2=new int(3);
	int &i, j, &k=*p2;
	i=j;
}

以下是该程序的错误及原因:

  1. 指针p1没有初始化就被解引用,导致无法预知*p1所指向的内存位置,这可能导致程序崩溃。
  2. 指针p2所指向的内存块没有释放,可能导致内存泄漏。
  3. 没有为引用变量i和j分配内存,这会导致编译错误。 改正后的程序如下:
#include <iostream>
using namespace std;
int main(){
    int *p1, *p2;
    p1 = new int; // 为指针p1分配内存
    *p1 = 4;
    p2 = new int(3);
    int i = *p1, j = 0, &k = *p2; // 为引用变量i和j分配内存,并初始化j为0
    cout << "i=" << i << ", j=" << j << ", k=" << k << endl;
    delete p1; // 释放p1所指向的内存
    delete p2; // 释放p2所指向的内存
    return 0;
}