C——指针进阶

一、指针数组
二、数组指针
2.1应用
2.2指针和数组的定义说明
2.3数组参数、指针参数
三、函数指针
四、函数指针数组
五、指向函数指针数组的指针


一、指针数组

指针数组是数组,是一个存放指针的数组

例如:
int a[10]; 存放了10个整形变量的数组
char a[10]; 存放了10个字符变量的数组
int * p 为一个存放整形常量 地址的指针变量

类比理解

int * a[10]为一个存放了10个指针变量的数组
应用:

char* arr[5] = {"hello","world"};
printf("%s\n",arr[0]);

可以输出吗?
数组arr内部存储的为指针,即一个地址,存储了首字母h、w的地址.
输出结果为
这里写图片描述


二、数组指针

一个指向数组的指针

int * p一个指向整形变量的指针
double * p一个指向双精度浮点型变量的指针
类比理解
int(*p)[10]
解释:

p先和*结合,说明p是一个指针变量,然后指针指向的是一个大小为10个整 形的数组,所以p是一个指针,指向一个数组,叫数组执政
注意:[]的优先级高于* ,所以加上(),保证p先与* 结合 `

2.1应用

1. 理解数组的地址

    int arr[10] = { 0 };
    arr;//表示数组首元素的地址
    &arr;//表示数组的地址
    printf("%p\n", arr);     //数组中首元素的地址
    printf("%p\n", arr + 1);//为数组中第二的元素的地址,比第一个大四个字节(int)
    printf("%p\n", &arr + 1);//输出的为数组的10个元素的下一位元素地址

输出结果为
这里写图片描述

2.存储数组的地址

int arr[10] = {0};
int *p1 =&arr; 错误 ,定义类型为存放整形变量的指针不可存放数组的地址
int (*p) [1] = &arr; 正确 ,数组指针存放数组的地址

3.二维数组的传参

void test()
{
  int arr[3][5] = {0};
  print(arr);  //自定义函数,数组名为首元素的地址,二维数组的首元素为第一行的地址,相当于一个一维数组
}
//仅讨论给自定义函数print()传参
void print (int arr [3][5])    //√
{}
void print (int arr [][5])     //√  忽略行数,已知列数
{}
void print(int (*arr)[5])    //√  定义数组指针,接收数组第一行的地址
{}
void print (int **arr)         //× 该二级指针意为指向一个一级指针,该一级指针指向int类型的变量,等于将二位数组第一行的地址当成了一个二级指针,第一行的元素当成一个一极指针
{}

2.2指针和数组的定义、声明

我们已经知道:定义是不存在时让他存在,而声明是不知道时让他被知道
栗子:

1.普通情况
//test.c
char arr [] = "abcdef";
char* p = "abcdef";
//main.c
extern char arr[];
extern char* p;
int main()
{
    printf("%s\n",arr);
    printf("%s\n",p);   //输出什么?
    return 0;
}

这里写图片描述

2.数组定义,指针声明
//test.c
//数组的定义
char arr[] = "abcdef";//定义时,arr为数组名,单独出现表示为首元素的地址,即a的地址。
//main .c
extern char* arr; //在32位机器中,指针占4个字节
int main ()
{
printf("%s\n",arr);//输出什么?
return 0;          
}

这里写图片描述
程序挂掉
分析:
这里写图片描述

printf("%s\n",arr);中要输出字符串(%s),arr会被认为是一个地址,但此时其中内容为a b c d,但内容中找不到编号为a,b,c,d的内存,所以挂掉。
修正 办法printf("%s\n", (char *)&arr);
修正后结果:
这里写图片描述

3.指针定义,数组声明
//test.c
char *p = "abcdef";
//main .c
extern char p;
int main ()
{
   printf("%s\n",p);
   return 0;
}

这里写图片描述
生成随机数
分析:
这里写图片描述
修正办法printf("%s\n",*(char**)p);
printf("%s\n",(char*)*(int*)p);
修正后结果这里写图片描述

2.3数组参数、指针参数

1.一维数组传参

#include<stdio.h>
void test (int arr[])       //接收该存储整形的数组
{}
void test (int arr[10])    //接收该存储整形的数组
{}
void test (int * arr)      //接收首元素地址
{}
void test2 (int *arr[20])  //接收指针数组
{}
void test2 (int **arr)      //二级指针
{}
int main()
{
int arr[10] = {0};      //存储普通整形变量(int)的数组
int *arr2[20] = {0};    //存储指针(指向普通整形变量(int))的数组
test(arr);              //数组名单独存在,降级为首元素(一个整形变量)的地址
test2(arr2);            //数组名单独存在,降级为首元素(一个指向整形变量的指针)的地址
}

2.二维数组传参

#include<stdio.h>
void test(int arr[3][5])      //ok
{}
void test(int arr[][])        //no
{}
void test(int arr[][5])       //ok
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少个元素
void test(int * arr)        // no 整个第一行元素的地址,应存在数组指针里
{}
void test(int * arr[5])     //no 只能接收指向整形变量的指针
{}
void test(int (*arr)[5])    //ok 数组指针,可存放一维数组的地址
{}
void test(int **arr)        //只能存放一级指针,且一级指针指向普通整形
{}
int main ()
{
int arr[3][5] = {0};
test(arr);     //降级为首元素的地址,二维数组的首元素为整个第一行
}

3.一级指针传参

#include<stdio.h>
void print(int *p ,int sz)
{
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d\n",*(p+i));
    }
}
int main()
{
    int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    int *p = arr;
    int sz = sizeof(arr) / sizeof(arr[0]);
    print(p, sz);
    system("pause");
    return 0;
}

思考:
当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
比如:

void test1(int * p)
{}
/*test1 函数能接收什么参数?
三种
1.int *p
2.int a[]
3.&n   int n
*/
void test2(char * p)
{}
/*test2 函数能接收什么参数?
三种
1. char * p
2.char a[]
3.&n   char n
*/

4.二级指针传参

#include<stdio.h>
void test(int ** ptr)
{
    printf("num = %d\n", **ptr);
}
int main()
{
    int n = 10;
    int *p = &n;
    int **pp = &p;
    test(pp);
    test(&p);
    system("pause");
    return 0;
}

输出结果
这里写图片描述
思考:
当函数的参数为二级指针的时候,可以接收什么参数?

#include<stdio.h>
void test(int ** p)
{
}
int main()
{
    char c = 'b';
    char* pc = &c;
    char** ppc = &pc;
    char* arr[10];
    test(&pc);
    test(ppc);
    test(arr);
    system("pause");
    return 0;
}

三、函数指针

首先看一段代码

#include<stdio.h>
void test(int ** p)
{
    printf("hehe\n");
}
int main()
{
    printf("%p\n", test);
    printf("%p\n", &test);
    system("pause");
    return 0;
}

这里写图片描述
输出的是两个地址,这两个地址是 test 函数的地址。
那我们的函数的地址要想保存起来,怎么保存?
代码:

void test()
{
    printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void(*pfun1)();
void *pfun2(); 
//要存放地址,首先要求是指针,()的优先级高于*。pfun1可以存放

void (*pfun1)();
分析该函数指针,指向一个参数部分为(),返回类型为void的函数
实际上,void (**pfun1)();//*可取无限多个 void (pfun1)();
因为函数名本身就是一个地址

//代码一
(*(void(*)())0)();

分析:该代码段的意思为将0转换为(void()())该类型,该类型为一个指向“参数部分为(),返回值为void的函数”的函数指针,(void()())0,带解引用,为该函数指针所指向的函数,带(),意为调用这个函数。

//代码二
void(*signal(int ,void(*)(int)))(int);

分析:signal为函数指针名signal(int ,void()(int)),为一个名为signal的函数,它的参数为(int,void()(int)),第一个参数类型为int,第二个参数类型为void()(int),为一个函数指针,指向一个参数部分为(int),返回类型为void的函数。该函数外围的void( )(int),意为函数的返回值为一个函数指针,该函数指针指向一个“参数部分为(int),返回值void”的函数。

四、函数指针数组

int (*parr1[10])();

五、指向函数指针数组的指针

int (*(*parr1)[10])();
详情请看下回分解~

猜你喜欢

转载自blog.csdn.net/qq_24990383/article/details/80225547