Exercise 18 Lập Trình C – Learn C The Hard Way


Exercise 18: Pointers To Functions

Con trỏ thực sự rất lợi hại, bạn có con trỏ trỏ tới struct, string, mảng, biến, và bạn cũng có thể dùng con trỏ để trỏ tới hàm (function). Trong bài hôm nay chúng ta sẽ tìm hiểu về chuyện này.
Lưu ý: Khái niệm này khác với khái niệm một hàm trả về một con trỏ nhé.
Đây là format của con trỏ hàm (function pointer) có dạng sau:

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

Và đây là cách để nhớ nó:
1. Viết một định nghĩa cho một hàm bình thường:

int callme(int a, int b)

2. Chuyển tên hàm thành tên con trỏ như sau:

int (*callme)(int a, int b)

3. Đổi thành tên một con trỏ:

int (*compare_cb)(int a, int b)

Cũng giống như bạn tạo ra một mảng, dùng một con trỏ trỏ tới mảng, thì bạn có thể dùng con trỏ đó để xử lý trên mảng, thì ở đây con trỏ trỏ tới function cũng vậy. Xem đoạn code này bạn có thể hình dung được cách sử dụng và để ý tên của chúng.

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

Thậm chí chúng ta có thể có một con trỏ trỏ tới 1 hàm, và hàm này lại return 1 con trỏ:
1. Viết hàm return 1 con trỏ:

char *make_coolness(int awesome_levels)

2. Chuyển thành con trỏ như đã làm ở trên:

char *(*make_coolness)(int awesome_levels)

3. Đổi tên:

char *(*coolness_cb)(int awesome_levels)

Bây giờ chúng đi và một ví dụ cụ thể để tìm hiểu cách sử dụng:

#include <stdio.h>
#include <stdlib.h>
#include <assert.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);
}

void destroy(compare_cb cmp)
{
    int i = 0;

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

    for(i = 0; i < 1; i++) {
        data[i] = i;
    }

    printf("\n");
}

void dump(compare_cb cmp)
{
    int i = 0;

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

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

    printf("\n");
}



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);

    printf("SORTED:");
    dump(sorted_order);

    destroy(sorted_order);

    printf("SORTED:");
    dump(sorted_order);


    return 0;
}

Trong chương trình này chúng tôi sử dụng một giải thuật sắp xấp các phần tử của mảng (bubble sort – đây là một giải thuật cực kì đơn giản, các bạn có thể tìm hiểu trên internet). Sau đây là một số giải thích để các bạn hiểu hơn về chương trình:
Dòng 1 – 6: Include các file header hay dùng để gọi một số hàm cần thiết
Dòng 7 – 17: Đây là hàm die mà chúng tôi có đề cập trong bài exercise 17 trước. Và tôi sẽ dùng nó để kiểm tra lỗi.
Dòng 21: Đây là từ khóa typedef và sau đó, tôi dùng compare_cb
Dòng 27 – 49: Chạy bubble sort. Hàm này như thế này:
    Dòng 27: Đây là nơi chúng tôi dùng typedef cho compare_cb với cái tham số là cmp. Hàm này sẽ return một “sự so sánh” giữa 2 số integer (trả về số int).
    Dòng 29 – 32: Tạo các biến trên stack, và một khối bộ nhớ trên heap (vì dùng hàm malloc) và kích thước bộ nhớ trên heap4 x count. Và đây là một mảng.
    Dòng 34: Khi target = NULL thì nghĩa là vùng nhớ trên heap không được cấp và chúng ta thoát khỏi hàm.
    Dòng 38 – 46: Đây là cấu trúc của giải thuật bubble sort.
    Dòng 40: Lưu ý chúng ta có sử dụng cmp. Ở đây chúng ta đưa vào cho nó 2 tham số là target[j]target[j + 1]. Để nói rằng thằng cmp đang trỏ tới 1 hàm nào đó gồm có 2 đối số vào       ab, và trong hàm đó xử lý a (target[j])b (target[j + 1]) để trả về một kết quả kiểu int (vì thằng typedef đã định nghĩa vậy).
    Dòng 41 – 43: Đây là kĩ thuật hoán đổi 2 số (swap).
    Dòng 48: Return một mảng đã được sắp xếp.

Dòng 51 – 68: Đây là 3 cái hàm mà thằng compare_cb nó sẽ trỏ tới để xứ lý và trả về 1 kiểu int.

Dòng 74 – 87: Đây là hàm có chức năng test cho hàm bubble sort phía trên.

What You Should See

Bạn cần chạy ra chương trình như thế này:

$ 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 
$ 

Research

  1. Lấy một editor có thể mở được mã hex và mở file ex18 lên xem và tìm dãy số hex mà bắt đầu một function.
  2. Và thử thay đổi một vài thứ trên hex editor và xem chuyện gì xảy ra.
  3. Thử gây ra lỗi ở compare_cb (ví dụ truyền sai kiểu dữ liệu) và xem trình biên dịch nói gì.
  4. Tìm hiểu thêm các giải thuật sort khác, vì đây là giải thuật “cũ kĩ” lắm rồi.
  5. Tìm hiểu 2 hàm dumpdestroy xem có tác dụng gì nhé.
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s