可变长数组

1. 数组问题

基本数组常见问题

  • 数组容量一旦确定无法修改。
  • 数组的存储类型无法修改。
  • 数组不得作为返回值类型。

设计结构体类型来描述数组,增强数组的功能

2. 可变长数组结构设计

目标存储类型为 Student *,数组中的每一个元素都是一个学生结构体指针。

typedef struct student_array
{
    /*
    elements 在这里可以认为是一个指针数组,存储类型为Student * 学生结构体指针,
    因为当前存储数据的内存空间在内存的堆区,需要通过指针访问。相当于通过当前
    elements 指针访问对应的内存空间,得到的是一个 Student * 指针,所以当前
    elements 类型是 Student ** 二级指针
    */
    Student ** elements;

    /*
    当前底层 elements 数组的容量
    */
    size_t capacity;

    /*
    当前底层 elements 有效元素个数
    */
    size_t size;
} Student_Array;

函数指针

1. 插件式编程逻辑

函数功能主体不变,同时引入不同的操作规则/函数指针,完成当前函数的功能多样式实现,生活中案例有,红白机/游戏机,擦丝器,可替换刀头螺丝刀

2. 函数指针语法要求

【核心理念】

  • 替换
  • 删除
int my_add(int n1, int n2);

函数名对于当前函数而言是一个【指针常量】,对应当前函数在内存【代码区】的空间首地址。函数指针的类型是根据当前函数的【返回值类型】和【参数类型】决定

如果需要定义一个函数指针用于存储 my_add 函数在内存中的首地址。格式

// 根据已知函数声明,分析当前的函数数据类型,推演对应函数指针定义形式
// 1. 【替换】将函数名替换为 (*函数指针名)
my_add ==> (*p_fun);
// 2. 【删除】删除函数形式参数列表中的参数变量名称
(int n1, int n2) ==> (int, int);

// 整合之后, 当前函数指针变量为,当前函数指针可以存储的函数地址
// 要求
//      1. 返回值为 int 类型,
//      2. 参数必须是两个 int 类型。
int (*p_fun)(int, int);

3. 函数指针案例

#include <stdio.h>

int my_add(int n1, int n2);

int main(int argc, char const *argv[])
{
    /*
    根据当前 my_add 函数定义一个函数指针
    函数名是当前函数在内存代码区的空间首地址。
    */
    int (*p_fun)(int, int) = my_add;//定义函数指针

    printf("p_fun : %p\n", p_fun);
    printf("my_add : %p\n", my_add);

    printf("my_add(10, 20) : %d\n", my_add(10, 20));
    printf("p_fun(10, 20) : %d\n", p_fun(10, 20));

    return 0;
}

int my_add(int n1, int n2)
{
    return n1 + n2;
}

4. 函数指针作为函数的参数【重点】

#include <stdio.h>

int my_add(int n1, int n2);
int my_sub(int n1, int n2);
int my_mul(int n1, int n2);
int my_div(int n1, int n2);

/**
 * 当前函数所需参数是两个 int 类型数据,和一个函数指针,函数指针要求
 * 提供的函数,必须是返回值为 int 类型,参数有是两个 int 类型函数。
 *
 * @param n1    用户提供的 int 类型数据
 * @param n2    用户提供的 int 类型数据
 * @param p_fun 用户指定处理当前两个 int 类型数据的函数。
 * @return 返回值是当前函数指针指向函数执行结果。
 */
int operate(int n1, int n2, int (*p_fun)(int, int));

int main(int argc, char const *argv[])
{
    /*
    函数所需参数是一个函数指针,需要提供给函数的实际参数
    是满足函数指针限制的函数名称。
    */
    int ret = operate(10, 20, my_add);
    printf("ret : %d\n", ret);

    ret = operate(100, 20, my_sub);
    printf("ret : %d\n", ret);

    ret = operate(10, 20, my_mul);
    printf("ret : %d\n", ret);

    ret = operate(100, 20, my_div);
    printf("ret : %d\n", ret);
    return 0;
}

int operate(int n1, int n2, int (*p_fun)(int, int))
{
    return p_fun(n1, n2);
}

int my_add(int n1, int n2)
{
    return n1 + n2;
}
int my_sub(int n1, int n2)
{
    return n1 - n2;
}
int my_mul(int n1, int n2)
{
    return n1 * n2;
}
int my_div(int n1, int n2)
{
    return n1 / n2;
}

5. 函数指针作为过滤函数条件限制实现

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

#include "student.h"

void filter_student_data1(Student **stu_array);
void filter_student_data2(Student **stu_array);

/**
 * 谓语判断,对提供的 Student * 数据进行判断,返回结果是一个
 * int 类型数据,满足条件返回 1,否则返回 0
 *
 * @param stu Student * 结构体指针
 * @return 函数内部进行数据判断,满足返回 1,否则返回 0
 */
int predicate(Student * stu);

/**
 * 使用 predicate 谓语函数作为当前过滤展示 Student * 条件内容,要求用户提供的
 * 参数为 Student * 指针数组,和 处理过滤当前 Student * 条件的函数指针,要求函数
 * 指针返回值类型必须是 int 类型,参数类型必须是 Student * 类型。
 *
 * @param stu_array Student * 指针数组
 * @param predicate 函数指针,要求提供的函数返回值为 int ,参数为 Student *
 */
void filter_student_data_using_predicate(Student **stu_array, int (*predicate)(Student *));

int main(int argc, char const *argv[])
{
    Student **stu_array = (Student **)calloc(10, sizeof(Student *));

    for (size_t i = 0; i < 10; i++)
    {
        stu_array[i] = create_new_student(i + 1, "James", i * 5, 'M');
    }

#if 0
    filter_student_data1(stu_array);
    printf("=----------------------------------=\n");
    filter_student_data2(stu_array);
#endif
    filter_student_data_using_predicate(stu_array, predicate);

    for (size_t i = 0; i < 10; i++)
    {
        release_student(stu_array[i]);
    }

    return 0;
}

int predicate(Student * stu)
{
    return stu->id < 6;
}

void filter_student_data_using_predicate(Student **stu_array, int (*predicate)(Student *))
{
    for (size_t i = 0; i < 10; i++)
    {
        // 直接利用函数指针作为 if 条件判断依据
        if (predicate(stu_array[i]))
        {
            show_student_data(stu_array[i]);
        }
    }
}

void filter_student_data1(Student **stu_array)
{
    for (size_t i = 0; i < 10; i++)
    {
        if (stu_array[i]->age > 20)
        {
            show_student_data(stu_array[i]);
        }
    }
}
void filter_student_data2(Student **stu_array)
{
    for (size_t i = 0; i < 10; i++)
    {
        if (stu_array[i]->id < 3)
        {
            show_student_data(stu_array[i]);
        }
    }
}