C语言入门之指针(3)

  • 什么是函数指针

可以定义一个指向函数的指针变量,用来存放某一函数的起始地址,这就意味着此指针变量指向该函数。例如:

      int (*p)(int,int);

   定义p是指向函数的指针变量,它可以指向类型为整型且有两个整型参数的函数。p的类型用int (*)(int,int)表示

  • 怎样定义和使用指向函数的指针变量

定义指向函数的指针变量的一般形式为

数据类型 (*指针变量名)(函数参数表列);

    int (*p)(int,int);

    p=max;  

    p=max(a,b);

    p+n,p++,p--等运算无意义

例: 输入两个数,根据输入的字符

   +,-,x,/算出对应的结果

解题思路:

1.定义两个float单精度浮点数变量a,b和一个字符变量c接收键盘的输入值及运算符号字符

2.再定义一个函数指针和4个加,,,除函数,

3.通过函数指针调用相应的函数进行运算

#include <stdio.h>  //包含标准输入输出头文件

//   加法
float add(float x,float y)
{
	return x+y;
}

// 减法
float minus(float x,float y)
{
	return x-y;
}

// 乘法
float mul(float x,float y)
{
	return x*y;
}

// 除法
float div(float x,float y)
{
	if(y != 0)
	   return x/y;
	printf("除数不能为0\n");
    
	return 0;
}

int main(void)  //主函数,程序的入口
{
	float a,b;
	char c;
	float (*func)(float,float);   //函数指针
	func = NULL;
	printf("请输入2个运算数字,和1个加减乘除运算符\n");
	scanf("%f,%f,%c",&a,&b,&c);
	switch(c)
	{
	case '+':
		func = add;
		break;
	case '-':
		func = minus;
		break;
	case '*':
		func = mul;
		break;
	case '/':
		func = div;
		break;
	default:
		printf("输入的运算符有误\n");
		break;
	}
	if(func != NULL)
	{
		float result = func(a,b);
		printf("result=%.2f\n",result);
	}
  
  return 0;
}
  • 函数指针数组

#include <stdio.h>  //包含标准输入输出头文件

//   加法
float add(float x,float y)
{
	return x+y;
}

// 减法
float minus(float x,float y)
{
	return x-y;
}

// 乘法
float mul(float x,float y)
{
	return x*y;
}

// 除法
float div(float x,float y)
{
	if(y != 0)
	   return x/y;
	printf("除数不能为0\n");
    
	return 0;
}

int main(void)  //主函数,程序的入口
{
	float a,b;
	char c;
	int index;
   
	float (*func[4])(float,float) = {    // 函数指针数组
		add,minus,mul,div 
	};

	printf("请输入2个运算数字,和1个加减乘除运算符\n");
	scanf("%f,%f,%c",&a,&b,&c);
	switch(c)
	{
	case '+':
		index = 0;
		break;
	case '-':
		index = 1;
		break;
	case '*':
	    index = 2;
		break;
	case '/':
		index = 3;
		break;
	default:
		index = 4;
		printf("输入的运算符有误\n");
		break;
	}
	if(index < 4)
	{
		float result = func[index](a,b);
		printf("result=%.2f\n",result);
	}
  
  return 0;
}
  • 空类型指针(void*

C语言中,void为“不确定类型”,不可以用void来声明变量。如:void a = 10;如果出现这样语句编译器会报错:variable or field ‘a’ declared void。

C语言中void * 为 “不确定类型指针”,void *可以用来声明指针。如:void * a

1void *可以接受任何类型的赋值:

    void *a = NULL

    int * b = NULL

    a  =  b//avoid * 型指针,任何类型的指针都可以直接赋值给它,无需进行强制类型转换

2void *可以赋值给任何类型的变量 但是需要进行强制转换:

    int * a = NULL

    void * b

    a  =  int *b

void* 在转换为其他数据类型时,赋值给void* 的类型 和目标类型必须保持一致。简单点来说:void* 类型接受了int * 的赋值后 这个void * 不能转化为其他类型,必须转换为int *类型;

#include <stdio.h> 
int main ()
{  
    short a= 10; 
    void *b = &a;  
    printf(“short a = %d\n",a);  
    printf("void (short*)b =%d \n",*(short*)b);
    printf(“void (double *)b =%d \n”,*(double*)b); //编译器并不会报错但是其结果却有点出人意料 	
			
    return 0; 
}
  • 用指向函数的指针作函数参数

指向函数的指针变量的一个重要用途是把函数的地址作为参数传递到其他函数

指向函数的指针可以作为函数参数,把函数的入口地址传递给形参,这样就能够在被调用的函数中使用实参函数

  • 返回指针值的函数

一个函数可以返回一个整型值、字符值、实型值等,也可以返回指针型的数据,即地址。其概念与以前类似,只是返回的值的类型是指针类型而已

定义返回指针值的函数的一般形式为

    类型名 *函数名(参数表列);

  • 什么是内存的动态分配

非静态的局部变量是分配在内存中的动态存储区的,这个存储区是一个称为的区域

C语言还允许建立内存动态分配区域,以存放一些临时用的数据,这些数据需要时随时开辟,不需要时随时释放。这些数据是临时存放在一个特别的自由存储区,称为

#include <stdio.h>
int a = 0; 全局初始化区
char *p1; 全局未初始化区
int main(void)
{
    int b; 栈
    char s[] = "abc"; 栈
    char *p2; 栈
    char *p3 = "123456"; 123456\0在常量区,p3在栈上。
    static int c =0; 全局(静态)初始化区
    p1 = (char *)malloc(10);  堆
    p2 = (char *)malloc(20);  堆
    return 0;
}
  • 怎样建立内存的动态分配

对内存的动态分配是通过系统提供的库函数来实现的,主要有malloccallocfreerealloc4个函数。

1.malloc函数

其函数原型为

void *malloc(unsigned int size);

其作用是在内存的动态存储区中分配一个长度为size的连续空间

函数的值是所分配区域的第一个字节的地址,或者说,此函数是一个指针型函数,返回的指针指向该分配域的开头位置

malloc(100);

开辟100字节的临时分配域,函数值为其第1个字节的地址

注意指针的基类型为void,即不指向任何类型的数据,只提供一个地址

如果此函数未能成功地执行(例如内存空间不足),则返回空指针(NULL)

2calloc函数

其函数原型为

   void *calloc(unsigned n,unsigned size);

其作用是在内存的动态存储区中分配n个长度为size的连续空间,这个空间一般比较大,足以保存一个数组。

calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size。这就是动态数组。函数返回指向所分配域的起始位置的指针;如果分配不成功,返回NULL。如:

     p=calloc(50,4);

  开辟50×4个字节的临时分配域,把起始地址赋给指针变量p

3free函数

其函数原型为

    void free(void *p);

其作用是释放指针变量p所指向的动态空间,使这部分空间能重新被其他变量使用。p应是最近一次调用callocmalloc函数时得到的函数返回值。

  free(p);

释放指针变量p所指向的已分配的动态空间

free函数无返回值

4. realloc函数

其函数原型为

void *realloc(void *p,unsigned int size);

如果已经通过malloc函数或calloc函数获得了动态空间,想改变其大小,可以用recalloc函数重新分配。

realloc函数将p所指向的动态空间的大小改变为sizep的值不变。如果重分配不成功,返回NULL。如

     realloc(p,50);         

 p所指向的已分配的动态空间改为50字节

以上4个函数的声明在stdlib.h头文件中,在用到这些函数时应当用“#include <stdlib.h>”指令把stdlib.h头文件包含到程序文件中。

  • 内存申请失败原因分析

假如有4k的地址空间,第一次分配了1k,第2次分配了2k,第3次分配了1k,刚好4k用完,然而程序释放了第1次的1k和第3次

的1k,内存有2k的空间可用,但这时要申请一块2k的内存,申请没有成功,原因是内存块虽然有2k的可用,但不是连续的2k,所

以导致申请内存失败

  • 回调函数

1 定义和使用场合

回调函数是指使用者自己定义一个函数,实现这个函数的程序内容,然后把这个函数(入口地址)作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数。函数是你实现的,但由别人(或系统)的函数在运行时通过参数传递的方式调用,这就是所谓的回调函数。简单来说,就是由别人的函数运行期间来回调你实现的函数。

这一设计允许了底层代码调用在高层定义的子程序(如图1-1所示)。C语言中回调函数主要通过函数指针的方式实现。

解题思路:

  1.    定义一个1秒的定时器
  2.    定义一个钩子,捕捉按键消息
  3.   定义函数指针实现回调
  4.   定义一个注册函数,实现回调函数注册
  5.   实现消息循环,消息翻译,消息派遣
  6.   主函数中调用注册函数,实现自己编写的函数每2秒被回调

callback.c

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>            //windows窗口头文件
#define WH_KEYBOARD_LL     13


static int randomBuf[5];  //用于存放随机产生的数据

void (*customeRandom)(int *buf);   //定义函数指针变量customeRandom
void (*resume)(int *buf);          //定义函数指针变量resume


//控制台消息响应
void CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
		static int i;
		if(++i % 2 == 0) //每2秒产生一组共5个随机数
		{
			if(customeRandom != NULL)
			{
				for(int j=0;j<5;j++)
				{
					randomBuf[j] = rand();	//产生随机数	
				}
				customeRandom(randomBuf);   //函数指针实现回调功能,调用的是其指向的函数,此例是用户设置的函数将会被调用
			}
		}
  
}

HHOOK g_Hook;   //钩子句柄
LRESULT CALLBACK KeyboardProc(int code,WPARAM w,LPARAM l)   //键盘钩子函数
{
     static int i;
    if(w == WM_KEYUP)   //按键抬起
	{
		if(i==0)
		{
			customeRandom = NULL;    //函数指针设为空
			i=1;
			printf("暂停随机数产生\n");

		}
		else
		{
			customeRandom = resume;		//恢复函数指针的指向的函数
			i = 0;
			printf("恢复随机数产生\n");
		}
	}
       

	return CallNextHookEx(g_Hook,code,w,l); 
}


/*
	设置回调函数,并启动Windows消息机制
*/
void setCallback(void (*f)(int *buf))
{
  customeRandom = f;   //函数指针指向参数f指向的函数
  resume = f;			//此函数指针仅为保存用
   unsigned int TimerID = 1;//Timer的ID是1
  int peried = 1000;//Timer的间隔是 1000ms
   
  //设置Timer  
  ::SetTimer(NULL, TimerID, peried, &TimerProc); 

  //设置钩子
  g_Hook = SetWindowsHookEx(WH_KEYBOARD_LL,KeyboardProc,GetModuleHandle(NULL),0);

  //定义消息变量
  ::MSG msg;
  while(::GetMessage(&msg, NULL, 0, 0)) //不停地查询消息
  {
	::TranslateMessage(&msg);     // 翻译消息
    ::DispatchMessage(&msg);	  // 消息派遣	
	
  }
  UnhookWindowsHookEx(g_Hook);   //解除钩子
}

main.c

#include <stdio.h>
#include <stdlib.h>


void setCallback(void (*f)(int *buf));


/*
	用户自己写的函数,此函数是个callback回调函数
*/
void generateRandom(int *buf)
{
	for(int i=0;i<5;i++)
	{
		printf("random=%d,",buf[i]);
	}
	printf("\n");
}

int main(void)
{

    setCallback(generateRandom);

}

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风雨依依

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值