Chúng ta có thể viết bình luận trong tên biến không?


145
int main()
{
     i/*nt*/a = 10;
     return 0;
}

Nếu tôi có mã trên và tôi muốn đếm số mã thông báo, nó sẽ là 14 hay 13 mã thông báo?

Viết bình luận bên trong tên biến có hợp lệ không? Bạn có thể giả định rằng int i, int a, int iađược định nghĩa trên toàn cầu.


13
Trong C "truyền thống" trước ANSI, ít nhất là khi được GNUcpp -traditional triển khai , nó sẽ mở rộng thành ia = 10;.
Nate Eldredge

37
thật là một câu hỏi thú vị - tại sao nó chưa bao giờ xảy ra với tôi trước đây?
StephenBoesch

177
@javadba: Bởi vì những người nhạy cảm sẽ không nghĩ đến việc làm như vậy?
jamesqf

5
Nếu bạn thực sự muốn làm điều đó, bạn có thể chuyển sang Fortran. Khoảng trắng bên ngoài chuỗi bị loại bỏ trong giai đoạn phân tích cú pháp đầu tiên.
mpez0

3
Tôi đã định sửa tiêu đề thành ".... trong tên biến ..." nhưng sau đó nhận ra rằng bạn có thể thực sự có nghĩa là "giữa". (Tôi muốn chỉnh sửa nó vì câu trả lời cho tiêu đề ban đầu là "Tại sao, rõ ràng là!" Phần quan trọng là "không có khoảng trắng".) Liệu tiêu đề "Có nhận xét (không có khoảng trắng xung quanh) các mã thông báo trong C không?" bày tỏ câu hỏi thực tế của bạn?
Peter - Phục hồi Monica

Câu trả lời:


197

Các chú thích bị xóa trong giai đoạn 3 của quá trình dịch chương trình 1 : mỗi chú thích được thay thế bằng một ký tự khoảng trắng. vì vậy nhận xét /*nt*/chắc chắn không phải là một mã thông báo.

Nếu không có int, main, i, ahoặc returnđược định nghĩa là tiền xử lý macro, phân tích các chương trình sản xuất 14 thẻ (không phải 13):

int main ( ) { i a = 10 ; return 0 ; }

Trừ khi iđược định nghĩa là kiểu có typedefcâu lệnh, nếu không sẽ xảy ra lỗi cú pháp do i akhông khớp với quy tắc trong ngữ pháp C.

Vì vậy bạn không thể viết bình luận bên trong tên biến, bình luận chia mã định danh thành 2 mã thông báo riêng biệt. Điều này đúng với bất kỳ tiền xử lý nào và mã thông báo ngôn ngữ C 2 .

Tuy nhiên, lưu ý rằng bạn có thể chèn chú thích ở những vị trí bất thường như giữa các toán tử một ngôi và toán hạng của chúng hoặc giữa #và chỉ thị tiền xử lý và các đối số của nó:

/**/#/**/include/**/<stdio.h>/**///////////////////////
/**/#/**/define/**/STAT/**/(/**/a/**/)/**/-/**/1/**////
/**/#/**/ifdef/**/STAT/**//////////////////////////////
/**/int/**/main/**/(/**/)/**/{/**//////////////////////
/**/int/**/a/**/=/**/+/**/1/**/;/**////////////////////
/**/printf/**/(/**/"Hello "/**/"world!\n"/**/)/**/;/**/
/**/return/**/STAT/**/;/**/////////////////////////////
/**/}/**///////////////////////////////////////////////
/**/#/**/endif/**//////////////////////////////////////

Nhưng định nghĩa macro ở trên không xác định macro giống hàm mà là macro thông thường STATmở rộng thành ( a ) - 1.

Các tên biến, giống như bất kỳ mã thông báo nào khác có thể được chia theo dòng mới thoát. Dòng mới đã thoát là các chuỗi hoặc \ngay sau đó là một dòng mới. Các trình tự này được loại bỏ khỏi mã nguồn trong giai đoạn 2 của quá trình dịch chương trình. Mục đích chính của chúng là phá vỡ các định nghĩa macro dài trên nhiều dòng.

Dưới đây là đoạn mã 3 tạo ra 14 mã thông báo giống nhau:

\
i\
nt\
 ma\
in()
{\
i/\
*nt\
*/a \
= 10;
r\
et\
urn\
 0;}

Hãy để ý cách trình tô màu mã bỏ sót các từ khóa được cắt lát và cắt hạt lựu và nhận xét :)


1) Hành vi này đã được chỉ định trong ANSI-C hay còn gọi là C89. Một số trình biên dịch cổ đại có hành vi khác biệt một cách tinh vi dẫn đến việc dán mã thông báo, nhưng những đặc thù như vậy chỉ quan tâm đến lịch sử.

2) Bạn gần như có thể chèn chú thích bên trong một hằng chuỗi bằng cách tận dụng thực tế là các hằng chuỗi liền kề được nối với nhau trong giai đoạn 6 của quá trình dịch chương trình: printf("Hello "/* my name is Luca */"world!\n");

3) Phong cách trình bày Cây thông Giáng sinh này không được sử dụng trong các chương trình thực, nó minh họa cách lạm dụng khả năng xử lý đầu vào của C. Các thủ thuật phức tạp hơn đã giành chiến thắng trong Cuộc thi Mã C Obfuscated Quốc tế


Tôi tự hỏi tại sao Tiêu chuẩn yêu cầu không được phân tách ký tự gạch chéo ngược tiếp nối dòng với ký tự dấu cách dòng mới bằng các ký tự khoảng trắng khác, vì không có trường hợp nào khác mà các khoảng trống ở cuối hoặc việc thiếu chúng sẽ có ý nghĩa về mặt ngữ nghĩa và một số định dạng tệp văn bản có thể không có thể phân biệt các dòng kết thúc bằng khoảng trống với các dòng không?
supercat

@supercat: Tôi đồng ý. Điều này cũng sẽ đề cập đến trường hợp các tệp đến từ các hệ thống kế thừa sử dụng chuỗi CR LF làm phần cuối dòng gây ra lỗi biên dịch trên hệ thống unix không nhận ra các dòng mới thoát bao gồm một \rtrước \n. Tuy nhiên, có một trường hợp mà điều này sẽ phản tác dụng: các nhận xét có thể chứa các ký tự theo sau là khoảng trắng, đặc biệt để tránh dán dòng:const char *path = "C:\\"; // the default path is C:\ 
chqrlie

Tiêu chuẩn không yêu cầu rằng các tệp văn bản phải có khả năng hỗ trợ các ký tự khoảng trắng ở cuối dòng. Viết một bình luận như The path is "C:\"vậy có vẻ tốt hơn là để ý nghĩa của mã phụ thuộc vào các dòng mới.
supercat

1
Về mặt kỹ thuật, tiêu chuẩn không đưa ra yêu cầu như vậy, vì giai đoạn dịch 1 bị bỏ qua được phép tách các khoảng trống ở cuối khỏi mọi dòng, miễn là hành vi này được ghi lại.
zwol

4
Câu trả lời này đi một chặng đường dài, chỉ để chứng minh rằng không có câu hỏi ngu ngốc nào. Làm tốt.
Overbryd

65

Theo quan điểm từ vựng, một nhận xét cũng giống như khoảng trắng.

Phần 6.4p3 của tiêu chuẩn C liên quan đến các yếu tố từ vựng nêu rõ:

... Các mã thông báo tiền xử lý có thể được phân tách bằng khoảng trắng ; điều này bao gồm các nhận xét (được mô tả sau) hoặc các ký tự khoảng trắng (dấu cách, tab ngang, dòng mới, tab dọc và nguồn cấp dữ liệu biểu mẫu) hoặc cả hai. ...

Cụ thể hơn, một bình luận được dịch vào một không gian duy nhất. Điều này được quy định trong phần 5.1.1.2p3:

Tệp nguồn được phân tách thành các mã thông báo tiền xử lý và chuỗi ký tự khoảng trắng (bao gồm cả nhận xét). Tệp nguồn sẽ không kết thúc bằng mã thông báo tiền xử lý một phần hoặc trong một phần nhận xét. Mỗi nhận xét được thay thế bằng một ký tự khoảng trắng. Các ký tự dòng mới được giữ lại. Việc mỗi dãy ký tự khoảng trắng không phải là ký tự trắng có được giữ lại hay thay thế bằng một ký tự khoảng trắng hay không là do việc triển khai xác định.

Để minh họa điều này, nếu bạn chuyển mã của mình qua bộ tiền xử lý, bạn sẽ nhận được:

  int main()
  {
       i a = 10;
       return 0;

  }

Vì vậy, các nhận xét, như khoảng trắng, dùng để phân tách các mã thông báo.

Điều này có nghĩa là mã sẽ chứa 14 mã thông báo, không phải 13.


26

Kết quả sẽ như thể bạn đã viết:

i a = 10;

KHÔNG PHẢI:

ia = 10;

12

Xem bản dịch (hay còn gọi là biên dịch) Giai đoạn 3 , bước 2: "Mỗi nhận xét được thay thế bằng một ký tự khoảng trắng" .

Vì vậy, về mặt khái niệm, i/*nt*/atrở thành i ađiểm đó.


Nhận xét không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Machavity

1

chỉ cần kiểm tra đoạn mã của bạn ở dạng nào

     int main()
    {
        int i/*nt*/a = 10;
        return 0;
    }

sẽ có sau khi xử lý trước. Chỉ cần thêm cờ "-E" vào trình biên dịch của bạn, gcc -E myscript.c và bạn sẽ nhận được kết quả:

e.sharaborin@landau:~$ gcc -E myscript.c
# 1 "myscript.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "myscript.c"
int main()
{
    int i a = 10;
    return 0;
}

Và rõ ràng, bạn có thể kết luận rằng có một sai lầm.


-9

Đúng, bạn có thể làm điều đó. Nhận xét sẽ được trình biên dịch bỏ qua. Chúng sẽ không ảnh hưởng đến biến. Nó sẽ giống nhau, chỉ cần đừng quên kết thúc thẻ bình luận.


5
"sẽ không ảnh hưởng đến biến. Nó sẽ giống nhau" Giống cái gì, i ahoặc ia?
HolyBlackCat
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.