关于C之函数指针及typedef简介

本文深入探讨了C语言中的函数指针概念,通过qsort()函数实例展示了如何使用函数指针进行数组排序,以及如何将函数指针作为参数传递给其他函数。文章还提供了多个函数指针应用实例,包括字符串大小写转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先看一个C标准库<stdio.h>中的一个函数qsort(),它的功能是对任何类型的数组进行排序。

void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))

参数:

  • base -- 指向要排序的数组的第一个元素的指针。
  • nitems -- 由 base 指向的数组中元素的个数。
  • size -- 数组中每个元素的大小,以字节为单位。
  • compar -- 用来比较两个元素的函数。

其中形参compar就是一个函数指针。

我们先看一个使用qsort()函数的例子:

#include <stdio.h>
#include <stdlib.h>
int cmpfunc (const void * a, const void * b);

int values[] = { 88, 56, 100, 2, 25 };

int main() {
   int n;
   printf("排序之前的列表:\n");
   for( n = 0 ; n < 5; n++ ) {
      printf("%d ", values[n]);
   }

   qsort(values, sizeof(values) - 1, sizeof(int), cmpfunc);

   printf("\n排序之后的列表:\n");
   for( n = 0 ; n < 5; n++ ) {
      printf("%d ", values[n]);
   }
  return(0);
}
int cmpfunc (const void * a, const void * b) {
   return ( *(int*)a - *(int*)b ); ←这种用法划重点
}
排序之前的列表:
88 56 100 2 25 
排序之后的列表:
2 25 56 88 100

我们可以看到函数名即是指向函数的指针。

我们分析一下comfunc()函数的算法:

由于qsort()函数是可以排序任意类型的数组的,所以形参是void类型的指针。

关于C之运算符优先级 查表可知:

优化级:* < (type)纯量表达式。所以*(int*)a:先void指针变量a强制转化为int类型的指针,然后通过*运算符取它的int型值,这样才可以进行比较。

考虑下面的函数原型:

void ToUpper(char *); // 把字符串中的字符转换成大写字符

ToUpper()函数的类型是“带char * 类型参数、 返回类型是void的函数”。下面声明了一个指针pf指向该函数类型:

void (*pf)(char *); // pf 是一个指向函数的指针
void ToUpper(char *);
void ToLower(char *);
int round(double);
void (*pf)(char *);
pf = ToUpper; // 有效, ToUpper是该类型函数的地址
pf = ToLower; //有效, ToUpper是该类型函数的地址
pf = round; // 无效, round与指针类型不匹配
pf = ToLower(); // 无效, ToLower()不是地址

作为函数的参数是数据指针最常见的用法之一, 函数指针亦如此。 例如, 考虑下面的函数原型:

void show(void (* fp)(char *), char * str);

show()函数有两个参数:1.fp(指向的函数授受char *类型的参数,返回类型为void),2.str(指向一个char类型的值)。

有以上声明,可以这样调用:

show(ToLower, mis); /* show()使用ToLower()函数: fp = ToLower */
show(pf, mis); /* show()使用pf指向的函数: fp = pf */

可以用函数指针直接调用函数:

int main() {
    void (*fp)(char *); // 声明一个函数指针
    fp = toUpper; // fp赋值为函数toUpper()的地址
    char * uppercase = (*fp)(str); // 传char *类型参数,调用执行函数
}
char * toUpper(char *) {
    ...
}

下面给出一个使用函数指针的完整例子:

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define LEN 81
char* s_gets(char *st, int n);
char showmenu(void);
void eatline(void); // 读取至行末尾
void show(void (*fp)(char*), char *str);
void ToUpper(char*); // 把字符串转换为大写
void ToLower(char*); // 把字符串转换为小写
void Transpose(char*); // 大小写转置
void Dummy(char*); // 不更改字符串
int main(void) {
	char line[LEN];
	char copy[LEN];
	char choice;
	void (*pfun)(char*); // 声明一个函数指针, 被指向的函数接受char *类型的参数, 无返回值
	puts("Enter a string (empty line to quit):");
	while (s_gets(line, LEN) != NULL && line[0] != '\0') {
		while ((choice = showmenu()) != 'n') {
			switch (choice) { // switch语句设置指针
			case 'u':
				pfun = ToUpper;
				break;
			case 'l':
				pfun = ToLower;
				break;
			case 't':
				pfun = Transpose;
				break;
			case 'o':
				pfun = Dummy;
				break;
			}
			strcpy(copy, line); // 为show()函数拷贝一份
			show(pfun, copy); // 根据用户的选择, 使用选定的函数
		}
		puts("Enter a string (empty line to quit):");
	}
	puts("Bye!");
	return 0;
}
char showmenu(void) {
	char ans;
	puts("Enter menu choice:");
	puts("u) uppercase l) lowercase");
	puts("t) transposed case o) original case");
	puts("n) next string");
	ans = getchar(); // 获取用户的输入
	ans = tolower(ans); // 转换为小写
	eatline(); // 清理输入行
	while (strchr("ulton", ans) == NULL) {
		puts("Please enter a u, l, t, o, or n:");
		ans = tolower(getchar());
		eatline();
	}
	return ans;
}
void eatline(void) {
	while (getchar() != '\n')
		continue;
}
void ToUpper(char *str) {
	while (*str) {
		*str = toupper(*str);
		str++;
	}
}
void ToLower(char *str) {
	while (*str) {
		*str = tolower(*str);
		str++;
	}
}
void Transpose(char *str) {
	while (*str) {
		if (islower(*str))
			*str = toupper(*str);
		else if (isupper(*str))
			*str = tolower(*str);
		str++;
	}
}
void Dummy(char *str) {} //不改变字符串
 
void show(void (*fp)(char*), char *str) {
	(*fp)(str); // 把用户选定的函数作用于str
	puts(str); // 显示结果
}
char* s_gets(char *st, int n) {
	char *ret_val;
	char *find;
	ret_val = fgets(st, n, stdin);
	if (ret_val) {
		find = strchr(st, '\n'); // 查找换行符
		if (find) // 如果地址不是NULL,
			*find = '\0'; // 在此处放置一个空字符
		else
			while (getchar() != '\n')
				continue; // 清理输入行中剩余的字符
	}
	return ret_val;
}

注意, ToUpper()、 ToLower()、 Transpose()和 Dummy()函数的类型都相同, 所以这 4 个函数都可以赋给pfun指针。

这种情况下, 可以使用typedef。 例如, 该程序中可以这样写:

typedef void (*V_FP_CHARP)(char *);
void show (V_FP_CHARP fp, char *);
V_FP_CHARP pfun;

如果还想更复杂一些, 可以声明并初始化一个函数指针的数组:

V_FP_CHARP arpf[4] = {ToUpper, ToLower, Transpose, Dummy};

然后把showmenu()函数的返回类型改为int, 如果用户输入u, 则返回0;如果用户输入l, 则返回2; 如果用户输入t, 则返回2, 以此类推。 可以把程序中的switch语句替换成下面的while循环:

index = showmenu();
while (index >= 0 && index <= 3) {
    strcpy(copy, line); /* 为show()拷贝一份 */
    show(arpf[index], copy); /* 使用选定的函数 */
    index = showmenu();
}

上面提到typedef,下面简要介绍一下它的用法:

typedef工具是一个高级数据特性, 利用typedef可以为某一类型自定义名称。 

1.定义类型别名(包括结构):

typedef unsigned char BYTE;
可以这样使用:
BYTE x, y[10], * z;

typedef struct {double x; double y;} rect;
可以这样使用:
rect r1 = {3.0, 6.0};
rect r2 = r1;

2.定义复杂类型(如数组、指针、函数):

typedef int arr5[5];
typedef arr5 * p_arr5;
typedef p_arr5 arrp10[10];
arr5 togs; // togs 是一个内含5个int类型值的数组==【int arr5[5]】
p_arr5 p2; // p2 是一个指向数组的指针, 该数组内含5个int类型的值==【int (*p2)[5]】
arrp10 ap; // ap 是一个内含10个指针的数组, 每个指针都指向一个内含5个int类型值的数组,
           // == 【(*ap[10])[5]】

typedef char (* FRPTC ()) [5];
注解:把FRPTC声明为一个函数类型, 该函数返回一个指针, 该指针指向内含5个char类型元素的数组。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

itzyjr

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

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

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

打赏作者

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

抵扣说明:

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

余额充值