C语言——深入了解指针

 

目录

 

1 —— 字符指针

2 —— 指针数组-数组指针

3 —— 数组传参和指针传参

4 —— 函数指针

5 —— 函数指针数组

6 —— 指向函数指针数组的指针

7 —— 回调函数

前言

1.指针是个变量,用来存放地址,地址是指向一块内存空间。

2.指针的大小是(32位平台)4个字节(64位平台)8个字节。

3.指针拥有类型,指针的类型决定了指针加减大小的长度。


 

  • 1 —— 字符指针

  • 在指针类型中有一种指针类型为char类型的指针为char*指针。
  • int main()
    {
        char ch = 'w';//创建一个char类型的ch变量存入字符w
        char* pc = &ch;//把ch的地址放到char类型的*pc指针里面去
        return 0;
    }
  • int main()
    {
        char arr[] = "abcdef";//字符数组
        char* pc = arr;//arr是数组名指向数组的地址放到指针*pc
        printf("%s\n",arr);//结果为abcdef
        printf("%s\n",pc);//结果为abcdef
        return 0;
    }
    int main()
    {
        char* p = "abcdef";//"abcdef"是一个常量字符串赋给*p是把首字符a的地址赋给*p而不是整个字符串
        printf("%c\n",*p);//结果为a
        printf("%s\n",p);//结果为abcdef是从a的地址开始向后打印字符串
        //"abcdef"为常量字符串,内容是不可被更改的
        //*p = 'w';为错误导致程序崩溃Segmentation fault(段错误)
        return 0;
    }
    int main()
    {
        char arr1[] = "abcdef";
        char arr2[] = "abcdef";//arr1,arr2地址不同指向的空间不同
        char* p1 = "abcdef";
        char* p2 = "abcdef";//p1,p2地址不同,但指向的空间相同都是"abcdef"改变p1并不影响p2
        return 0;
    }
  • 2 —— 指针数组-数组指针

  • 指针数组是数组,如一个数组里面存放{1,2,3,4,5,6,7,8,9};这是一个数组存放九个整形是整形数组,而指针数组是一个数组里面存放的是指针。
    int main()
    {
        int* parr[10];//是存放整形指针的数组,存放10个元素,每个元素是int*类型 - 指针数组
        char* pch[10];//存放字符指针的数组,存放10个元素,每个元素是char*类型 - 指针数组
        return 0;
    }

    指针数组的用法:

    int main()
    {
    	int a = 10;
    	int b = 20;
    	int c = 30;
    	int* parr[3] = { &a, &b, &c };
    	for (int i = 0; i<3; i++)
    	{
    		printf("%d\n", *(parr[i]));
    	}
        //输入结果为10 20 30
    	int arr1[] = { 1, 2, 3, 4, 5 };
    	int arr2[] = { 2, 3, 4, 5, 6 };
    	int arr3[] = { 3, 4, 5, 6, 7 };
    	int* parr1[] = { arr1, arr2, arr3 };
    	for (int i = 0; i<3; i++)
    	{
    		for (int j = 0; j<5; j++)
    		{
    			printf("%d ", *(parr1[i] + j));
                //parr1[i]是下标为i的数组+j是下标增加j//找到下标为i数组的第j个元素
    		}
    		printf("\n");//换行
    	}
        //结果为1 2 3 4 5
        //     2 3 4 5 6
        //     3 4 5 6 7
        return 0;
    }

    数组指针是一个指针,如char ch = 'w'; 而char* ps=&ch;是一个*ps的指针指向了ch的地址,而数组指针同理,是一个指针指向数组的。

    int main()
    {
        int arr[10]={1,2,3,4,5,6,7,8,9,10};//是一个整形数组
        int (*parr)[10] = &arr;//(*parr)是一个指针,指向了有[10]个元素的数组的地址每个元素是int
        //把arr的地址用*parr指针来接收
        return 0;
    }
    int main()//把一个指针数组存入数组指针
    {
        int* arr[5] = {0};
        int* (*parr)[5] = &arr;//(*parr)是一个指针,指向一个数组有[5]个元素每个元素是int*类型
        return 0;
    }

    注意:int arr[10];     比如&arr表示数组的地址,不是首元素的地址。

  • 数组的地址+1表示跳过整个数组的大小,所以&arr+1相对于&arr相差40

  • 数组指针的用法:

    void Show(int(*p)[5], int x, int y)//用数组指针传参*p是指针指向第一行数组有[5]个元素每个元素是int
    {
    	for (int i = 0; i < x; i++)//打印一行
    	{
    		for (int j = 0; j < y; j++)//打印一行的第j的元素
    		{
    			printf("%d ", *(*(p + i)+j));//*(*(p+i)+j)难理解的话用p[i][j]效果一样
                //*(p+i)是找到这一行+j是第j个元素地址在*号解引用找到第j个元素
    		}
    		printf("\n");
    	}
    }
    int main()
    {
    	int arr[3][5] = { { 1, 2, 3, 4, 5 }, { 2, 3, 4, 5, 6 }, { 3, 4, 5, 6, 7 } };//二维数组
    	Show(arr, 3, 5);
    	//自定义一个函数把二维数组的每行每列打印出来,arr是首元素地址代表第一个元素,就是第一行的地址
    	//二维数组的首元素地址就是第一行的地址
    	return 0;
    }
  • 3 —— 数组传参和指针传参

  • 写代码的时候要把数组或者指针传给函数。
  • 一维数组传参
    void test(int arr[])//没问题
    {}
    void test(int arr[10])//没问题元素个数写不写都可以
    {}
    void test(int *arr)//没问题用地址来接受数组的地址写成指针也可以
    {}
    void test2(int *arr2[20])//没问题arr2和[20]先结合是数组每个元素是int*(元素个数可以省略)
    {}
    void test2(int* *arr)//没问题
    {}
    int main()
    {
        int arr[10];
        int* arr2[10];
        test(arr);
        test2(arr2);
        return 0;
    }

    二维数组传参

    void test(int arr[3][5])//传过来一个数组用数组接受!!行[3]可以省略但是列[5]不能省列
    {}
    void test(int arr[][5])//省略行的写法
    {}
    void test(int (*arr)[5])//*arr是个指针指向了一个数组有五个元素每个元素是int这是指向一行的元素
    {}
    int main()
    {
        int arr[3][5]={0};
        test(arr);
        return 0;
    }

    指针传参

    void test(int* p)//用指针接收
    {}
    int main()
    {
        int a=10;
        int *p = &a;
        test(p);//ok
        test(&a);//ok
        return 0;
    }

    二级指针传参

    void test(int** ptr)//二级指针来接收
    {}
    int main()
    {
        int a=10;
        int *p=&a;
        int **ps=&p;
        int* arr[10]={0};
        test(ps);//ok
        test(&p);//ok
        test(arr);//ok
        return 0;
    }
  • 4 —— 函数指针

  • 如数组指针是指向数组的指针,那函数指针就是指向函数的指针。
    int Add(int x,int y)
    {
        return x+y;
    }
    int main()
    {
        int a=10;
        int b=20;
        Add(a,b);//&Add和Add都是函数的地址
        int(*pa)(int,int)=Add;
        //*pa是个指向函数的指针(int,int)是函数的参数类型写成int x int y都可以返回类型是int
        (*pa)(a,b);//函数指针的用法*pa是对pa解引用找到指向的函数传参数(a,b)和Add(a,b)结果一样
        return 0;
    }

    无限套娃

    (*(void (*)())0)();
    //一步一步了解
    //void (*)()//(*)是一个函数指针参数()无参返回类型是void ——函数指针
    //然后(*  ()   0)()//0前面是个()里面放着一个函数指针就是把0强制类型转换成一个函数指针把0当成一个函数的地址0就是一个函数的地址
    //然后*0就是对0进行解引用操作,使用0指向的这个函数最后一个()是函数的参数(无参)
    
    
    void (*arr(int,void(*)(int)))(int);
    //arr是一个函数名,后面(int,viod(*)(int))是参数一个是int一个是(*)是函数指针参数是(int)返回类型是viod
    //然后void (*)(int)参数是int返回类型是void
    //所以arr这是一个函数指针指向的函数返回类型是一个函数指针void (*)(int)
  • 5 —— 函数指针数组

  • 函数指针数组是一个数组用来存放一个或多个函数指针,上代码:
    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(*p[4])(int, int) = { Add, Sub, Mul, Div };
    	//p先和[4]结合是个数组,int(*)(int,int)是元素类型每个元素是函数指针
    	//函数指针数组的用法:
    	int a = 10;
    	int b = 20;
    	for (int i = 0; i < 4; i++)
    	{
    		int c = p[i](a, b);//p[i]找到函数指针数组的第i个元素因为函数原来就是个地址不需要解引用然后(a,b)进行传参再用int c来接收返回值
    		printf("%d\n", c);//打印整数的结果为30 -10 200 0
    	}
    	return 0;
    }
  • 用函数指针数组写一个简单的计算器:

    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;
    }
    void menu()
    {
    	printf("————————————————");
    	printf("——1.加法————2.减法————");
    	printf("——3.乘法————4.除法————");
    	printf("——0.退出———————————");
    
    }
    int main()
    {
    	int input = 0;
    	int a = 0;
    	int b = 0;
    	menu();//打印一个计算器的菜单
    	do
    	{
    		printf("请输入:");
    		scanf("%d", &input);//选择函数
    		switch (input)//使用函数input为0则结束switch
    		{
    		case 1:
    			printf("请输入两个数进行操作:");
    			scanf("%d%d", &a, &b);
    			printf("%d ", Add(a, b));
    			break;
    		case 2:
    			printf("请输入两个数进行操作:");
    			scanf("%d%d", &a, &b);
    			printf("%d ", Sub(a, b));
    			break;
    		case 3:
    			printf("请输入两个数进行操作:");
    			scanf("%d%d", &a, &b);
    			printf("%d ", Mul(a, b));
    			break;
    		case 4:
    			printf("请输入两个数进行操作:");
    			scanf("%d%d", &a, &b);
    			printf("%d ", Div(a, b));
    			break;
    		case 0:
    			printf("退出计算器");
    			break;
    		default://默认输入不是switch里面的值为错误
    			printf("输入错误");
    			break;
    		}
    	} while (input);
    	return 0;
    }

     

  • 6 —— 指向函数指针数组的指针

  • 指向函数指针数组的指针顾名思义是个指针指向了一个函数指针的数组
    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(*parr[4])(int, int) = { Add, Sub, Mul, Div };//是一个数组-函数指针的数组,指向函数指针数组的指针是要有一个指针能够指向这个数组
    	int(*(*psarr)[4])(int, int) = &parr;//*psarr是一个指针,指向[4]数组有四个元素每个元素是int(*)(int,int)函数指针
    	return 0;
    }

     

  • 7 —— 回调函数

  • 回调函数就是一个通过函数指针调用的函数,如果拿到一个函数的地址就可以调用那个函数
  • 回调函数用法:
    void Show(char* str)//Show函数的创建用来打印字符串
    {
    	printf("%s\n", str);//将abcdef穿进来用指针str接收%s打印出字符串
    }
    void test(void (*ps)(char*))//参数是函数指针
    {
    	ps("abcdef");//ps是个函数指针指向Show所以直接调用函数名(参数)即可
    }
    int main()
    {
    	test(Show);//用test函数调用Show函数,Show是个地址用来找到函数本体
    	return 0;
    }

     

  • 用回调函数优化过的通讯录:
  • 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;
    }
    void menu()
    {
    	printf("————————————————");
    	printf("——1.加法————2.减法————");
    	printf("——3.乘法————4.除法————");
    	printf("——0.退出———————————");
    
    }
    void Calc(int(*ps)(int, int))//函数参数传一个函数指针
    {
    	int x = 0;
    	int y = 0;
    	printf("请输入两个数进行操作:");
    	scanf("%d%d", &x, &y);
    	printf("%d ", ps(x, y));//调用函数指针指向的函数
    }
    int main()
    {
    	int input = 0;
    	menu();//打印一个计算器的菜单
    	do
    	{
    		printf("请输入:");
    		scanf("%d", &input);//选择函数
    		switch (input)//使用函数input为0则结束switch
    		{
    		case 1:
    			Calc(Add);//使用回调函数来调用函数可以解决代码冗余的问题——方便多了
    			break;
    		case 2:
    			Calc(Sub);
    			break;
    		case 3:
    			Calc(Mul);
    			break;
    		case 4:
    			Calc(Div);
    			break;
    		case 0:
    			printf("退出计算器");
    			break;
    		default://默认输入不是switch里面的值为错误
    			printf("输入错误");
    			break;
    		}
    	} while (input);
    	return 0;
    }

     

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值