oOo VnSharing oOo

Go Back   Diễn đàn > VnSharing School > IT - Information Technology > Các lĩnh vực khác > Lập trình - Programming >

Trả lời
Kết quả 1 đến 2 của 2
 
  • Công cụ
  • Hiển thị
    1. [Bài dịch] Linux coding convention

      ~ Nếu bạn có copy qua đâu đó, xin hãy ghi rõ nguồn: vn-sharing.net ~


      Bài dịch: Linux coding convention


      Coding convention (quy tắc viết code) đơn giản là một tập hợp các phương pháp lập trình cho một ngôn ngữ, sao cho lập trình viên có thể viết ra những phần mềm dựa trên ngôn ngữ này có thể dễ dàng đọc, sữa chữa, phân tích, nâng cấp. Bất cứ một lập trình viên nào cũng sẽ cần đến những quy tắc này. (mình đã gặp rất nhiều người viết code tốt nhưng không sử dụng quy tắc nào cả, gây rất nhiều khó khăn khi maintain, và ngược lại - những người chăm bẵm cho từng dòng code của họ - dù code "đẹp" nhưng logic bên trong quá kém).

      Trong bài dịch này mình sẽ cố gắng viết ngắn gọn nhất có thể, và như vậy sẽ rút bớt lượng từ ngữ và code mẫu, nên sẽ mặc định người đọc đã có một lượng kiến thức nhất định về ngôn ngữ C. Không nên bắt đầu đọc coding convention trước khi học một ngôn ngữ nào đó. Và dù là bài dịch từ coding convention của Linux kernel nhưng nó đã được cắt bớt một vài thứ liên quan tới kernel development, tập trung hơn vào general-purpose C và thêm vào đó một số kinh nghiệm của mình. Bài viết gốc: https://www.kernel.org/doc/Documentation/CodingStyle

      Trong bài viết này mình xin giới thiệu coding convention cho C, của Linus Torvalds, tác giả chính của Linux kernel. Những quy tắc này đặc biệt có lợi nếu như bạn:
      * Là coder thuần C (hầu hết). C++ programmers cũng sẽ phát hiện ra thêm một chút gì đó.
      * Lập trình cho các hệ thống nhúng (embedded systems).
      * Sẽ/đang viết các phần mềm (chủ yếu) console/terminal chuẩn (80x24).
      * <..>


      Chương 1
      Thụt lề
      Một tab sẽ tương đương 8 khoảng trắng. Đặt tab rộng bằng 4 hoặc 2 khoảng trắng sẽ giống như cố gắng định nghĩa số Pi bằng 3 trong khi giá trị của nó là 3.14159.

      Việc thụt dòng 8 khoảng trắng này có một vài ý nghĩa nhất định. Một quy tắc cơ bản trong lập trình đó là bạn không nên lồng các khối lệnh quá sâu vào nhau, sẽ rất rối rắm. Ví dụ:
      PHP Code:
      if (myStruct->argv ENABLE_BACKFACE_CULLING) {
              
      glEnable(GL_CULL_FACE);
              
      myStruct->argv 0;

              if (!
      myStruct->argv) {
                      
      int flag 0;
                      
      printf("Enter further flags: ");
                      
      scanf("%i", &flag);
          
                      if (
      flag) {
                              
      /* xyz với flag */
                      
      }
              }

      Sử dụng cách thụt dòng này, bạn sẽ biết được khi nào đoạn code của mình đã bị thụt vào quá sâu, vì lúc đó code sẽ dồn hết vào bên phải, đặc biệt nếu bạn đang viết phần mềm chạy trên console.

      Cách thụt dòng này không có ý nghĩa trong switch - case vì một/nhiều case chỉ được thụt một lần trong switch. Nên tốt nhất đừng thụt case mà hãy thụt lệnh đứng trong case.
      PHP Code:
      switch(myStruct->renderDevice) {
      case 
      GL_COMPAT_1_3:
              
      /* Enable extensions and create VAOs (GL_ARB_vertex_array_object) */
              
      break;
      case 
      GL_CORE_3_3:
              
      /* Create VAOs */
              
      break;

      default:
              break;

      Nên:
      PHP Code:
      if (af();
      g();

      <..>

      5;
      2;
      z
      Không nên:
      PHP Code:
      if (af(); g();

      <..>

      = (2) = (5); 
      Chương 2
      Khoảng trắng và ngoặc

      Sẽ không có sự khác biệt nhiều trong cách đặt dấu đóng/mở ngoặc trong C, nhưng chúng ta nên sử dụng những gì mà K&R C đã đề xuất: Mở ngoặc ở cuối statement và đóng ngoặc ở một dòng riêng. Ví dụ:
      PHP Code:
      if (== 0) {
              
      f();

      Cách đặt dấu này áp dụng được cho các lệnh khối như if, switch, for, do, while, ngoại trừ định nghĩa function. Ví dụ:
      PHP Code:
      void f()
      {
              return;

      Vì function không thể lồng nhau nên ta sẽ xuống dòng riêng để mở ngoặc cho function, và mở ngoặc cuối dòng cho các trường hợp còn lại. Điều này cũng sẽ giúp bạn phân biệt.

      Một điều nữa: Dù dấu đóng ngoặc được đặt riêng biệt trên một dòng nhưng trong trường hợp của do(..) while lại khác:
      PHP Code:
      do {
              
      f();
      } while(
      != 0); 
      Nên nhất quán trong việc đặt dấu ngoặc trong các conditional statements.
      Không nên:
      PHP Code:
      if (== 0) {
              
      f();
              
      g();
      } else 
      (); 
      và:
      PHP Code:
      if (== 0) {
              
      f();

      Nên:
      PHP Code:
      if (== 0) {
              
      ();
              
      g();
      } else {
              
      h();


      PHP Code:
      if (== 0f(); 
      3.1: Khoảng trắng
      Sử dụng khoảng trắng sau từ khóa (chuẩn) của C, ngoại trừ toán tử và tên hàm (function).

      Từ khóa: if, switch, do, while, case, for
      Toán tử: sizeof

      Ví dụ:
      PHP Code:
      if (flags 0x002

      PHP Code:
      GLfloat *normalData malloc(sizeof *normalData); 
      Dấu '*' trong định nghĩa function nên được đặt dính vào tên function.
      PHP Code:
      void *glMapBuffer(..); 
      Điều này sẽ tránh được:
      PHP Code:
      inta, *b, *c/* Không nhất quán, bên trái và bên phải lẫn lộn, hơn nữa không thể có khai báo con trỏ liên tục 
      Rất nhiều programmers giỏi vẫn có sai sót trong việc đặt dấu '*'.

      Chương 4
      Đặt tên
      Việc đặt tên cho biến, hàm có lẽ sẽ tùy vào mỗi người. Nhưng ít nhất, đừng viết một loạt các functions có tên dạng:
      PHP Code:
      int DoSomething();
      int DoAnotherThing();
      int DoMoreThings(); 
      và ở đâu chui ra một function lạc loài:
      PHP Code:
      int foo(); 
      Cách đặt tên hàm phổ biến trong C có dạng:
      PHP Code:
      void do_something(); 
      Kiểu của C++ (Không hẳn, nhưng mình đã thấy có nhiều C++ projects theo cách đặt tên này):
      PHP Code:
      void DoSomething(); // Bắt đầu bằng chữ cái viết hoa và các từ tiếp theo cũng viết hoa chữ cái đầu. 
      Các hàm ẩn (thường không được gọi trực tiếp) trong C có thêm dấu '_' đặt trước.
      PHP Code:
      void _kefir();

      void kefir(); /* kefir() sẽ gọi _kefir(). Không nên expose _kefir() ra API. */ 
      Nếu phải rút gọn tên hàm, đừng làm nó quá khó đọc, thành vô nghĩa.
      PHP Code:
      void trieu_hoi_chaien(); /* function gốc. */
      void trhchaien(); /* Không nên - "trh" nghĩa là gì? */ 
      Chương 5
      typedefs
      ]

      Không nên dùng những thứ như
      PHP Code:
      typedef struct {
              
      int x;
              
      int y;
              
      int z;
              
      int w;
      vector;

      typedef vector *vector_t/* <-- Nó đây */ 
      Lật thêm 50 trang code nữa và bạn thấy
      PHP Code:
      vector_t myvector
      Và nếu không quay lại 50 trang code đầu để xem định nghĩa của struct vector, liệu bạn có thể đoán được vector_t là gì? Hơn nữa, bạn không thể biết được kiểu của myvec là con trỏ, hay nó chỉ là một đối tượng thông thường (vì đã được giấu tronng typedef). Một điều nữa là nếu dấu '*' được gắn trong typedef, bạn cũng sẽ không thể dùng nó để định nghĩa một danh sách con trỏ (== sự không nhất quán). Ví dụ:
      PHP Code:
      typedef vector *vector_t;

      <..>

      vector_t v, *v1, *v2/* Kiểu 1, không có sự nhất quán ở đây, và sai. */
      vector *v, *v1, *v2/* Tốt hơn nhiều, nhưng sẽ làm typedef và vector_t trở nên vô dụng. */ 
      Nên dùng trực tiếp:
      PHP Code:
      struct vector *myvec 
      .

      Việc này sẽ cho phép bạn kiểm soát được kiểu của myvec (là con trỏ, là một instance của struct vector).

      Vậy nên dùng _t khi nào? Khi mà các đối tượng được instanced từ các tên typedef-ed sẽ không được truy cấp trực tiếp (nội bộ) (giống như hàm _kefir() đã nói ở chương 4). Nói chung, những thứ có thể được truy cập trực tiếp thì không nên dùng _t đằng sau.

      Chương 6
      Hàm

      Tên hàm nên ngắn gọn và mỗi hàm chỉ nên làm một việc.
      Tên hàm nên tỉ lệ nghịch với mức độ thụt dòng và độ phức tạp của hàm đó. Ví dụ, một hàm lớn dùng switch-case để lọc ra và trả về kết quả, chỉ nên đơn giản có tên là select_something(). Tuy nhiên cũng có ngoại lệ. Hàm đơn giản không thể có cái tên quá dài. Như vậy không nên:
      PHP Code:
      int add_two_numbers_into_one_and_return_the_result(int aint b)
      {
              return 
      b;

      Không nên đặt quá nhiều biến local vào một function. Với prototypes, tốt nhất là kèm thêm tên parameter vào parameter đó, đặc biệt là đối với những hàm có từ 3 parameters trở lên. Ví dụ:
      PHP Code:
      int make_plane(intintintint); /* ??? */
      int make_plane(int aint bint cint d); /* Tốt hơn rồi! */ 

      Chương 7
      Commenting

      Comment thì rất tốt, nhưng comment quá nhiều lại là một vấn đề. Code tồi thì có cố gắng giải quyết cách hoạt động trong comment của nó cũng là vô ích.
      Nói chung, nên giải thích đoạn code của bạn LÀM GÌ, không phải LÀM THẾ NÀO. Hạn chế commenting trong thân hàm. Comment quá nhiều: 1> Bạn đã viết một hàm tồi, chúc mừng. hoặc 2> Chia nhỏ các phần cần comment đó ra các hàm nhỏ hơn, cho đến lúc không còn phải comment thật nhiều trong thân hàm nữa..

      Commenting có nhiều cách, và mỗi cách tùy vào mỗi người, nên mình sẽ không liệt kê ra ở đây. Bạn có thể comment kiểu multi-line:
      PHP Code:
      // Header
      //
      // Line 1
      // Line 2
      // .. 
      hoặc
      PHP Code:
      /*
       * Header
       *
       * Line 1
       * Line 2
       * ..
       */ 
      đều được. Đây là kiểu của mình:
      PHP Code:
      /*
       * Draws a simple RPG-like dialogue, with a backdrop bitmap, a cursor and a string list.
       * String list can be passed unformatted (formatting is done inside the function).
       *
       * backDrop
       *    Backdrop bitmap.
       *
       * cursor
       *    Cursor bitmap (the flashing cursor at the bottom of the dialogue).
       * 
       * stringList
       *    A list of strings.
       *    The function will read the strings line by line until the final string is reached, and then returns.
       *    At this time, only TEXT *is supported (which has the same functionality as a STRING  *).
       *
       * backDropLayer
       *    Determines the rendering order.
       *
       * backDropX, backDropY
       *    x and y position of the backdrop image. Cursor and string list follows automatically.
       *
       * __todo: Add avatar support.
       * __todo: Add support for passing 2D vectors as position.
       * __todo: Add support for STRING  * aside of TEXT *.
       */ 
      Chương 8
      Macros và Enumerations.


      Macros
      Macros khai báo dưới dạng constant phải được viết hoa toàn bộ, còn các macros khai báo dưới dạng hàm inline (well.. not actually xD) có thể cho phép naming như naming một function.

      PHP Code:
      #define SOME_CONSTANT (1<<2)
      #define assert(x) \
              
      do { \
                      if(!(
      x)) \
                              
      __lassert(x);
                      } while(
      false
      Khai báo biến macros phải không được trùng với nơi mà macro được inlined.
      PHP Code:
      #define foo(x) \
              
      int bar 
      Đoạn code sau sẽ không chạy.
      PHP Code:
      int bar 0;
      foo(5); 
      Đóng ngoặc macros nếu có ít nhất 1 toán tử trong macro.
      PHP Code:
      #define COMPONENT_SWIZZLING (COMPONENT_FLAGS & (1<<2)) 
      Nếu không đưa vào hai dấu ngoặc, COMPONENT_FLAGS sẽ được & với 1 và sau đó << với 2.

      Enumerations
      Nên sử dụng enumerations nếu bạn phải thay đổi thứ tự liên tục. Với đoạn code sau:

      PHP Code:
      #define GPU_HAS_FEATURE_A 0x0001
      #define GPU_HAS_FEATURE_B 0x0002
      <..>
      #define GPU_HAS_FEATURE XZ 0x0091 
      Sẽ là vấn đề nếu bạn muốn nhét một GPU_HAS_FEATURE_F vào đâu đó giữa GPU_HAS_FEATURE_E va GPU_HAS_FEATURE_G. Thật ra có thể cho GPU_HAS_FEATURE_F một con số bất kì nhưng như thế không "sạch" và có thể vô tình cho trùng với một hằng số nào đó. Dùng enumerations, ta chỉ cần thêm GPU_HAS_FEATURE_F vào và compiler sẽ tự động lo phần còn lại.

      Chương 9
      Functions có trả về

      Sẽ có nhiều trường hợp mà bạn cần phân vân nên trả về functions theo kiểu boolean (0, 1) hay kiểu integers. Nếu không kiên định, có thể gây ra không nhất quán giữa hàm trả về.

      Quy tắc chung: Nếu tên hàm mang một mệnh lệnh, một hành động (ví dụ: import_table(), boot_PC()), hàm đó nên trả về một integer. Nếu tên hàm có chứa danh từ và tính từ/động từ nằm ở vị ngữ (ví dụ: GL_up() (GL = danh từ, up = động từ)), nó nên trả về một boolean.

      Các hàm tính toán: sin(), pow(), tan(), floor(),... không lệ thuộc vào kiểu trả về này vì giá trị trả về của chúng là một kết quả tính toán. Thường thì chúng sẽ trả về một giá trị thể hiện out of range, hoặc pass lỗi sang global states, chờ được query. (giống OpenGL với glGetError(), glGetProgramiv(), glGetShaderiv(),..) nếu phát hiện có lỗi.
      Sửa lần cuối bởi Marshmallow; 13-05-2015 lúc 12:53.
      Trả lời kèm trích dẫn

    2. #2
      Tham gia ngày
      10-08-2016
      Bài viết
      22
      Cấp độ
      3
      Reps
      114
      quá hay
      Trả lời kèm trích dẫn

    Đánh dấu

    Quyền viết bài

    • Bạn không thể đăng chủ đề mới
    • Bạn không thể gửi trả lời
    • Bạn không thể gửi đính kèm
    • Bạn không thể sửa bài
    •  

    Theo giờ GMT +7. Bây giờ là 04:59.

    Powered by vBulletin.
    Copyright© 2024 vBulletin Solutions, Inc. All rights reserved.
    Board of Management accepts no responsibility legal of any resources which is shared by members.