C 对指针数组、数组指针 和 函数指针、函数指针数组、指向函数指针数组的指针的理解

0.指针和数组的定义与声明(说明指针和数组是不一样的)

//text.c
char arr[] = "abcdef"; //在text.c文件中定义数组
//main.c
ertern char *arr; // 调用其他源文件中的参数时要用ertern
int main()
{
    printf("%s\n", arr);
    return 0;
}
执行后程序崩溃;text.c中的arr[]占用了7个字节,main.c中的arr是个指针,所以只占了四
个字节,而指针中存放的是地址,所以就会误把字符''abcd''当做一个地址,去找这个地址所代
表的空间,然后输出这块空间上的字符串,但由于字符''abcd''所对应的ASCII转换成的地址无
法去访问,所以最后程序访问出错,直接崩掉

这里写图片描述

若想让输出的结果为正确的,则需要把paintf中的arr改为(char *)&arr,取出arr中的地址,
但因为arr原本就是char *类型的,再加上个&,就变成了char **类型,所以需要强制转换为
char *类型,也就是拿到了a的地址,然后在打印,结果就会是abcdef

//text.c
char *p = "abcdef";//在text.c文件中定义指针
//main.c
ertern p[];
int main()
{
    printf("%s\n", p);
    return 0;
}
执行结果为随机值;text.c中的p存放的是字符串"abcdef"'a'的地址,占4个字节,
main.c中的p[](未指定大小,所以最多为4个字节)则误把text.c中的这个地址当做字符串,
把没地址分成四份存起来,在printf中的p是首元素地址,也就是前1/4元素的值被转换成字符
形式输出来

这里写图片描述

若想输出为正确的值,则需要把printf中的p改成(char *)*(int *)p,因为p中存放的是a的
地址的首地址,先把p强制转化为int *形式(取出p中的4个字节)再解引用,得到八位十六进制
数,即为a的地址,因为要打印出字符串,所以要再强制转化为char *类型
或
把p改成*(char **)p,先把p当做是char *类型的,然后对char *类型的p取地址,就是
char **类型,再解引用,因为char **类型解引用取出的是四个字符,正好取到p中a的地址,
所以打印时刚好打出字符串"abcdef"

声明与定义指向的是同一块空间,但因为在链接是只查找参数名,所以链接时不会出现问题

1.指针数组

指针数组强调的是数组,所以它是一个数组,用来存放指针
例如:

int* arr[10] 存放整形指针的数组,数组包含10个元素;

char* arr[10] 存放字符形指针的数组,数组包含10个元素;

char** arr[10] 存放字符型二级指针的数组,数组包含10个元素。

这里写图片描述

2.数组指针

与上面相反,数组指针强调指针,所以他是个指针,指向一个数组
例如:

int arr[10] = {0};
int (*p) [10] = &arr;(因为[]的优先级比*高,所以要给*p加上(), 
否则这个表达式就是前面提到的指针数组)

void text(int (*p) [5])
{
    ...
}
int main()
{
    int arr[3][5] = {123456789101112131415};
    text(arr);
    return 0;
}

因为在main函数中传过去的arr是首元素地址,在二维数组中,首元素地址是第一行的地址,第一行则可以看成一个一维数组,数组中包含5个元素,这个数组的地址就是 int (*p) [5]
这里写图片描述

3.函数指针

指向函数的指针变量
例:

void text()
{
    printf("hello world\n");
}

int main()
{
    void (*p)() = text;//这里的text与&text作用相同,都表示函数的地址
    (*p)();
    p();
    return 0;
}

这里写图片描述

从结果可以看出,(*p)()与p()的调用结果是相同的,因为C语言设计者为了方便初学者的理解
,所以可以写成(*p)()的形式,但要注意如果有*,就一定要(),否则就会出现错误,因这样写
最后会把函数的返回值解引用

4.函数指针数组

是一个数组,里面存放许多指向函数的指针

例如:

int (*p[10])(int);
[]的优先级高于*,所以p先和[]结合,说明这是一个数组,去掉名称看类型,这是一个函数指针
,所以是函数指针数组;包含函数返回值类型为int,函数参数类型也为int10个函数指针

转移表:
#include <stdio.h>

void menu()
{
    printf("*************************\n");
    printf("****  1.Add   2.Sub  ****\n");
    printf("****  3.Mul   4.Div  ****\n");
    printf("****      0.exit     ****\n");
    printf("*************************\n");
}

int Add(int x, int y)
{
    return x + y;
}

int Sub(int x, int y)
{
    return x - y;
}

int Mul(int x, int y)
{
    return x * y;
}

int Div(int x, int y)
{
    return x / y;
}

int main()
{
    int x = 0;
    int y = 0;
    int ret = 0;
    int input = 0;
    int(*p[5])(int, int) = { 0, Add, Sub, Mul, Div };

    do
    {
        menu();

        printf("please choose:");
        scanf("%d", &input);

        if ((input >= 1) && (input <= 4))
        {
            printf("please input two numbers:");
            scanf("%d %d", &x, &y);
            printf("%d\n", ret = p[input](x, y));
        }

    } while (input);

    return 0;
}

这里写图片描述
这里写图片描述

当参数类型与返回类型相同时才可以使用函数指针数组,函数指针数组可以减少代码的数量,
以上代码若不用函数指针数组,用最基本的switch case语句,需要逐条列出来,代码长度
会大幅增加 

5.指向函数指针数组的指针

是一个指向数组的指针,数组中的元素都是函数指针

例如(借用函数指针数组的例子):

void (*a)(int, int) = Add;//指向函数Add的指针a;

这里写图片描述

void (*count[4])(int, int) = { Add, Sub, Mul, Div };
//count为函数指针数组,里面包含的元素为函数指针a,s,m,d;

这里写图片描述

void (*(*p)[4])(int, int) = &count;//p为指向函数指针数组count的指针;

这里写图片描述

猜你喜欢

转载自blog.csdn.net/K_A_Irving/article/details/80217855