0


【C语言】深度剖析指针和数组的关系


一、数组名和&数组名

区别:数组名表示首元素的地址,地址加1表示跳过一个数据类型的大小。

&数组名表示取出的是整个数组的地址,地址加1表示跳过一个数组的长度。

举例:arr[]需要使用整型(等)指针来指向,而&arr[]需要使用数组指针来指向。

数组名通常表示首元素的地址,但有两个例外:

1、sizeof(数组名),这里的数组名表示数组首元素的地址。

2、&数组名,表示取出的是这个数组。

二、字符指针

1、指向字符数组首元素的字符指针 char* p=arr

#include <stdio.h>
int main()
{
    char arr[5] = "abc";
    char* p = arr;
    printf("%s", p);//打印abc
    return 0;
}

数组名是首元素的地址,这里指针p指向的是arr数组的首元素'a'的地址,可以通过指针p,以%s的形式打印字符串。

2、指向常量字符串的字符指针 const char* p="abc"

#include <stdio.h>
int main()
{
    const char* p = "abc";//字符串abc为常量字符串
    printf("%s", p);
    return 0;
}

因为"abc"是常量字符串,不能被修改,所以指针p需要使用const修饰。若没有const,.c文件运行时程序会直接挂掉,.cpp文件更加严格,会直接报错。

三、指针数组 int* p[3]={arr1,arr2,arr3}

指针数组本质上是数组,是存放指针的数组。

#include <stdio.h>
int main()
{
    int arr1[] = { 1,2,3,4,5 };
    int arr2[] = { 2,3,4,5,6 };
    int arr3[] = { 3,7,5,8,99 };
    int* p[3] = { arr1,arr2,arr3 };
    for (int i = 0;i < 3;i++)
    {
        for (int j = 0;j < 5;j++)
        {
            //printf("%d ", p[i][j]);
            //printf("%d ", *(*(p+i) + j));//p[i]可以写成*(p+i)
            printf("%d ",*(p[i]+j));//解释如下图
        }
        printf("\n");
    }
    return 0;
}

三种指针数组的打印方式都是可以的,这里的代码类似二维数组,该数组首元素是第一行。详细解析如下:

四、数组指针 int(*p)[10]=&arr

数组指针是指向数组的指针。

int(p)[10]=&arr,数组指针p的类型是int()[10](去掉p,即为类型)。这里指针+1跳过一个数组大小。

#include <stdio.h>
print(int(*p)[5], int r, int c)//接收的是指向一维数组的数组指针
{
    for (int i = 0; i < r;i++)
    {
        for (int j = 0; j < c; j++)
        {
            //printf("%d ", p[i][j]);
            //printf("%d ", *(p[i]+j));
            printf("%d ", *(*(p+i)+j));
        }
        printf("\n");
    }
}
int main()
{
    int arr[3][5] = { 1,5,4,6,7,9,8,4,2,5,4,5,4,5,6 };
    print(arr, 3, 5);//传过去的是二维数组
    return 0;
}

打印方式的理解与上一个例子相同,注意二维数组的首元素是第一行。

再来看一个例子:int(p[10])[5],这是一个存放数组指针的数组,数组元素的类型是int()[5]。

五、数组传参

1、一维数组传参

void test(int arr[])//一维数组传参,数组接收
{}
void test(int arr[10])//一维数组传参,数组接收,10可有可无
{}
void test(int* arr)//一维数组传参,指针接收
{}
int main()
{
    int arr[10] = { 0 };
    test(arr);
}
void test2(int* arr[20])//指针数组传参,指针数组接收,20可有可无
{}
void test2(int** arr)//传过来的是一级指针的指针,当然可以用二级指针接收
{}
int main()
{
    int* arr2[20] = { 0 };
    test2(arr2);
}

2、二维数组传参

void test(int arr[3][5])//二维数组传参,二维数组接收
{}
void test(int arr[][])//错误,行不能省略
{}
void test(int arr[][5])//二维数组传参,二维数组接收
{}
void test(int *arr)//错误,传过来的是第一行的地址,相当于一维数组的地址,不能用一级指针接收
{}
void test(int* arr[5])//错误,不能用指针数组接收
{}
void test(int (*arr)[5])//可以用数组指针接收
{}
void test(int **arr)//错误,传过来的是一维数组的指针,不能用二级指针接收,二级指针是指向一级指针的指针
{}
int main()
{
 int arr[3][5] = {0};
 test(arr);//传过来的相当于第一行的地址
}

六、指针传参

1、一级指针传参

void text(int* p)
{}
int main()
{
    int a = 0;
    int* pa = &a;
    int arr[10] = { 0 };
    text(&a);//可以传整型变量的地址
    text(pa);//可以传一级指针的地址
    text(arr);//可以传整型数组的地址
    return 0;
}

只要传过来的本质是一级指针,就可以用一级指针接收。

2、二级指针传参

void text(int** p)
{}
int main()
{
    int* p1 = NULL;
    int** p2 = NULL;
    int* arr[10];
    text(&p1);//可以传一级指针的地址
    text(p2);//可以传二级指针
    text(arr);//可以传指针数组
    return 0;
}

七、函数指针

对于函数名与&函数名,它们的地址是一样的,且本质上没有任何区别。

使用函数值指针进行函数的调用,可以把p当成函数名调用,解引用无意义。

1、两个代码的理解

( *( void (*)() )0 )();

以上代码是一次函数调用,先将0强制类型转换为函数指针类型,该函数指针无参,返回值为void。由于函数指针解引用无意义,所以这里的这颗*可有可无。整个代码的意思是对0地址处的函数进行一次函数调用。

void ( *signal( int , void(*)(int) ) )(int);

以上代码是一次函数声明。

signal是函数名,它的参数是int和函数指针void(*)(int),返回类型是函数指针void(*)(int)。

2、函数指针重命名

typedef void(*pf_t)(int);
pf_t signal(int,pf_t);

把void(*)(int)类型重命名为pf_t。

上方函数可以用pf_t来代替void(*)(int)。

3、函数指针的使用——回调函数

int Add(int a, int b)
{}
int Sub(int a,int b)
{}
int Mul(int a, int b)
{}
int Div(int a, int b)
{}
int calc(int(*p)(int,int))
{
    return p(1, 2);
}
int main()
{
    calc(Add);
    return 0;
}

当有多个参数和返回类型均相同的函数时,可以使用函数指针对这些函数进行按需调用,减少代码冗余。

八、函数指针数组——用途:转移表

函数指针数组是存放函数指针的数组。

int Add(int a, int b)
{}
int Sub(int a,int b)
{}
int Mul(int a, int b)
{}
int Div(int a, int b)
{}
int main()
{
    int input,x,y;
    scanf("%d%d%d",&input,&x,&y);
    //int(*p)(int, int) = Add;
    int(*arr[4])(int,int) = {Add,Sub,Mul,Div};//数组arr的类型直接将函数指针的p替换即可
    int ret=arr[input](x,y);//使用函数指针数组对函数进行调用——转移表
    for (int i = 0; i < 4; i++)//函数指针数组的遍历调用
    {
        int ret = arr[i](8, 4);
    }
    return 0;
}

可以使用函数指针数组对相同形参、返回类型的函数进行调用,在实际使用中实现函数的跳转功能,所以被称为转移表,有较大的使用价值。

九、函数指针数组指针

int main()
{
    int(*p)(int, int) = Add;//p是函数指针
    int(*arr[4])(int,int) = {Add,Sub,Mul,Div};//arr是函数指针数组
    int(*(*parr))(int,int)=&arr//parr是函数指针数组指针
    return 0;
}

写法可以按照上一级指针/数组类型模仿写。


本文转载自: https://blog.csdn.net/gfdxx/article/details/125128344
版权归原作者 蒋灵瑜的流水账 所有, 如有侵权,请联系我们删除。

“【C语言】深度剖析指针和数组的关系”的评论:

还没有评论