Menu

Chú thích trong C: giải thích // và /* */

C có hai kiểu chú thích, một dòng // và nhiều dòng /* */, với lịch sử khác nhau và một cái bẫy khi lồng nhau. Đây là cách dùng cả hai, cùng những gì đáng chú thích và những gì không.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Chú thích (comment) là phần văn bản mà trình biên dịch vứt bỏ. Nó tồn tại hoàn toàn cho những người đọc code sau này, mà một trong số họ thường chính là bạn. C có hai dạng chú thích, và biết khi nào nên dùng dạng nào chỉ mất khoảng hai phút.

Hai dạng chú thích

Chạy thử: đầu ra chỉ có một dòng. Cả hai chú thích đã bị xóa trước khi trình biên dịch kịp phân tích chương trình: chúng không tốn gì lúc chạy và không thêm gì vào tệp thực thi.

// kéo dài đến hết dòng vật lý. Không gì có thể đứng sau nó trên dòng đó, nên đoạn sau không hoạt động như vẻ ngoài của nó:

int x = 5;  // set x to five  int y = 6;   /* y is never declared */

/* ... */ kết thúc tại */ đầu tiên, dù nó nằm ở đâu. Nó có thể bắt đầu và kết thúc giữa dòng, đôi khi khá hữu ích:

int total = price /* before tax */ + shipping;

Vì sao có hai kiểu

/* */ là của C nguyên bản, từ năm 1972. // đến từ C++ và chỉ chính thức được thêm vào C ở C99. Lịch sử đó giải thích một điều bạn sẽ để ý khi đọc code cũ: các thư viện viết để chạy được trên C89 dùng /* */ cả cho chú thích một dòng, vì // sẽ gây lỗi biên dịch trên những bộ công cụ cũ mà chúng vẫn hỗ trợ.

Ngày nay mọi trình biên dịch bạn có thể dùng đều chấp nhận cả hai. Hãy dùng // cho các ghi chú thông thường và /* */ khi chú thích thật sự trải qua nhiều dòng. Nếu bạn nhắm tới một trình biên dịch nhúng (embedded) rất cũ, hãy kiểm tra trước khi dựa vào //.

Chú thích không lồng nhau được

Đây là cái bẫy thật sự duy nhất:

/* Disable this section for now
   int a = compute();
   /* the classic helper - keep an eye on it */
   int b = a * 2;
*/

Chú thích khối kết thúc tại */ đầu tiên, tức cái ở dòng 3. Dòng 4 và 5 khi đó lại thành code chạy thật, và */ thừa ở dòng 6 là lỗi cú pháp. Thông báo của trình biên dịch chỉ vào dòng cuối và hoàn toàn không giúp gì cho việc tìm nguyên nhân.

Cách sửa là dùng bộ tiền xử lý, vốn xử lý được việc lồng nhau:

#if 0
    int a = compute();
    /* the classic helper - keep an eye on it */
    int b = a * 2;
#endif

#if 0 không bao giờ đúng, nên bộ tiền xử lý xóa mọi thứ cho tới #endif trước khi trình biên dịch nhìn thấy. Cách này vẫn đúng dù bên trong có chú thích, dấu nháy hay các khối #if khác, và rất dễ tìm lại khi bạn dọn dẹp.

Vô hiệu hóa code khi gỡ lỗi

Tạm thời bỏ một dòng là cách dùng chú thích phổ biến nhất hằng ngày. Khi chương trình chạy sai, tắt từng câu lệnh một sẽ cho bạn biết câu lệnh nào là thủ phạm.

Bỏ chú thích dòng printf rồi chạy lại để xem vòng lặp dần tạo ra kết quả. Lần theo bằng cách in ra không thanh lịch gì, nhưng trong C nó nhanh và luôn hiệu quả: trình gỡ lỗi (debugger) cho bạn biết nhiều hơn, còn một printf cho bạn biết điều gì đó ngay lập tức.

Hai thói quen giúp việc này không trở thành mớ hỗn độn. Hãy xóa code đã bị vô hiệu hóa trước khi commit; hệ thống quản lý phiên bản đã nhớ phiên bản cũ nên bạn không cần nhớ. Và khi cố ý để lại một dòng bị tắt, hãy ghi lý do ngay bên cạnh.

Chú thích tài liệu

Một chú thích khối phía trên hàm là nơi bạn giải thích hàm làm gì, các tham số có nghĩa gì, và bất cứ điều gì bất ngờ về nó.

Các công cụ như Doxygen đọc những chú thích có cấu trúc như thế này và tạo ra tài liệu tham khảo. Kiểu riêng của Doxygen dùng /** ... */ với các thẻ @param và @return:

/**
 * Converts Celsius to Fahrenheit.
 * @param c temperature in Celsius
 * @return the same temperature in Fahrenheit
 */
double celsius_to_fahrenheit(double c);

Với code của riêng bạn, kiểu nào cũng được. Điều quan trọng là chú thích nằm cạnh phần khai báo mà mọi người đọc, thường là trong tệp header, thay vì bị chôn trong phần cài đặt.

Điều gì đáng chú thích

Quy tắc vẫn đứng vững khi gặp các codebase thật: chú thích lý do, không chú thích việc code làm.

i++;  // increment i          <- says nothing the code did not
/* Skip the BOM: files exported by the old system start with
   three bytes that are not part of the data. */
offset += 3;

Chú thích thứ hai chứa thông tin không có ở đâu trong code. Chú thích thứ nhất là thứ gây nhiễu, rồi sẽ có lúc mâu thuẫn với chính dòng nó mô tả, vì chú thích không được cập nhật khi code thay đổi.

Những thứ thật sự đáng chú thích, riêng trong C:

  • Ai sở hữu vùng nhớ này. Nếu một hàm trả về con trỏ mà nơi gọi phải free, hãy nói rõ. C không có cách nào thể hiện điều đó trong kiểu dữ liệu.
  • Đơn vị và khoảng giá trị. int timeout; rất mơ hồ: giây hay mili giây?
  • Tính đúng đắn không hiển nhiên. Vì sao vòng lặp dừng ở n - 1, vì sao phép ép kiểu này an toàn, vì sao bộ đệm là 256 byte.
  • Những điều kỳ lạ có chủ ý. Code trông như bug nhưng không phải bug sẽ thu hút các "bản sửa" từ người đọc sau này, trừ khi nó được ghi chú rõ.

Chú thích đó xứng đáng có mặt: dòng bên dưới trông như thừa nhưng không phải.

Chú thích bên trong chuỗi không phải chú thích

Một chi tiết cuối cùng. Các ký hiệu chú thích không có ý nghĩa đặc biệt bên trong một chuỗi ký tự hay một hằng ký tự:

Cả hai dòng đều được in ra đầy đủ. Trình biên dịch tách các chuỗi trước khi tìm chú thích, nên // bên trong dấu nháy chỉ đơn giản là hai ký tự. (%% ở dòng đầu là cách in ra một dấu phần trăm bằng printf: một dấu % đứng riêng sẽ bắt đầu một đặc tả định dạng.)

Câu hỏi thường gặp

Viết chú thích trong C như thế nào?

Có hai cách. // this is a comment kéo dài đến hết dòng. /* this is a comment */ có thể trải qua bao nhiêu dòng cũng được và kết thúc tại */. Cả hai đều bị loại bỏ trước khi biên dịch, nên không bao giờ ảnh hưởng đến chương trình.

C có hỗ trợ chú thích // không?

Có, từ C99. Kiểu chú thích này được mượn từ C++ và ngày nay được hỗ trợ ở mọi nơi. Chỉ những trình biên dịch C89 thật sự cổ mới từ chối nó, đó là lý do code rất cũ dùng /* */ cho mọi thứ, kể cả chú thích một dòng.

Có lồng chú thích vào nhau trong C được không?

Không. /* outer /* inner */ still outer */ kết thúc tại */ đầu tiên, để lại still outer */ thành code lỗi. Để vô hiệu hóa một khối đã chứa sẵn chú thích /* */, hãy dùng #if 0 ... #endif, cách này lồng nhau được đúng.

Làm sao vô hiệu hóa (comment out) một khối code trong C?

Bọc nó trong /* */ nếu bên trong không có chú thích khối nào, hoặc thêm // vào đầu mỗi dòng. Cách chắc chắn nhất cho các vùng lớn là #if 0 ở trước và #endif ở sau: bộ tiền xử lý xóa mọi thứ ở giữa, và cách này vẫn đúng dù bên trong có chú thích hay dấu nháy.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu