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


Exercise 23: Meet Duff’s Device

Đây là một bài phức tạp (chỉ có 2 hàm phức tạp), và đây là một bài test não.
Trong ngôn ngữ C, thật sự quá nhiều điều rắc rối vì cú pháp của nó, cho nên việc học ngôn ngữ C thành chuyên gia là một điều không dể.
Tom Duff đã tạo ra một chương trình dựa vào sự rác rối của vòng lặp do-while switch-case. Chương trình này rất nổi tiếng (thông tin trên Wikipedia), tên gọi của nó là Duff’s device. Sau khi học bài này, bạn 1 sẽ thấy nó tuyệt vời, 2 là thấy nó như ác quỷ. Và hãy xem lời khuyên ở cuối bài.


Warning!
Ngôn ngữ C thật sự rất điên rồ, và bạn hoàn toàn có thể tạo ra những đoạn chương trình giống như vậy. Nhưng những chương trình như vậy thực sự rất khó hiểu, có thể khiến bạn phát điên. Tuy nhiên nếu bạn hiểu được những bài như thế này, thì trình độ của bạn đã ở rất cao rồi. Bạn đã hiểu sâu về C cũng như về máy tính (bộ nhớ). Nhưng bạn không nên sử dụng nó từ này kể về sau. Vì nó sẽ gây khó chịu cho người đọc.


Dưới đây là chương trình, 2 hàm khó nhất cũng chính là thứ chính yếu trong chương trình này duffs_device zeds_device.

    #include <stdio.h>
    #include <string.h>
    #include "dbg.h"
    
    int normal_copy(char *from, char *to, int count)
    {
        int i = 0;
          for (i = 0; i < count; i++) {
            to[i] = from[i];
        }
        return i;
    }
 
    int duffs_device(char *from, char *to, int count)
    {
        {
            int n = (count + 7) / 8;
 
            switch (count % 8) {
                case 0:
                    do {
                        *to++ = *from++;
                        case 7:
                        *to++ = *from++;
                        case 6:
                        *to++ = *from++;
                        case 5:
                        *to++ = *from++;
                        case 4:
                        *to++ = *from++;
                        case 3:
                        *to++ = *from++;
                        case 2:
                        *to++ = *from++;
                        case 1:
                        *to++ = *from++;
                    } while (--n > 0);
             }
         }
 
         return count;
     }
 
     int zeds_device(char *from, char *to, int count)
     {
         {
             int n = (count + 7) / 8;
 
             switch (count % 8) {
                 case 0:
     again:    *to++ = *from++;
 
                 case 7:
               *to++ = *from++;
                 case 6:
               *to++ = *from++;
                 case 5:
               *to++ = *from++;
                 case 4:
               *to++ = *from++;
                 case 3:
               *to++ = *from++;
                 case 2:
               *to++ = *from++;
                 case 1:
               *to++ = *from++;
               if (--n > 0)
                   goto again;
             }
         }
 
         return count;
     }
 
     int valid_copy(char *data, int count, char expects)
     {
         int i = 0;
         for (i = 0; i < count; i++) {
             if (data[i] != expects) {
                 log_err("[%d] %c != %c", i, data[i], expects);
                 return 0;
             }
         }
 
         return 1;
     }
 
     int main(int argc, char *argv[])
     {
         char from[1000] = { 'a' };
         char to[1000] = { 'c' };
         int rc = 0;
 
         // set up the from to have some stuff
         memset(from, 'x', 1000);
         // set it to a failure mode
         memset(to, 'y', 1000);
        check(valid_copy(to, 1000, 'y'), "Not initialized right.");

        // use normal copy to
        rc = normal_copy(from, to, 1000);
        check(rc == 1000, "Normal copy failed: %d", rc);
        check(valid_copy(to, 1000, 'x'), "Normal copy failed.");

        // reset
        memset(to, 'y', 1000);

        // duffs version
        rc = duffs_device(from, to, 1000);
        check(rc == 1000, "Duff's device failed: %d", rc);
        check(valid_copy(to, 1000, 'x'), "Duff's device failed copy.");

        // reset
        memset(to, 'y', 1000);

        // my version
        rc = zeds_device(from, to, 1000);
        check(rc == 1000, "Zed's device failed: %d", rc);
        check(valid_copy(to, 1000, 'x'), "Zed's device failed copy.");

        return 0;
    error:
        return 1;
    }

Trong đoạn code này, chúng ta có 3 phiên bản cho hàm copy:

  • normal_copy Dùng vòng lặp for để copy các kí tự từ mảng này qua mảng khác, đây là cách rất bình thường.
  • duffs_device Hàm huyền thoại của chúng ta – Duff’s device, một hàm khiến các lập trình viên rất khó chịu (như đã nói ở đầu bài).
  • zeds_device Một phiên bản khác của Duff’s device.

Các bạn hãy học kĩ 3 hàm này rồi hãy đi tiếp. Và thử giải thích nó xem, các bạn đã hiểu nó như thế nào?

What You Should See

Bài này không hề có một output nào. Cho nên các bạn hãy chạy nó với Valgrind hay debugger để xem được nhiều thứ hơn.

Solving the Puzzle

Điều đầu tiên để hiểu là C thật sự quá lỏng lẽo với những cú pháp của nó. Trong trường hợp này là do-whileswitch-case, chúng lồng vào nhau, nhưng nó vẫn chạy đúng.
Điều thứ 2 là bạn nhìn thấy lệnh switch-case không hề có break, cho nên nó sẽ nhảy vào một case nào đó, sau đó nó sẽ tiếp tục thực hiện những lệnh phía dưới nó cho đến khi hết case.
Điều cuối cùng là count % 8 và biến n.
Tất cả những điều trên làm cho chương trình này trở nên khó chịu.

Điều bây giờ bạn cần làm là debug từng dòng code và ghi lại các giá trị biến và giá trị trong mảng để hiểu nó một cách sâu sắc.

Lời khuyên: Không bao giờ sử dụng chương trình này cũng như những chương trình tương tự như vậy.

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 )

Google+ photo

You are commenting using your Google+ 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 )

w

Connecting to %s