PDF版 ePub版

# 练习18：函数指针

int (*POINTER_NAME)(int a, int b)

• 编写一个普通的函数声明：int callme(int a, int b)
• 将函数用指针语法包装：int (*callme)(int a, int b)
• 将名称改成指针名称：int (*compare_cb)(int a, int b)

int (*tester)(int a, int b) = sorted_order;
printf("TEST: %d is same as %d\n", tester(2, 3), sorted_order(2, 3));

• 编写：char *make_coolness(int awesome_levels)
• 包装：char *(*make_coolness)(int awesome_levels)
• 重命名：char *(*coolness_cb)(int awesome_levels)

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

/** Our old friend die from ex17. */
void die(const char *message)
{
if(errno) {
perror(message);
} else {
printf("ERROR: %s\n", message);
}

exit(1);
}

// a typedef creates a fake type, in this
// case for a function pointer
typedef int (*compare_cb)(int a, int b);

/**
* A classic bubble sort function that uses the
* compare_cb to do the sorting.
*/
int *bubble_sort(int *numbers, int count, compare_cb cmp)
{
int temp = 0;
int i = 0;
int j = 0;
int *target = malloc(count * sizeof(int));

if(!target) die("Memory error.");

memcpy(target, numbers, count * sizeof(int));

for(i = 0; i < count; i++) {
for(j = 0; j < count - 1; j++) {
if(cmp(target[j], target[j+1]) > 0) {
temp = target[j+1];
target[j+1] = target[j];
target[j] = temp;
}
}
}

return target;
}

int sorted_order(int a, int b)
{
return a - b;
}

int reverse_order(int a, int b)
{
return b - a;
}

int strange_order(int a, int b)
{
if(a == 0 || b == 0) {
return 0;
} else {
return a % b;
}
}

/**
* Used to test that we are sorting things correctly
* by doing the sort and printing it out.
*/
void test_sorting(int *numbers, int count, compare_cb cmp)
{
int i = 0;
int *sorted = bubble_sort(numbers, count, cmp);

if(!sorted) die("Failed to sort as requested.");

for(i = 0; i < count; i++) {
printf("%d ", sorted[i]);
}
printf("\n");

free(sorted);
}

int main(int argc, char *argv[])
{
if(argc < 2) die("USAGE: ex18 4 3 1 5 6");

int count = argc - 1;
int i = 0;
char **inputs = argv + 1;

int *numbers = malloc(count * sizeof(int));
if(!numbers) die("Memory error.");

for(i = 0; i < count; i++) {
numbers[i] = atoi(inputs[i]);
}

test_sorting(numbers, count, sorted_order);
test_sorting(numbers, count, reverse_order);
test_sorting(numbers, count, strange_order);

free(numbers);

return 0;
}

ex18.c:1~6

ex18.c:7~17

ex18.c:21

ex18.c:27~49

ex18.c:27

ex18.c:29~34

ex18.c:38

ex18.c:39

ex18.c:40

ex18.c:41-43

ex18.c:48

ex18.c:51-68

compare_cb函数类型三个不同版本，它们需要和我们所创建的typedef具有相同的定义。否则C编辑器会报错说类型不匹配。

ex18.c:74-87

ex18.c:90-103

ex18.c:105-107

ex18.c:109

## 你会看到什么

$make ex18 cc -Wall -g ex18.c -o ex18$ ./ex18 4 1 7 3 2 0 8
0 1 2 3 4 7 8
8 7 4 3 2 1 0
3 4 2 7 1 0 8
\$

## 如何使它崩溃

unsigned char *data = (unsigned char *)cmp;

for(i = 0; i < 25; i++) {
printf("%02x:", data[i]);
}

printf("\n");

55:48:89:e5:89:7d:fc:89:75:f8:8b:55:fc:8b:45:f8:29:d0:c9:c3:55:48:89:e5:89:

## 附加题

• 用十六进制编辑器打开ex18，接着找到函数起始处的十六进制代码序列，看看是否能在原始程序中找到函数。
• 在你的十六进制编辑器中找到更多随机出现的东西并修改它们。重新运行你的程序看看发生了什么。字符串是你最容易修改的东西。
• 将错误的函数传给compare_cb，并看看C编辑器会报告什么错误。
• NULL传给它，看看程序中会发生什么。然后运行Valgrind来看看它会报告什么。
• 编写另一个排序算法，修改test_sorting使它接收任意的排序函数和排序函数的比较回调。并使用它来测试两种排序算法。