C语言指针进阶

前言

我是在B站鹏哥C语言那自学的C,每篇博客相当于对自己上课内容的复习,希望自己在大学这段时间多写博客,做到至少一周两更,如果内容有勘误欢迎指正,大家一起加油。

字符指针

字符指针除了指向字符还可以指向字符串

int main()
{
	//一般使用
	char a = 'a';
	char * pa = &a;
	//指向字符串
	char * ch = "hello world";//把"hello world"的首字符地址存储在指针变量ch
	char arr[] = "hello world";//将整个字符串放在arr数组中
	
	printf("%c\n",*ch);//打印首元素
	printf("%s\n",ch);//打印ch字符串
	printf("%s\n",arr);//打印arr数组
	return 0;
}

我们来看一下下面这个代码

int main()
{
	char str1[] = "hello world";
	char str2[] = "hello world";
	const char* str3 = "hello world";
	const char* str4 = "hello world";

	if(str1==str2)
		printf("str1 & str2 are same\n");
	else
		printf("str1 & str2 are not same\n");

	if(str3==str4)
		printf("str3 & str4 are same\n");
	else
		printf("str3 & str4 are not same\n");
	return 0;
}

结果如下:
在这里插入图片描述

在这里插入图片描述

指针数组

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

int main()
{
	int a[5] = {1,2,3,4,5};
	int b[] = {6,7,8,9,10};
	int c[] = {0,2,4,6,8};
	int *arr[3] = {a,b,c};//定义一个指针数组{a,b,c}都是首元素地址
	int i = 0;
	for(i = 0;i<3;i++)
	{
		int j = 0;
		for(j = 0;j<5;j++)
		{
			// printf("%d ",*(arr[i]+j));
			printf("%d ",arr[i][j]);//这两种打印方法结果相同
		}
		printf("\n");
	}
	return 0;
}

结果如下:
在这里插入图片描述

数组指针

定义

数组指针是指向数组的指针,其本质为指针

int main()
{
	int arr[10] = {1,2,3,4,5};
	int (*parr)[10] = &arr;//取出的是数组的地址
	//parr就是一个数组指针
	//arr - 数组名是首元素地址 - arr[0]的地址
	printf("%p\n", arr);//取出首元素地址
	printf("%p\n", arr+1);//取出第二个元素的地址(跳过4个字节)

	printf("%p\n", &arr);//取出数组的地址
	printf("%p\n", &arr+1);//取出下一个数组的地址(跳过40个字节)
	return 0;
}

结果如下:
内存地址用16位方式存储:(E18)H-(DF0)H= (40)D
在这里插入图片描述

数组指针的简单应用

void print1(int (*arr)[5],int row,int col)//定义一个函数(变量是二维数组第一行地址,行,列)
{
	int i = 0;
	for(i = 0;i<row;i++)
	{
		int j = 0;
		for(j = 0;j<col;j++)
		{
			printf("%d ",arr[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = {{1,2,3,4,5},{2,3,4,5,6},{4,5,6,7,8}};
	print1(arr,3,5);
	//数组名arr,表示首元素地址
	//二维数组里的首元素是二维数组的第一行
	//这里传递的arr,相当于第一行的地址,是一维数组的地址
	//可以用数组指针来接收
	getchar();
	return 0;
}

结果如下:
在这里插入图片描述

小回顾

int arr[5];//一个整型数组
int *parr[5];//一个整型指针parr的数组
int (*parr2)[5];//一个数组parr2的指针,数组有5个元素,整型类型
int (*parr3[10])[5];//parr3是一个存放数组指针的数组,有10个数组指针,每个数组指针指向一个数组,这个数组有5个元素,都为整型类型

提醒

数组名是首元素地址但有两个例外:
1.&数组名 - 数组名表示整个数组, 取出的是整个数组的地址
2.sizeof(数组名) - 数组名表示整个数组,计算的是整个数组的大小,单位是字节

数组参数、指针参数

一维数组传参

//一维数组传参
void test(int arr[])
{}//1.传入一个数组
void test(int arr[10])
{}//2.传入一个数组,arr[10]里面的10没有意义。
void test(int *arr)
{}//3.传入一个整型指针
void test(int *arr[20])
{}//4.
void test(int **arr)
{}//5.二级指针,**arr==&arr[]
int main()
{
	int arr[10] = {0};//数组
	int *arr2[20] = {0};//整形指针数组,存放int*
	test(arr);//1,2,3都可以
	test2(arr2);//3,4可以
}

二维数组传参

void test(int arr[3][5])
{}//1.可以
void test(int arr[][])
{}//2.不行,只能省略行
void test(int arr[][])
{}//3.可以
//总结:函数形参的设计只能省略第一个[]的数字
//因为对一个二维数组,可不知道有多少行,但是必须知道一行多少元素
void test(int *arr)
{}//3.不行,传的是第一行低的值,是一维数组的地址
void test(int *arr[5])
{}//4.不行,这是指针数组
void test(int (*arr)[5])
{}//5.可以,指向5个元素的数组
void test(int **arr)
{}//6.不行,传的第一行一维数组的地址
int main()
{
	int arr[3][5] = {0};
	test(arr);
}

一级指针传参

void print(int*p, int size)//自定义打印函数
{
    int i = 0;
    for(i =0; i<size; i++)
    {
        printf("%d ",*(p+i));
    }
}
int main()
{
	int arr[10] = {1,2,3,4,5,6,7,8,9,10};
	int* p = arr;//整型指针p是arr数组首元素地址
	int size = sizeof(arr)/sizeof(arr[0]);//判断有多少个元素
	//一级指针p传给函数
	print(p,size);
	return 0;
}

结果如下:
在这里插入图片描述

二级指针传参

void test(int** ptr)
{
	printf("num = %d\n",  **ptr);
}
int main()
{
	int n = 10;
	int *p = &n;//一级指针
	int **pp = &p;//二级指针
	test(pp);
	test(&p);
	return 0;
}

结果如下:
在这里插入图片描述

函数指针

函数指针是指向函数的指针,存放函数地址的指针

函数指针的使用

int add(int x,int y)//定义一个函数
{
    return x+y;
}
int main()
{
    int (*pf)(int, int) = &add;//定义一个函数指针*pf
    int (*pf)(int, int) = add;//add ==pf
	//&函数名-取到函数的地址
	//函数名 == &函数名
	//数组名 != &数组名
    int ret = (*pf)(3,5);//(*pf)解引用调用add函数,(3,5)传参
    int ret = pf(3,5);    
    //两者等价
	printf("%d",ret);  
    return 0;
}

经典案例分析

((void ()())0)();
调用0地址处的函数
该函数无参,返回类型是void

  1. void (*)() - 函数指针类型
  2. (void (*)())0 - 对0进行强制类型转化,被解释为一个函数地址
  3. (void ()())0 - 对0地址进行解引用操作
  4. (void ()())0() - 调用0地址的函数

void (signal(int,void ()(int)))(int);
1.signal 和 ()相结合, 说明signal是函数名
2.signal函数的第一个参数类型是int,第二个参数类型是函数指针
该函数指针,指向一个参数为int,返回类型是void的函数
3.signal函数的返回类型也是一个函数指针
该函数指针,指向一个参数为int,返回类型是void的函数
signal是一个函数声明

函数指针数组

函数指针数组 - 存放函数指针的数组
函数指针数组的简单应用:简易计算器

#include<stdio.h>
#include<windows.h>
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("***************************\n");
    printf("*****1.Add*****2.Sub*******\n");
    printf("*****3.Mul*****4.Div*******\n");
    printf("*********0.Exit************\n");
}
int calc(int (*pf)(int, int))//(int (*pf)(int,int))是函数指针
{
    int x,y;
    printf("输入你想计算的两个数");
    scanf("%d %d",&x, &y);
    return pf(x, y);
}
int main()
{   
    int input = 0;
    do
    {
        int (*pfArr[5])(int,int) = {NULL, Add, Sub, Mul, Div};//函数指针数组
        int x, y, ret = 0;
        menu();
        printf("请选择:>");
        scanf("%d" ,&input);
        if(input >=1 && input <= 4)
        {
            printf("输入你想计算的两个数");
            scanf("%d %d",&x, &y);
            ret = (pfArr[input])(x, y);
            //pfArr就是函数指针数组
            //(pfArr[input])调用函数指针数组里的元素,(x,y)调用输入的两个参数进行计算
            printf("ret = %d\n", ret);
        }
        else if(input == 0)
        {
            printf("退出程序");
            break;
        }
        else
        {
            printf("程序错误");
        }
    } while (input);
    return 0;    
}

*指向函数指针数组的指针

int (pf)(int, int); //函数指针
int (
pf1[3])(int, int); //函数指针数组
int (* (*pf2)[3])(int, int) = &pf1;//指向函数指针数组的指针
pf2就是一个指向函数指针数组的指针
了解一下就可以了

回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所定向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应.

我们先来看一个qsort函数:

#include<stdio.h>
#include<stdib.h>
void qsort (void* base,//base放的是待排序数据中第一个对象的地址
			 size_t num,//排序数据元素的个数
 			 size_t size,//排序数据中一个元素的大小,单位是字节
            int (*compar)(const void*,const void*)//用来比较待排序数据中的2个元素的函数(需要自己定义函数)
            );
void print(int arr[], int size)
{
    int i = 0;
    for(i =0; i<size; i++)
    {
        printf("%d ",arr[i]);
    }
}//打印数组
int cmp_int(const void*e1, const void*e2)
{
    return *(int*)e1-*(int*)e2;//(void*)类型指针要强制转化为(int*)整型指针,如果e1指向的元素 > e2指向的元素返回大于0的数字,如果小于返回小于0的数字
}	
int main()
{
	int arr[] = {9,8,7,6,5,4,3,2,1};
	int sz = sizeof(arr)/sizeof(arr[0]);//计算数组中有多少个元素
	//排序
	qsort(arr, sz, sizeof(arr[0]),cmp_int);
	print(arr,sz);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值