Saturday, May 2, 2015

Đặt vấn đề
Giả sử trong bài toán có nhiều lựa chọn - option, vậy làm thế nào để trong một thời điểm có thể chọn:
+ Một trong những option (only one)
+ Nhiều option cùng một lúc

1. Một trong những option
option.c

 #include <stdio.h>  
   
 enum {  
   OPE_ADD = 0,  
   OPE_SUB,  
   OPE_MUL,  
   OPE_DEV  
 };  
    
 void math_operator(int a, int b, unsigned char ope)  
 {  
   if(ope == OPE_ADD){  
     printf("sum: a+b= %d \n", (a+b));  
   }  
   
   if(ope == OPE_SUB){  
     printf("sub: a-b= %d \n", (a-b));  
   }  
   
   if(ope == OPE_MUL){  
     printf("multi: a*b= %d \n", (a*b));  
   }  
   
   if(ope == OPE_DEV){  
     if(b!=0)  
       printf("devi: a/b= %f \n", ((float)a/b));  
     else  
       printf("fail ! \n");  
   }  
   printf("\n");  
 }  
   
 int main()  
 {  
   
   math_operator(4,2, OPE_ADD);  
   math_operator(4,2, OPE_SUB);  
   math_operator(4,2, OPE_MUL);  
   math_operator(4,2, OPE_DEV);  
   
   return 0;  
 }   

Compile & Execute
 $ gcc option.c   
 $ ./a.out   
 sum: a+b= 6   
   
 sub: a-b= 2   
   
 multi: a*b= 8   
   
 devi: a/b= 2.000000   

Đối số ope cho biết hàm math_operator phải thực hiện phép toán nào . Vì ope chỉ mang một giá trị nên hàm math_operator chỉ thực hiện duy nhất một phép toán trong các phép toán ADD, SUB, MUL và DEV.

Note: Tùy vào kiểu dữ liệu của biến ope ta sẽ có 2^n option cho biểu thức if(ope == value), vơi n là số bít của ope. Cụ thể trong trường hợp này vì ope có kiểu unsigned char nên độ dài là 8bit, suy ra ope có tất cả 2^8 = 256 giá trị khác nhau.

2. Nhiều option cùng một lúc
bitmask.c
 #include <stdio.h>  
   
 #define OPE_MASK_ADD (1 << 0) // 1 = 0x01 = 00000001  
 #define OPE_MASK_SUB (1 << 1) // 2 = 0x02 = 00000010  
 #define OPE_MASK_MUL (1 << 2) // 4 = 0x04 = 00000100  
 #define OPE_MASK_DEV (1 << 3) // 3 = 0x01 = 00001000  
   
 void math_operator(int a, int b, unsigned char ope)  
 {  
   if(ope & OPE_MASK_ADD){  
     printf("sum: a+b= %d \n", (a+b));  
   }  
   
   if(ope & OPE_MASK_SUB){  
     printf("sub: a-b= %d \n", (a-b));  
   }  
   
   if(ope & OPE_MASK_MUL){  
     printf("multi: a*b= %d \n", (a*b));  
   }  
   
   if(ope & OPE_MASK_DEV){  
     if(b!=0)  
       printf("devi: a/b= %f \n", ((float)a/b));  
     else  
       printf("fail ! \n");  
   }  
   printf("\n");  
 }  
   
 int main()  
 {  
   
   math_operator(4,2, (OPE_MASK_ADD));  
   math_operator(4,2, (OPE_MASK_ADD | OPE_MASK_SUB));  
   math_operator(4,2, (OPE_MASK_ADD | OPE_MASK_SUB | OPE_MASK_MUL));  
   math_operator(4,2, (OPE_MASK_ADD | OPE_MASK_SUB) | OPE_MASK_MUL | OPE_MASK_DEV);  
   
   return 0;  
 }  
   

Compile & Execute
 $ gcc bitmask.c   
 $ ./a.out   
 sum: a+b= 6   
   
 sum: a+b= 6   
 sub: a-b= 2   
   
 sum: a+b= 6   
 sub: a-b= 2   
 multi: a*b= 8   
   
 sum: a+b= 6   
 sub: a-b= 2   
 multi: a*b= 8   
 devi: a/b= 2.000000   

Khác với trường hợp 1, ở đây mỗi lần gọi hàm math_operator thì có thể thực hiện được nhiều phép toán cũng với chỉ đối số ope như trên.
Ở trường hợp này ta sử dụng một kỹ thuật gọi là kỹ thuật mặt nạ - bitmask. Kỹ thuật này thay vì quan tâm đến giá trị của biến ope như trường hợp trên, ta lại quan tâm đến từng bit của ope, mỗi bit được sử dụng cho một option. Để hiểu kỹ thuật này trước hết cần xem lại các phép toán Bitwise Operators


bit number 8 bit number 7 bit number 6 bit number 5 bit number 4 bit number 3 bit number 2 bit number 1
0 0 0 0 0 0 1 1

Các define quy định bit nào sẽ sử dụng cho action nào
 #define OPE_MASK_ADD (1 << 0) // 1 = 0x01 = 00000001  
 #define OPE_MASK_SUB (1 << 1) // 2 = 0x02 = 00000010  
 #define OPE_MASK_MUL (1 << 2) // 4 = 0x04 = 00000100  
 #define OPE_MASK_DEV (1 << 3) // 3 = 0x01 = 00001000 

Như vậy theo ví dụ bitmask.c quy định bit ở vị trí số 1 sẽ dùng để biểu diễn cho action ADD, tương tự như vậy bít ở vị trí 2,3,4 biểu diễn cho action SUB, MUL và DEV.
+Để đưa các action vào function ta dùng phép OR bit (ký hiệu: |)
+Để lọc ra giá trị các bit tương ứng với các action ta dùng phép AND bit (ký hiệu: &).

Ví dụ để function thực hiện hai phép toán ADD và SUB:
math_operator(4,2, (OPE_MASK_ADD | OPE_MASK_SUB));
Khi đó
 ope = 00000001 | 00000010 = 00000011  
Bên trong hàm thực hiện phân tích bit
   if(ope & OPE_MASK_ADD){  //true00000011 & 00000001 = 00000001 # 0
     printf("sum: a+b= %d \n", (a+b));  
   }  
   
   if(ope & OPE_MASK_SUB){  //true00000011 & 00000010 = 00000010 # 0
     printf("sub: a-b= %d \n", (a-b));  
   } 


   if(ope & OPE_MASK_MUL){  //false00000011 & 00000100 = 00000000 = 0
     printf("multi: a*b= %d \n", (a*b));  
   }  
   
   if(ope & OPE_MASK_DEV){  //false00000011 & 00001000 = 00000000 = 0
     if(b!=0)  
       printf("devi: a/b= %f \n", ((float)a/b));  
     else  
       printf("fail ! \n");  
   } 

Kết quả function sẽ thực hịện hai phép toán ADD và SUB đúng như mong muốn.

Note
+ Số action đươc vào phụ thuộc vào số bit mà đối số ope được khai báo, như ví dụ trên ope được khai báo là unsigned char nên có 8 bit, vì vậy sẽ đưa vào cùng lúc được 8 action. Ví dụ ope được khai báo kiểu int thì sẽ có tối đa 32 action đưa vào cùng lúc
+ Đối số ope thường được khai báo kiểu char, unsigned char, int, unsigned int

Leave a Reply

Subscribe to Posts | Subscribe to Comments

- Copyright © Lập trình hệ thống nhúng Linux . Powered by Luong Duy Ninh -