一:指针是什么

1. 指针是**变量**
2. 指针是用来存放地址的变量(存放变量的地址),用来表示指定内存空间的地址。
3. 地址的大小是**固定**的:占4个字节(32位系统)或者8个字节(64位系统)
4. 指针**存在类型**,如整型指针、字符指针等,其类型决定了指针加减运算时移动的步长大小。同时指针可以进行**算术、关系运算**。

二:字符指针

    字符指针:是指存放char类型变量的地址的变量,称为字符指针。

字符指针可以有以下3中方式进行表示:
方式1:

char  a = 'c';
char* p = &a;   // 把字符变量a的地址赋值给字符指针p
*p = 'd';            // 修改字符指针p所指向内存空间存储的内容
printf("%c\n", *p);  // 输出:d

方式2:

char arr[] = "abcdef";  // arr数组的长度为7,不是6
char *p = arr;    // 数组名就是数组首元素的地址
printf("%c\n", *p);  // 解引用后得到字符a                                输出:a
printf("%s\n", p);   // 输出字符指针p所指向的字符串               输出:abcdef
printf("%s\n", arr); // 数组名就是首元素的地址,故与p相同:输出:abcdef

方式3:

char *p = "abcdef"; // 该字符串是一个常量字符串,且把该字符串的首字符的地址放到字符指针p中
// 该字符串在内存中的表示是:a b c d e f \0,把该字符串的地址存到字符变量p中,该地址为字符串首字符的地址。
printf("%c\n", *p);  // p指向的是字符串首字符的地址,即'a'的地址,则解引用后,得到字符'a'; 输出:a
printf("%s\n", p);   // 把字符指针p所指向的字符串给打印出来, 输出:abcdef

2.1 常量字符串

  1. 常量字符串的内容是不能被修改的;
  2. 内存中只存在一份,多个字符指针指向相同的字符串,则这些指针中存放的地址相同;
  3. 将其赋值给字符指针时,是把其首字符的地址(其地址)赋值给(传给)字符指针。
    如下面例子:

    int main()
    {
    char* p = "abcdef"; // 该字符串是一个常量字符串,且把该字符串的首字符的地址放到字符指针p中
    *p = '1';
    printf("%s\n", p);   // 把字符指针p所指向的字符串给打印出来, 输出:abcdef
    return 0;
    }

    C语言指针的理解
    C语言指针的理解
    C语言指针的理解
    C语言指针的理解
    所以正确的写法:

    int main()
    {
    const char* p = "abcdef"; // 
    *p = '1';                          // 此时编译都过不去的。
    printf("%s\n", p);   // 把字符指针p所指向的字符串给打印出来, 输出:abcdef
    return 0;
    }

    例子2:

    int main()
    {
    // 对于常量字符串,C会把其存储到单独的一个内存区域,当多个指针同时指向同一个字符串时,它们指向的是同一块内存
    // 但是用相同的常量字符串去初始化不同的数组时,就会指向不同的内存块。
    char arr1[] = "hello world"; // 将字符串放到数组arr1中,不是常量字符串
    char arr2[] = "hello world"; // 
    const char* p1 = "hello world"; // 为常量字符串
    const char* p2 = "hello world"; // 为常量字符串,和上面的为同一个,为了节省内存空间,在内存中只储存一份
    
    if (arr1 == arr2)
        printf("arr1 == arr2\n"); 
    else
        printf("arr1 != arr2\n");// 执行
    if (p1 == p2)
        printf("p1 == p2\n");// 执行
    else
        printf("p1 != p2\n");
    printf("arr1 and arr2's address = %p and %p\n", arr1, arr2); // 
    printf("p1   and p2's   address = %p and %p\n", p1, p2);
    return 0;
    }

    C语言指针的理解
    注释:两个技术论坛网站:
    segmentfault.com(中国网站,段错误)
    stackoverflow.com(国外网站,栈溢出错误)

    三:指针数组

    指针数组:是指存放指针的数组,是一个数组,数组的元素是地址。
    如:

    int arr1[5] = {0}; // 整型数组,因为数组的元素是整型
    char arr2[5] = {0}; // 字符数组,因为数组的元素是字符类型
    int* arr3[3];           // 存放整型指针的数组----指针数组
    char* arr4[4];        // 存放字符指针的数组----指针数组

    理解指针数组的例子(但实际不是这么用指针数组的):

    int main()
    {
    int  a = 10;
    int  b = 20;
    int  c = 30;
    int* arr[3] = { &a, &b, &c }; // arr就是一个指针数组,且为一个整型指针数组,因为存放的是整型变量的地址。
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        printf("%d\t", *(arr[i]));// 10 20 30
    }
    return 0;
    }

    指针数组的应用:

    int main()
    {
    int arr1[] = { 1,2,3,4,5 };
    int arr2[] = { 6,7,8,9,10};
    int arr3[] = { 11,12,13,14,15 };
    int* parr[3] = { arr1, arr2, arr3 };
    int i, j;
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 5; j++)
        {
            // 以下三种写法都对:都可以一次打印每个数组的元素
            // 找到数组的每个元素:parr[i],其中每一个元素是一个数组(arr1..3)
            // +j表示找到指定数组中第j个元素的地址,在对其进行解引用,则找到对应的值
            //printf("%d\t", *(parr[i] + j)); // 得到第i行的地址后向后偏移j的地址,再进行解引用
    
            //printf("%d\t", parr[i][j]);
            printf("%d\t", *(*(parr+i)+j));
        }
        printf("\n");
    }
    return 0;
    }

    C语言指针的理解

四:数组指针

4.1 定义

数组指针:是一个指针,该指针指向的是一个数组

4.2 理解数组指针

int* p = NULL; // 整型指针 -- 指向整型的指针 --- 可以存放整型的地址
char* p1 = NULL; // 字符指针 -- 指向字符的指针 --- 可以存放字符的地址
// 数组指针 -- 指向数组的指针 --- 可以存放数组的地址
int arr[10] = {0}; // 定义并初始化一个含有10个元素的整型数组,数组名arr:是首元素地址(等同于&arr[0] <---> arr)。
&arr: 是数组的地址(也是数组首元素的地址),但是&arr 和 arr 和&arr[0]的地址相同,只是&arr + 1是跨过整个数组长度,另外两个地址+1是跨过数组中一个元素。
现在我们把&arr存起来,放到一个指针变量中,该指针变量就是数组指针。
即为:int(*p)[10] = &arr;
解释:由于的优先级低于[],而p又要是一个指针,因此,写作( * p),又因为是存放的是数组的地址,因此要加上[10],表示指向的是数组的地址,且数组中存放的元素的类型是int,因此(*p)前面再加上int。简而言之:p是一个指针,向后一看[10],说明指向的是一个包含10个元素的数组,且数组元素的类型为int。

4.3 指针数组和数组指针的区分

int* p1[10];     // 指针数组
int (*p2)[10];  // 数组指针

4.4 例子:指向char指针类型的数组的指针

char* arr[5];
char* (*p)[5] = &arr;// 因为存放的是数组的地址,因此需要用一个数组指针来接收该地址:(*p)[], 由于数组的元素个数为5且类型为char*,因此写作:char* (*p)[5] = &arr;
// 剖析:
// (*p):代表p是一个指针,p是指针变量的名字
//  [5]:代表p指向的数组是5个元素。
// char*:代表指向的数组的元素类型是char*

4.5 数组的地址和数组名的比较

int main()
{
    int arr[5] = { 1,2,3,4,5 };
    int(*p)[5] = &arr;
    printf("arr  = %p\n", arr); //数组名表示数组首元素的地址
    printf("&arr = %p\n", &arr); // 数组的地址
    printf("p    = %p\n", p);    // 存放数组地址的指针变量

    printf("arr  + 1 = %p\n", arr + 1); // 指向数组的下一个元素的地址
    printf("&arr + 1 = %p\n", &arr + 1); // 指向下一个数组的地址,即数组最后一个元素后面的地址
    printf("p    + 1 = %p\n", p + 1);    // 同上

    int i = 0;
    for (i = 0; i < 5; i++)
    {
        printf("%d ", (*p)[i]);
        printf("%d ", *(*p + i)); // *p是解引用得到arr,再加i表示得到第i个元素的地址,再解引用
        //printf("%d ", arr[i]);   //结果同上,依次打印数组中每个元素
    }
    return 0;
}

C语言指针的理解

4.6 数组指针主要用在二维及以上维度的数组

int arr[2][3] = {{1,2,3}, {4,5,6}};
对于二维数组arr,数组名(arr)也是首元素的地址,把二维数组看成一维数组,即每一行看做一个元素,即第一行是二维数组的第一个元素(首元素);
简而言之,对于二维数组,数组名是首元素的地址;首元素是第一行;第一行是一个二维数组。
使用:当把二维数组名作为实参进行传递时,且形参写成指针的形式,则需要一个指针来接收,因为数组名是首元素(第一行、一维数组)的地址,而数组的地址应该放到数组指针中。只不过这个数组指针指向的不是二维数组,而是指向第一行(一维数组的,其有3个元素,每个元素为int),故形参应该写成:int (*p)[3];
因为p指向的是一个数组,所以p+1是跳过一个数组的长度,即指向了第二行

4.7 数组指针作为函数参数

// 参数是数组的形式
void print1(int arr[2][3], int r, int c)
{
    int i, j;
    for (i = 0; i < r; i++)
    {
        for (j = 0; j < c; j++)
        {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }

}

// 参数是指针的形式
void print2(int (*p)[3], int r, int c)
{
    int i, j;
    for (i = 0; i < r; i++)
    {
        for (j = 0; j < c; j++)
        {
            // p + i:跳过i行,即指向了第i行
            // *(p + i):找到了第i行,即拿到了这一行的数组名
            // *(p + i) + j:指向了i行这个一维数组中第j个元素,对其进行解引用,则找到了第i行第j列的元素
            printf("%d ", *(*(p + i) + j)); // 同上
            printf("%d ", (*(p + i))[j]); // 这种方式也对前面找到一维数组的数组名
            printf("%d ", p[i][j]);       // 同上
            printf("%d ", *(p[i] + j));   // 同上
        }
        printf("\n");
    }

}

int main()
{
    int arr[2][3] = { 1,2,3,4,5,6 };
    print1(arr, 2, 3); // 形参为数组形式
    print2(arr, 2, 3); // 形参为数组指针形式
    return 0;
}

C语言指针的理解

4.8 指针和数组的区分

int arr1[5]; // arr1是一个包含5个元素的整型数组
int* arr2[5]; // arr2是一个包含5个元素的int数组,即指针数组
int
(*p)[5]; // p是一个指向包含5个int类型数组的指针, 即数组指针
int (*p1[10])[5]; // p1是一个数组,该数组有10个元素,每个元素的类型为:数组指针,该数组指针指向的元素有5个元素,每个元素是int类型。把数组名和方块去掉,剩下的就是元素的类型。

五:数组和指针作为函数参数

数组和指针可以作为函数的参数使用

5.1 一维数组作为函数参数

可以用数组和指针两种形式来接收参数

void fun1(int arr[]) { ; }    // 用数组进行接收,不指定数组大小
void fun2(int arr[5]) { ; }   // 用数组进行接收,指定数组大小,指定也不会用
void fun3(int *parr) { ; }   // 用指针进行接收,因为数组元素为int,所以指针类型为int
void pfun1(int *arr[]) { ; }
void pfun2(int *arr[5]) { ; }       // 同上,数组大小可以指定和不指定
void pfun3(int **parr) { ; }        // 数组元素为指针,故用二级指针来接收
int main()
{
    int arr1[5] = { 1,2,3,4,5 };
    int* arr2[5];
    fun1(arr1);
    fun2(arr1);
    fun3(arr1);
    pfun1(arr2);
    pfun2(arr2);
    pfun3(arr2);
    return 0;
}

5.2 二维数组作为函数参数

可以用数组和指针两种形式来接收参数

void fun1(int arr[2][3]) { ; }
void fun2(int arr[][3]) { ; } // 行数可以省略
//void fun3(int arr[][]) {;}    // 列数不可以省略, error
//void fun4(int arr[2][]) {;}   // error
void pfun1(int(*p)[3]) { ; }  // 用指针来接收
// 因为arr是数组名,就是首元素的地址,即第一行的地址
// 即一个一维数组的地址,即数组的地址,因此要用数组指针来接收
int main()
{
    int arr[2][3] = { 1,2,3,4,5,6 };
    fun1(arr);
    fun2(arr);
    //fun3(arr);
    //fun4(arr);
    pfun1(arr);
    return 0;
}

5.3 一级指针作为函数参数

    当参数为一级指针时,如果指向的是一个数组,可以用指针来接收,也可以用数组形式来接收。
void fun1(int *parr, int len)
{
    int i;
    for (i = 0; i < len; i++)
    {
        printf("%d ", *(parr + i)); // 1 2 3 4 5
        printf("%d ", parr[i]);     // 1 2 3 4 5
    }
}

void fun2(int parr[], int len)
{
    int i;
    for (i = 0; i < len; i++)
    {
        printf("%d ", parr[i]); // 1 2 3 4 5
        printf("%d ", *(parr + i)); // 1 2 3 4 5
    }
}

int main()
{
    int arr[5] = { 1,2,3,4,5 };
    int* p = arr;
    int len = sizeof(arr) / sizeof(arr[0]);
    fun1(p, len);
    fun2(p, len);
    return 0;
}

5.4 二级指针作为函数参数

    形参要写成二级指针的形式,如void fun1(int \*\*p) {;}
    实参要用指针变量的地址来进行传递,也可以用指针数组的数组名来进行传递。

六:函数指针

6.1 定义:

    指向函数的指针 -- 函数指针(指向数组的指针 -- 数组指针)

例如:

int sadd(int x, int y)
{
    return x + y;
}
void menu(char* str)
{
    printf("%s\n", str);
}

int main()
{
    printf("%d + %d = %d\n", 2, 3, 2 + 3);
    printf("%p\n", sadd);    // 函数名是函数的地址  
    printf("%p\n", &sadd);   // &函数名也是函数的地址,二者相同
    int(*psadd)(int, int) = &sadd;  // 定义一个函数指针,并将函数sadd的地址赋值给它
    int ssum = (*psadd)(2, 3); // 通过数组指针的形式调用函数
    printf("sum = %d\n", ssum);

    void (*pme)(char*) = &menu; // 指向返回类型为void,参数为char*的函数指针
    (*pme)("hello world");  // 先解引用得到函数,再进行调用
    pme("hello world");          // 直接用函数指针变量名代替指向的函数名也可以,即不用解引用
    (pme)("hello world");        // 去掉解引用可以,即去掉*
    (**pme)("hello world");     // 加两个*也是正确的,说明函数指针前面的*不起作用
    return 0;
}

C语言指针的理解

6.2 函数指针写法:

    返回类型 (\*函数指针变量名)(指向函数的参数列表)
    如上述sadd函数的函数指针写法:
    int (*psadd)(int , int ) = &sadd; // or = sadd;                 // 函数指针
    int (*parr)[5]               = &arr;     // int arr[5] = {0};         // 数组指针,
    **通过函数指针调用其指向的函数**:
    方法1:(*psadd)(2,3); // 通过指针形式来调用函数。
    方法2:(**psadd)(2,3);
    方法3:(psadd)(2,3);   // 方法1和方法3好理解,推荐使用
    方法4:psadd(2,3);

6.3 函数指针的理解

   下面代码来自:C陷阱和缺陷
   1.  (  * (void (*)()) 0   )();
   把0的类型强制转换为函数指针类型,该函数指针的参数为:无参,返回值为:void类型,然后对其进行解引用,拿到指向的函数后再进行调用(最后一个(),因为无参,所以()内没有参数)
         该语句其实是一次函数调用,调用0地址处的参数为无参,返回类型为void类型的那个函数。
    2. void (* func(int, void (*)(int))  )(int);
    这是一次函数声明
    该函数的函数名是func,其有两个参数(第一个为int类型,第二个为函数指针类型,其所指向的函数的参数为int类型,返回类型为void类型),其返回类型为(把函数名以及后面()以及内的参数类型去掉就是返回类型) void (*)(int),即返回类型也为函数指针类型,其所指向的函数的参数为int,返回类型为void类型。
    可以简化为以下两句代码:
    typedef void (*pfun)(int);
    pfun signal(int, pfun);

七:函数指针数组

7.1 定义

整型数组:是指存放的元素的类型为int的数组;如:int arr[10] = {0};
指针数组:是指存放的元素的类型为指针的数组;如:int* arr[10] = {0};
则:
函数指针数组:是指存放的元素类型为函数指针数组;
当想把多个函数的指针存到一起(参数和返回类型相同),则需要一个数组来接收,即需要用到函数指针数组。
如何定义一个函数指针数组?
函数指针:函数返回类型 (*pfunc)(参数列表); 如:int (*psadd)(int, int) = &sadd;
函数返回类型 (*parr[数组大小])(参数列表) = &arr; 如:int (*parr[4])(int, int) = {sadd, ssub, smul, sdiv};

7.2 使用场景

int sadd(int x, int y) { return x + y; }
int ssub(int x, int y) { return x - y; }
int smul(int x, int y) { return x * y; }
int sdiv(int x, int y) { return x / y; }
int main()
{
    int (*pfunc[4])(int, int) = {sadd, ssub, smul, sdiv};
    int i;
    for (i = 0; i < 4; i++)
    {
        printf("%d\t", pfunc[i](5,2));     // 两种方法都可以
        printf("%d\t", (*pfunc[i])(5, 2)); // 
    }
    return 0;
}

C语言指针的理解

7.3 应用-转移表

    和7.2类似
void menu()
{
    printf("***************************************\n");
    printf("*******     1. add     2. sub      ******\n");
    printf("*******     3. mul     4. div       ******\n");
    printf("*******     0. exit    ......           ******\n");
    printf("***************************************\n");
}
int sadd(int x, int y) { return x + y; }
int ssub(int x, int y) { return x - y; }
int smul(int x, int y) { return x * y; }
int sdiv(int x, int y) { return x / y; }

int main()
{
    int input, x, y, i;
    int (*pfarr[])(int, int) = { 0, sadd, ssub, smul, sdiv };
    int psz = sizeof(pfarr) / sizeof(pfarr[0]);
    printf("%d\n", psz);
    do
    {
        menu();
        printf("please select 0-%d:", psz - 1);
        scanf("%d", &input);
        if (0 == input)
        {
            printf("exit\n");
            break;
        }
        else if (input > 0 && input < psz)
        {
            printf("please input two number:x and y:");
            scanf("%d %d", &x, &y);
            printf("%d\n", pfarr[input](x, y));
        }
        else
        {
            printf("selection error, please renew input: 0-%d:\n", psz);
        }
    } while (input);
    return 0;
}

八:回调函数

8.1 定义

    1. 回调函数:首先是一个函数。
    2. 调用:是通过函数指针调用,而不是由其直接显式地调用,如:sadd(2,4);而是通过指向该函数的函数指针来调用。
    3. 实现过程:把函数的指针作为参数传递给另外一个函数,在该函数内部,通过函数指针的形式调用其所指向的函数(e.g. sadd),此时其就是回调函数。
    4. 特点:在满足一定条件时,用于对该事件进行的响应。

8.2 使用回调函数

int sadd(int x, int y) { return x + y; }
int ssub(int x, int y) { return x - y; }
int smul(int x, int y) { return x * y; }
int sdiv(int x, int y) { return x / y; }

void scalc(int (*pfunc)(int, int))
{
    int x, y;
    printf("please input two number:");
    scanf("%d %d", &x, &y);
    int ret = pfunc(x, y); // 是通过scalc函数传过来函数地址来调用其指向的函数。
    printf("%d\n", ret);
}
int main()
{
    int i;
    int (*parr[4])(int, int) = { sadd, ssub, smul, sdiv };
    for (i = 0; i < 4; i++)
    {
        scalc(parr[i]);
    }
    return 0;
}

C语言指针的理解

8.3 实现qsort函数排序

struct Student
{
    char name[20];
    int age;
};

void bubble_sort(int* parr, int len)
{
    int i, j, tmp, flags;
    for (i = 0; i < len - 1; i++)
    {
        flags = 1;
        for (j = 0; j < len - 1 - i; j++)
        {

            if (parr[j] > parr[j + 1])
            {
                tmp = parr[j];
                parr[j] = parr[j + 1];
                parr[j + 1] = tmp;
                flags = 0;
            }
        }
        if (1 == flags) {break;}
    }
}

int sort_int(const void* elem1, const void* elem2)
{
    return *((int*)elem1) - *((int*)elem2);
}

int sort_double(const void* elem1, const void* elem2)
{
    //if (*((double*)elem1) > *((double*)elem2)) // 同上
    if (*(double*)elem1 - *(double*)elem2 > 1e-6)
        return 1;
    else if (*(double*)elem1 - *(double*)elem2 < 0.0 - 1e-6)
        return -1;
    else
        return 0;
    //return *((double*)elem1) - *((double*)elem2);
}
int sort_stu_by_age(const void* elem1, const void* elem2)
{
    return ((struct Student*)elem1)->age - ((struct Student*)elem2)->age;
}
int sort_stu_by_name(const void* elem1, const void* elem2)
{
    return strcmp(((struct Student*)elem1)->name, ((struct Student*)elem2)->name);

}

void self_swap(char* buf1, char* buf2, int width)
{
    int i;
    char tmp;
    for (i = 0; i < width; i++)
    {
        tmp = *buf1;
        *buf1 = *buf2;
        *buf2 = tmp;
        buf1++;
        buf2++; 
    }
}
void self_qsort(void* base, int num, int width, int (*pfunc)(const void* elem1, const void* elem2))
{
    int i, j;
    int flags;
    for (i = 0; i < num - 1; i++)
    {
        flags = 1;
        for (j = 0; j < num - 1 - i; j++)
        {
            if (pfunc((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
            {
                flags = 0;
                self_swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
            }
        }
        if (1 == flags) {break;}
    }
}

int main()
{
    // void qsort(void *base, size_t num, size_t width, int(__cdecl *compare)(const void *elem1, const void *elem2))
    // base: start of target array: 目标数组的起始位置,写数组名即可
    // num:  array size of elements: 目标数组的元素个数,写数组长度
    // width: element size in bytes: 目标数组每个元素所占字节大小,可以用sizeof(数组名[0])
    // compare: comparison function: 要手动实现来调用的比较函数
    // elem1/2 :pointer to the key for the search: 要比较的两个元素的地址(指针)
    // void*类型的指针 可以接受任意类型的的地址
    //                 不能进行解引用操作
    //                 不能进行+-操作,因为不知道跨多少个字节 

    int i;
    // 生成不同类型的数据
    int arr1[] = { 19, 17, 14, 13, 10, 8, 5, 2, 1, 0 };
    int len1 = sizeof(arr1) / sizeof(arr1[0]);

    double arr2[] = { 9.9, 8.8, 7.7, 6.6, 5.5, 4.4, 3.3, 2.2, 1.1, 0.0 };
    int len2 = sizeof(arr2) / sizeof(arr2[0]);

    struct Student stu[] = { {"lilei", 18},{"hanmeimei", 14}, {"Luxi", 15} };
    int len3 = sizeof(stu) / sizeof(stu[0]);

    // 对整型数组进行冒泡排序
    // bubble_sort(arr1, len1) 

    // 用库函数stdlib.h qsort对不同类型数据进行排序
    // qsort(arr1, len1, sizeof(arr1[0]), sort_int);// 对整型进行排序
    // qsort(arr2, len2, sizeof(arr2[0]), sort_double);//对浮点型数据进行排序
    // qsort(stu, len3, sizeof(stu[0]), sort_stu_by_age);// 按照年龄排序
    // qsort(stu, len3, sizeof(stu[0]), sort_stu_by_name);// 按照姓名排序

    // 用自定义函数模拟qsort实现不同类型数据进行排序
    // 对整型进行排序
    self_qsort(arr1, len1, sizeof(arr1[0]), sort_int);
    for (i = 0; i < len1; i++) { printf("%d ", arr1[i]); }
    printf("\n\n");

    //对浮点型数据进行排序
    self_qsort(arr2, len2, sizeof(arr2[0]), sort_double);
    for (i = 0; i < len2; i++) { printf("%.2lf ", arr2[i]); }
    printf("\n\n");

    // 按照年龄排序
    self_qsort(stu, len3, sizeof(stu[0]), sort_stu_by_age);
    for (i = 0; i < len3; i++) { printf("%d %s;", stu[i].age, stu[i].name); }
    printf("\n\n");

    // 按照姓名排序
    self_qsort(stu, len3, sizeof(stu[0]), sort_stu_by_name);
    for (i = 0; i < len3; i++) { printf("%d %s;", stu[i].age, stu[i].name); }
    printf("\n\n");
    return 0;
}

C语言指针的理解

九:指向函数指针数组的指针

指向函数指针数组的指针:是一个指针,其指向的一个数组,数组的元素的类型为函数指针

int sadd(int, int);
int arr[6] = {0};
int (*p)[6] = &arr; // 数组指针,即数组的地址
int (*pfunc)(int, int) = sadd; // 函数指针,即函数的地址
int (*pfuncArr[4])(int, int) = {sadd, ssub, smul, sdiv}; // 函数指针数组,即存放函数指针的数组。
int (* (*ppfuncArr)[4]    )(int, int)= &pfuncArr; // 指向函数指针数组的指针
    // 1. 是一个数组指针
    // 2. 其指向的数组的元素个数为4个,大小对应于指向的函数指针数组的大小
    // 3. 指向的数组的每个元素的类型为函数指针
    // 4. 函数指针类型为:int (*)(int, int)

持续更新中...
请敬期待!