Bootstrap

C 习题答案20240710-前置

一、C语言 写出指针的定义和使用示例

指针(Pointer) 指针是指向内存位置的变量,它存储了一个内存地址,这个地址指向某个特定类型的数据。

  • 地址部分:存储变量或者数据结构在内存中的位置。
  • 类型部分:指针指向的数据的类型,例如整数、字符或自定义结构体等。
    在C语言中,使用指针可以实现以下几个重要的功能:
应用场景
  • 直接访问和修改内存:通过指针可以直接访问内存中的数据,而不需要通过变量名。
  • 动态内存分配:可以使用指针来动态分配内存,例如使用 malloc() 和 free() 函数。
  • 函数参数传递:可以通过指针在函数之间传递数据,从而可以修改函数外部的变量值。
  • 实现复杂数据结构:例如链表、树等数据结构通常需要使用指针来实现。
#include <stdio.h>

int main() {
    int num = 10;    // 定义一个整数变量 num,并赋值为 10
    int *ptr;        // 定义一个整型指针 ptr

    ptr = &num;      // 将 ptr 指向 num 的地址

    // 输出 num 的值及其地址
    printf("Value of num: %d\n", num);
    printf("Address of num: %p\n", &num);

    // 输出 ptr 存储的地址和其指向的值
    printf("Value of ptr: %p\n", ptr);
    printf("Value pointed to by ptr: %d\n", *ptr);

    // 修改 num 的值通过指针
    *ptr = 20;
    printf("New value of num: %d\n", num);

    return 0;
}

二、C语言 写出值传递和址传递的区别和使用示例

值传递

在值传递中,函数接收的是实参的副本,而不是实参本身的地址。这意味着函数操作的是实参的拷贝,对形参的修改不会影响到实参本身。

#include <stdio.h>

// 值传递的函数
void increment(int num) {
    num++;
    printf("Inside function: %d\n", num);
}

int main() {
    int x = 5;
    
    // 调用函数,传递 x 的值
    increment(x);
    
    // x 的值没有被修改
    printf("Outside function: %d\n", x);

    return 0;
}
址传递

在地址传递中,函数接收的是实参的地址,也就是实参的指针。这样函数就可以直接操作实参所在的内存地址,从而修改实参的值。

#include <stdio.h>

// 地址传递的函数
void incrementByAddress(int *ptr) {
    (*ptr)++;
    printf("Inside function: %d\n", *ptr);
}

int main() {
    int x = 5;
    
    // 调用函数,传递 x 的地址
    incrementByAddress(&x);
    
    // x 的值被修改为 6
    printf("Outside function: %d\n", x);

    return 0;
}

四、C语言 编写一个程序,实现以下功能:

C语言 给定一个整数数组 nums 和数组长度 n,计算数组中所有元素的和,并找出数组中的最大值和最小值。

#include <stdio.h>

// 计算数组总和、最大值和最小值
void calculate(int *nums, int n, int *sum, int *max, int *min) {
    *sum = 0;
    *max = nums[0];
    *min = nums[0];
    
    for (int i = 0; i < n; ++i) {
        *sum += nums[i];
        if (nums[i] > *max) {
            *max = nums[i];
        }
        if (nums[i] < *min) {
            *min = nums[i];
        }
    }
}

int main() {
    int nums[] = {5, 10, 2, 8, 3};
    int n = sizeof(nums) / sizeof(nums[0]);

    int sum, max, min;
    
    // 调用计算函数,传递数组首地址和长度
    calculate(nums, n, &sum, &max, &min);
    
    // 输出结果
    printf("Array sum: %d\n", sum);
    printf("Array max value: %d\n", max);
    printf("Array min value: %d\n", min);

    return 0;
}

五、C语言 写出共用体的定义和使用示例

共用体(Union)是一种数据结构,它允许在相同的内存位置上存储不同的数据类型。与结构体不同的是,结构体中的每个成员都占用独立的内存空间,而共用体中的所有成员共享相同的内存空间。这意味着在任何时刻,共用体只能存储一个成员的值,修改一个成员的值会影响到所有其他成员的值。

使用场景

节省内存:当多个数据成员在不同时间点使用,但不会同时使用时,可以使用共用体来节省内存。
类型转换:可以将一个数据类型的值转换为另一个数据类型的值来进行操作。

#include <stdio.h>

// 定义一个共用体
union Data {
    int intValue;
    float floatValue;
    char strValue[20];
};

int main() {
    // 创建一个共用体变量
    union Data data;

    // 将 int 值赋给共用体的 intValue 成员
    data.intValue = 10;
    printf("data.intValue: %d\n", data.intValue);

    // 将 float 值赋给共用体的 floatValue 成员
    data.floatValue = 3.14;
    printf("data.floatValue: %.2f\n", data.floatValue);

    // 将字符串赋给共用体的 strValue 成员
    snprintf(data.strValue, sizeof(data.strValue), "Hello, World!");
    printf("data.strValue: %s\n", data.strValue);

    // 由于所有成员共享同一内存区域,输出结果可能会出现不预期的结果
    printf("After setting strValue:\n");
    printf("data.intValue: %d\n", data.intValue);
    printf("data.floatValue: %.2f\n", data.floatValue);

    return 0;
}

六、C语言 写出结构体的定义和使用示例,写出结构体和类之间的异同。

结构体:不同类型数据的集合

#include <stdio.h>
#include <string.h>

// 定义一个结构体
struct Person {
    char name[50];
    int age;
    float height;
};

int main() {
    // 创建结构体变量
    struct Person person1;

    // 向结构体成员赋值
    strcpy(person1.name, "John");
    person1.age = 25;
    person1.height = 175.5;

    // 访问结构体成员并输出
    printf("Person 1\n");
    printf("Name: %s\n", person1.name);
    printf("Age: %d\n", person1.age);
    printf("Height: %.1f\n", person1.height);

    return 0;
}
结构体和类之间的异同
相同点:

成员组合:结构体和类都允许将多个不同类型的数据组合在一起。
数据封装:可以通过结构体和类来实现数据的封装,保护数据不被直接访问和修改。

不同点:
  1. 成员访问控制

    • 结构体中的成员默认是公有的(Public),即可以直接访问和修改。
    • 中的成员可以具有公有、私有(Private)和受保护(Protected)等不同的访问控制权限,通过访问修饰符进行控制。
  2. 方法和函数

    • 可以包含方法(Member Functions),允许操作和修改类的成员数据。
    • 结构体通常用于简单的数据组织,不包含方法。
  3. 继承和多态

    • 支持继承(Inheritance)和多态(Polymorphism),允许代码的复用和扩展。
    • 结构体不支持继承和多态,只能通过组合来实现复杂的数据结构。
  4. 内存布局

    • 结构体中的成员按照定义的顺序依次存储,可以通过偏移量访问各个成员。
    • 类中的成员可能会受到编译器的优化和重新排列,不同编译器可能会有不同的布局。

七、请编写一个程序,实现以下功能:

给定学生的成绩信息,包括学生姓名和各门课程的成绩,计算每位学生的平均成绩,并找出所有学生中的最高平均成绩和最低平均成绩。

要求

  1. 使用结构体来表示学生信息,结构体包括学生姓名和一个包含成绩的整型数组。
  2. 使用指针来访问结构体数组中的每个学生及其成绩。
  3. 输出每位学生的平均成绩,以及所有学生中的最高平均成绩和最低平均成绩。
#include <stdio.h>

#define MAX_STUDENTS 5  // 假设有5位学生

// 定义学生结构体
struct Student {
    char name[50];
    int grades[3];  // 假设每位学生有3门课程的成绩
    float average;  // 平均成绩
};

// 计算每位学生的平均成绩
void calculateAverage(struct Student *students, int numStudents) {
    for (int i = 0; i < numStudents; ++i) {
        float sum = 0.0;
        for (int j = 0; j < 3; ++j) {  // 计算每位学生的总成绩
            sum += students[i].grades[j];
        }
        students[i].average = sum / 3.0;  // 计算平均成绩
    }
}

// 找出所有学生的最高和最低平均成绩
void findMinMaxAverage(struct Student *students, int numStudents, float *max, float *min) {
    *max = students[0].average;
    *min = students[0].average;
    
    for (int i = 1; i < numStudents; ++i) {
        if (students[i].average > *max) {
            *max = students[i].average;
        }
        if (students[i].average < *min) {
            *min = students[i].average;
        }
    }
}

int main() {
    // 假设有5位学生的成绩信息
    struct Student students[MAX_STUDENTS] = {
        {"Alice", {85, 90, 88}},
        {"Bob", {79, 82, 75}},
        {"Charlie", {92, 88, 95}},
        {"David", {70, 65, 68}},
        {"Eve", {90, 92, 87}}
    };

    int numStudents = MAX_STUDENTS;
    float maxAverage, minAverage;

    // 计算每位学生的平均成绩
    calculateAverage(students, numStudents);

    // 找出所有学生的最高和最低平均成绩
    findMinMaxAverage(students, numStudents, &maxAverage, &minAverage);

    // 输出每位学生的平均成绩
    printf("Average grades:\n");
    for (int i = 0; i < numStudents; ++i) {
        printf("%s: %.2f\n", students[i].name, students[i].average);
    }

    // 输出所有学生的最高和最低平均成绩
    printf("Max average grade: %.2f\n", maxAverage);
    printf("Min average grade: %.2f\n", minAverage);

    return 0;
}

八、字段和属性的定义

字段(Fields)

  • 定义:字段是类的成员变量,用于存储数据。
  • 访问级别:字段可以是public、private、protected或internal,这决定了字段的可见性和可访问性。
  • 封装性:字段通常不提供额外的逻辑或验证,它们直接暴露类的内部状态。
  • 使用场景:通常,字段用于类的内部实现,而不应直接暴露给类的外部使用者,因为这可能破坏封装性。

属性(Properties)

  • 定义:属性是一种特殊的类成员,它提供了对类字段的访问。属性使用get和set访问器来定义读取和写入值时的行为。
  • 访问级别:与字段类似,属性也可以有不同的访问级别。
  • 封装性:属性提供了一种封装字段的方式,允许在读取或写入字段值之前执行额外的逻辑,如验证、转换或触发事件。
  • 使用场景:属性通常用于暴露类的状态给外部使用者,同时保持对内部数据的控制。
    主要区别
  • 封装性:字段直接暴露数据,而属性提供了一种封装数据的方式,允许在访问数据之前执行额外的逻辑。
  • 访问控制:通过属性,你可以更精细地控制对数据的访问。例如,你可以提供一个只读的属性,或者一个只在特定条件下可写的属性。
  • 数据验证:属性允许你在设置或获取值时执行数据验证。例如,你可以确保一个表示年龄的属性不会被设置为负数。
  • 灵活性:由于属性本质上是方法,因此它们比字段更灵活。你可以随时更改属性的实现,而不会影响调用代码。
  • 性能:虽然现代编译器的优化使得属性和字段之间的性能差异变得微乎其微,但在极端性能敏感的场景中,直接访问字段可能会稍微快一些,因为属性访问涉及到方法调用。

示例

public class Person
{
    // 字段示例
    private int _age;

    // 属性示例
    public int Age
    {
        get { return _age; }
        set
        {
            if (value >= 0)
            {
                _age = value;
            }
            else
            {
                throw new ArgumentOutOfRangeException(nameof(value), "Age cannot be negative.");
            }
        }
    }
}
;