Thứ Tư, ngày 22 tháng 6 năm 2011

Viết 1 lần chạy mọi nơi với C

Có lẽ mọi người sẽ không khỏi thắc mắc tại sao Bioz và IEEV lại có phần ưu ái cho ngôn ngữ C. Điều đó là vì C  hiện nay vẫn là ngôn ngữ phổ biến trong các thiết bị nhúng. C được hỗ trợ ở nhiều môi trường khác nhau. Cấu trúc C đơn giản, trong sáng và có sự uyển chuyển cần thiết cho việc triển khai các giải thuật yêu cầu sự tối ưu cao. Tuy nhiên C cũng có nhiều đường C, giống như một môn phái võ thuật với nhiều chi nhiều nhánh. Với mỗi môi trường bạn sẽ có các trình biên dịch khác nhau, các hỗ trợ ngoài chuẩn khác nhau, và đương nhiên kết quả chạy ra của cùng một mã nguồn (trong tình huống hên mà biên dịch thành công) đôi khi cũng khác nhau. Chính vì lẽ đó mà Bioz viết bài này nhằm đưa ra một vài điểm chú ý trong quá trình triển khai thuật toán với C, hướng tới mục tiêu cố gắng "viết một lần, biên dịch và chạy cho ra cùng kết quả ở mọi nơi", giảm thiểu những sai sót do vấn đề khác môi trường, khác nền tảng gây ra.
  1. Hãy từ bỏ quan niệm sai lầm rằng kích thước của kiểu dữ liệu là như nhau trên mọi nền tảng (platform) vì nó có thể thay đổi đến mức bạn có thể sốc. Đây là một dạng sai lầm do sự thiếu cọ xát của lập trình viên. Vì thông thường họ chỉ dính với một môi trường nào đó và ôm lấy nó trong một thời gian dài. Khi họ chuyển sang triển khai ứng dụng ở một môi trường khác họ sẽ dễ dàng chết vì sự thiếu hiểu biết của chính mình. Khi đó những hàm đơn giản như sizeof(long) đôi khi lại là nguồn gốc gây ra những lỗi treo hệ thống bất chợt và vì thông thương đầu óc con người hay có chuyện "ngu" đi đôi với "lì" nên bạn không dễ dàng hiểu lý do tại sao vì mọi thứ trông không có vẻ gì là bất thường.
    Ví dụ: với Visual studio 2010 nếu bạn biên dịch chương trình cho
    - Window OS 64bit: sizeof(long) = 4 và sizeof(long*) = 8
    - Window OS 32bit: sizeof(long) = 4 và sizeof(long*) = 4
  2. Lưu ý tránh sử dụng các hằng số hệ thống (system constant) đặc biệt, các định nghĩa lại không chính thống (macro). Các hằng số này về bản chất chỉ là những định danh được một hệ thống cụ thể định nghĩa lại để hàm ý một giá trị hay một xử lý nào đó. Điều này giúp mã nguồn nhìn có vẻ mang tính người nhiều hơn tuy nhiên theo Bioz, với dân chuyên nghiệp nó không có ý nghĩa nhiều, đôi khi còn gây khó chịu, rắc rối hóa mã nguồn, giống taczan ở rừng quen rồi thì ra thành phố dù văn minh hơn chưa hẵn đã thấy tốt. Ví dụ trong hệ thống window bạn dùng "NULL", tuy nhiên hằng số này không tồn tại trong các nền tảng khác như linux. Do vậy thay vì dùng NULL bạn chỉ đơn giản hãy sử dụng 0. Ngoài ra NULL lại dễ gây hiểm lầm vì NULL về bản chất không phải là 0.
  3. Hãy luôn luôn chỉ khai báo biến ở đầu hàm trước tất cả các xử lý (ngay cả biến chạy cho vòng lặp) vì không phải trình biên dịch nào cũng cho phép bạn khai báo mọi nơi, mọi lúc. Mặt khác điều này cũng giúp bạn tập thói quen khai báo tài nguyên chương trình một cách có tính toán và chừng mực, tránh thói  bừa bãi trong lập trình...dễ gây ra họa về sau. 
  4. Luôn luôn viết cú pháp default trong một bộ switch case. Nhiều người nghĩ rằng mình viết mã ngắn, có khả năng cắt xén thì mình là chuyên nghiệp. Tuy nhiên tôi đảm bảo những tay chuyên nghiệp ấy lại luôn là những tay bị ảnh hưỡng tâm linh nặng nề do bị đám người đọc mã của họ chửi thề. Dù sao thì đó cũng không phải lý do tôi đưa ra lời khuyên này mà lý do chính là có nhiều trình biên dịch sẽ báo lỗi nếu không có xét default trong cấu trúc điều kiện switch.
  5. Không chú thích (comment) bằng phong cách C++ (//) trong C, khuyến cáo sử dụng (/**/) vì có vài trình biên dịch không phải anh em của Microsoft thường báo lỗi.
  6. Không dùng cú pháp tam thức: a > b ? a = 0 : a = 1. Tôi đã nói ở trên cao thủ thực sự không bao giờ nên khoa trương hay phô diễn trong quá trình code. Bioz luôn khuyến cáo các bạn hãy chọn đi theo con đường của những nhà phát triển chính quy, nề nếp, đừng đi theo phong cách làm mã nguồn mở của những tay cao thủ ngỗ ngáo trên giang hồ. Điều này tuy có thể thõa cái cái chí tang bồng, sự yêu thích phóng khoáng nhất thời của cá nhân bạn nhưng rồi sẽ có lúc hoặc là bạn tự bóp mình hoặc hại chết oan mạng đám đàn em muỗi mòng quanh bạn. Hơn thế nữa dạng cú pháp ở trên cũng không được chấp nhận ở nhiều trình biên dịch thế hệ củ, đơn giản dành cho thiết bị nhúng. 
  7. Định dạng đường dẫn của hệ thống tập tin và thư mục thường khác nhau trên các hệ thống khác nhau. Đây là một trong những yếu tố cần thận trọng trong quá trình phát triển trên một môi trường mới. Ví dụ: "c:\\bioz.cute" sẽ được chấp nhận trong window nhưng không được cho phép trong linux. Tuy nhiên "c://bioz.cute" thì sẽ làm việc trên cả hai môi trường. Hay trên linux "Bioz.cute" và "bioz.cute" là hai tập tin khác nhau, tuy nhiên trên window thì chúng chỉ là 1 (không thể tồn tại song song tại cùng cấp thư mục).
  8. Luôn luôn dùng return trong một hàm, cho dù bạn không return gì cả. Nghe điều này thật vô lí, tuy nhiên nhiều trình biên dịch sẽ báo lỗi khi không phát hiện thấy có return trong hàm xử lý. Về mặt lý luận thì ràng buộc này cũng có cái lợi là nó cho bạn cái nhìn rõ ràng hơn, chắc chắn hơn về vị trí thoát ra của một hàm.
  9. Luôn luôn chỉ định kiểu (Type) cho các biến static. Vì một vài trình biên dịch thế hệ cũ sử dụng int như là kiểu mặc định của biến static nên lập trình viên không cần phải chỉ ra kiểu của nó. Tuy nhiên vì hầu hết các trình biên dịch thế hệ mới không cho phép sự lười biếng này nên Một lần nữa quan điểm của Bioz là đừng tiếc một vài kí tự để rồi hối hận cả đời, lập trình là công việc của những kẻ kiên nhẫn, phóng khoáng nhưng có nguyên tắc. 
  10. Tránh sử dụng các mô hình (models) hay thư viện đặc thù, thuộc về hỗ trợ của hệ thống. Nếu có sử dụng hãy có gắng tách những xử lý này ra khỏi các tính toán của giải thuật. Ví dụ như các event handling model hay threading library, rõ ràng chúng sẽ hoàn toàn khác biệt trong các môi trường khác nhau như linux hay window. 
  11. Trong quá trình phát triển, cấu trúc dự án, cần chú ý quản lý độ sâu của phân cấp tham chiếu include, cũng như kích thước của tập tin mã nguồn. Điều này không chỉ nhằm tránh sự phức tạp một cách quá đáng của cấu trúc mà con tránh lỗi gây ra do giới hạn độ lớn kích thước của mã nguồn xử lý do trình biên dịch quy định.
  12. Không khai báo tham số tham chiếu bằng toán tử &, thay vì thế hãy luôn sử dụng * cho dù bạn chỉ muốn làm việc với kiểu giá trị đơn chứ không phải mảng. Cũng giống như một vài gợi ý mà tôi đề cập ở trên, chú ý này cũng vì rất nhiều các trình biên dịch dành cho thiết bị nhúng như ARM, DSP không hỗ trợ & trong danh sách tham số của hàm. 
  13. Có lẽ bạn luôn tự hào mình là cao thủ về C, tuy nhiên theo kinh nghiệm cá nhân tôi, càng cao thủ thì lại càng dễ tự bóp mình bằng các lỗi xuất phát từ những sai lầm có thể nói là hết sức ngớ ngẩn, hết sức căn bản ... hết sức thô thiển. Nó có thể chỉ đơn giản là độ ưu tiên của các phép toán. Vì vậy một trong những nguyên tắc rút ra từ đau thương đó là hãy cố gắng sử dụng cặp dấu "(" ")" thích hợp. Chúng sẽ giúp bạn phân định rạch ròi các thành phần của một công thức tính toán và tránh sai sót tới mức có thể, đồng thời sự cẩn trọng này còn giúp bạn vượt qua sự khác biệt trong đối xử ưu tiên phép toán nếu có của các trình biên dịch khác nhau. Hãy thử so sánh kết quả của:  a = b >> 1 + b >> 2 và a = (b >> 1) + (b >> 2).
  14. Cho dù bạn có là ai thì việc tạo ra cho mình một phong cách, dựng cho mình những nguyên tắc hữu ích tích lũy từ kinh nghiệm cọ xát trong công việc là cần thiết cho năng xuất, hiệu quả, và sự trong sáng trong kỹ năng lập trình của chính bạn, code convention nên là một bí cấp nội công xuyên suốt và không bao giờ thừa, tham khảo.
Chú ý: Bài viết này là dạng bài sẽ được cập nhật liên tục do nội dung mang tính tích lũy kinh nghiệm. Do vậy đừng thắc mắc khi ngày mai quay trở lại bạn thấy mọi thứ đã khác xưa.

Cập nhật gần nhất [30apri2013].

 
 Binh Nguyen - Bioz

Cảm nhận:

1 thảo luận:

Đề nghị ad xem lại cái #2, macro NULL được định nghĩa trong file stdlib.h . Đã là std rồi thì nó tồn tại trên mọi môi trường, đừng có quy chụp theo kiểu thiếu cơ sở.
__http://www.cplusplus.com/reference/cstdlib/

Đăng nhận xét

chia sẻ cho chúng tôi ý tưởng hay khó khăn của bạn ...