初步了解指针(二)

时间:2021-01-31 14:19:17

数组指针
1、理解两种概念:
数组指针与指针数组

int arr[5];//数组
int *p[5];//指针数组
int (*p[5];//数组指针
int (*p[10][5];//存放指针数组的数组

数组指针(也称行指针):定义 int (*p)[n];

  • ()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
  • 如要将二维数组赋给一指针,应这样赋值:
    指针+1,加上其所指向类型的大小
int a[3][4] ={ {1,2,3,4},{4,5,6,7},{8,9,0,1} };
    int(*pi)[4] = a;
    int arr[4] = {1,2,0,8 };
    int(*p)[4] = arr;
    printf("%p\n", pi); //0115FBDC
    printf("%p\n", pi+1);//0115FBEC 二维指针发生降维,指针是指向一维数组的指针;
    printf("%d\n", **(pi + 1));//4
    printf("%p\n", p);//0115FBB8
    printf("%p\n", p+1);//0115FBC8  一维数组的数组指针;
    printf("%d\n",*(*(p+1)-1));//8

指针数组:定义 int *p[n];

  • []优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。

  • 执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]…p[n-1],而且它们分别是指针变量可以用来存放变量地址。

  • *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。

  • 将二维数组赋给一指针数组:

    int b[3][4] = { {1},{2},{3} };
    int *p1[3];
    int i = 0;
    for (; i < 3; i++) {
        p1[i] = b[i];
        printf("%d ", *p1[i]);//1 2 3
        printf("\n");
        printf("%p ", p1[i]);//0136F958(&b)  0136F968  0136F978
        printf("\n");
    }

2、储存数组地址

    int arr[10] = {1,2, 0 };
    int(*p)[10] = &arr;//数组指针
    printf("%p\n", p);   //001AFBEC(指向数组地址的指针)
    printf("%p\n", &arr);//001AFBEC(数组的地址)
    printf("%p\n", *p);  //001AFBEC(解引用之后的内容为&arr)
    printf("%d\n", **p);     //1(数组第一个元素的内容)

3、指针与数组的定义与声明
- 定义:(开辟空间)让不存在的存在;
- 声明:(不开辟空间)让不知道的知道;
- 声明其实就是定义的变量本身,因此,声明与定义必须保持一致。
数组参数与指针参数
1、一维数组传参:

int a[10] = {0};    //int a   int a[10]    int *a
int *b[20] = { 0 };// int *b  int *b[10]  int **b

2、二维数组传参:

int a[1][5] = { 0 };//int a[][5] int a[1][5] int (*a)[5]

3、一级指针传参:
参数为一级指针的情况:一维数组(类型区别),一维指针,

int main(){
int a[5]={2,3,4,5,1};
int *p=a;
print(p,2);//(int *p,int n)
}

4、二级指针传参:
参数为二级指针的情况:二级指针,指针数组,一级指针地址

int n=10;
int *p=&n;
int **pp=&p;
test(pp);// int **ppa

函数指针
1、函数指针就是函数的指针;(入口地址)
定义:void (*pfun)()

 char * (*fun1)(char * p1,char * p2);
 //fun1不是什么函数名,而是一个指针变量,它指向一个函数。这个函数有两个指针类型的参数,函数的返回值也是一个指针。
 //(改写之后为 char * (*)(char * p1,char * p2) fun1;)
 char * *fun2(char * p1,char * p2);
 //函数的返回值类型为char**,是个二级指针。
 char * fun3(char * p1,char * p2);
 //fun3是函数名,p1,p2是参数,其类型为char *型,函数的返回值为char *类型。
  • 在Visual C++6.0里,给函数指针赋值时,可以用&fun或直接用函数名fun。这是因为函数名被编译之后其实就是一个地址,所以这里两种用法没有本质的差别。
    两段有趣的代码
1(*(void(*)())0()//将0强转成返回值为void的函数指针,并且调用这个指针指向的函数。
 -  float (*h)()//h是返回值为float的函数指针。 - (float(*)())//类型强转符
 -  (*p)()//若p为函数指针,调用p所指向的函数 2、 void (*signal(int ,void(*)(int)))(int)
//返回值为void(*)(int)的函数指针

2、函数指针数组、函数指针的数组的指针

  • 函数指针数组: 存放函数地址的数组;

    • int (*p[10]) ()
    • 函数指针的用途:转移表;
  • 函数指针数组的指针:指向指针数组元素都是函数指针的指针

回调函数
一个通过函数指针调用的函数。其不是由该函数的实现方法直接调用,而是通过特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件的响应。
实例:模拟实现qsort(冒泡法)链接如下:(https://blog.csdn.net/fayfayfaydyt/article/details/80465345))

计算器实例

#pragma warning (disable:4996)
int add(int a,int b) {
    return a + b;
}
int mul(int a, int b) {
    return a * b;
}
int sub(int a, int b) {
    return a - b;
}
int dv(int a, int b) {
    if (b == 0) {
        printf("div zero!error!\n");
        return 0;        //分母为0;
    }
    return a / b;
}
void memu( ) {
    printf("******************************\n");
    printf("***** 1、add 3、sub *****\n");
    printf("***** 2、mul 4、div *****\n");
    printf("***** 0、quit *****\n");
    printf("******************************\n");

}
int main() 
{
    int x, y;
    int input = 0;
    int ret = 0;
    int (*p[5])(int ,int )= { 0,add,sub,mul,dv };//函数指针数组,p[0],p[1],p[2]...p[5];
    do {
        memu();
        printf("请选择\n");
        scanf("%d", &input);
        if ((input <= 4 && input>0))//判断输入是否满足循环
        {
                printf("输入操作数:");
                scanf("%d%d", &x, &y);
                ret = p[input](x, y);//调用函数p[input]
                printf("%d\n", ret);  
        }
            else if (input==0)
            {
                break;
            }
            else {
                printf("输入有误\n");
            }
    } while (1);    
    system("pause");
    return 0;
}

综上,指针就是地址,地址的地址叫二级指针,地址的数组叫指针数组,指向数组的指针叫数组指针,指向函数的指针叫函数指针。