Các quy tắc về việc sử dụng dấu gạch dưới trong định danh C ++ là gì?


930

C ++ thường đặt tên các biến thành viên bằng một số loại tiền tố để biểu thị thực tế rằng chúng là các biến thành viên, thay vì các biến hoặc tham số cục bộ. Nếu bạn đến từ nền MFC, có thể bạn sẽ sử dụng m_foo. myFooThỉnh thoảng tôi cũng thấy .

C # (hoặc có thể chỉ là .NET) dường như khuyên bạn chỉ nên sử dụng dấu gạch dưới, như trong _foo. Điều này có được phép theo tiêu chuẩn C ++ không?


3
Các glibc thủ công trang về có thể được tìm thấy tại gnu.org/software/libc/manual/html_node/Reserved-Names.html Chỉnh sửa: xem thêm opengroup.org/onlinepubs/009695399/functions/xsh_chap02_02.html
CesarB

6
Chỉ cần lưu ý rằng sự thiếu hiểu biết của các quy tắc này không nhất thiết ngụ ý rằng mã của bạn sẽ không biên dịch hoặc chạy, nhưng có khả năng mã của bạn sẽ không thể di chuyển đến các trình biên dịch và phiên bản khác nhau, vì không thể đảm bảo rằng sẽ không có tên đụng độ. Để sao lưu điều này, tôi biết về việc triển khai nhất định một hệ thống quan trọng đã được sử dụng như một quy ước đặt tên cho chữ in hoa ở khắp mọi nơi. Không có lỗi do điều này. Tất nhiên đó là thực hành xấu.
g24l

Câu trả lời:


852

Các quy tắc (không thay đổi trong C ++ 11):

  • Dành riêng trong mọi phạm vi, bao gồm cả việc sử dụng làm macro thực hiện :
    • các định danh bắt đầu bằng một dấu gạch dưới được theo sau bởi một chữ cái viết hoa
    • định danh chứa dấu gạch dưới liền kề (hoặc "dấu gạch dưới kép")
  • Dành riêng trong không gian tên toàn cầu:
    • định danh bắt đầu bằng dấu gạch dưới
  • Ngoài ra, mọi thứ trong stdkhông gian tên được bảo lưu. (Tuy nhiên, bạn được phép thêm các chuyên môn mẫu.)

Từ tiêu chuẩn C ++ 2003:

17.4.3.1.2 Tên toàn cầu [lib.global.names]

Một số bộ tên và chữ ký chức năng luôn được dành riêng cho việc thực hiện:

  • Mỗi tên có một dấu gạch dưới kép ( __) hoặc bắt đầu bằng dấu gạch dưới theo sau là một chữ cái viết hoa (2.11) được dành riêng cho việc thực hiện cho bất kỳ việc sử dụng nào.
  • Mỗi tên bắt đầu bằng dấu gạch dưới được dành riêng cho việc triển khai để sử dụng làm tên trong không gian tên toàn cầu. 165

165) Tên như vậy cũng được bảo lưu trong không gian tên ::std(17.4.3.1).

Bởi vì C ++ dựa trên tiêu chuẩn C (1.1 / 2, C ++ 03) và C99 là một tham chiếu quy phạm (1.2 / 1, C ++ 03), những điều này cũng được áp dụng, từ Tiêu chuẩn C năm 1999:

7.1.3 Định danh dành riêng

Mỗi tiêu đề khai báo hoặc xác định tất cả các mã định danh được liệt kê trong phần phụ liên quan của nó và tùy ý khai báo hoặc xác định các định danh được liệt kê trong các thư mục và định danh thư viện tương lai liên quan luôn được dành riêng cho bất kỳ mục đích sử dụng nào hoặc để sử dụng làm định danh phạm vi tệp.

  • Tất cả các định danh bắt đầu bằng một dấu gạch dưới và một chữ cái viết hoa hoặc một dấu gạch dưới khác luôn được dành riêng cho bất kỳ việc sử dụng.
  • Tất cả các mã định danh bắt đầu bằng dấu gạch dưới luôn được dành riêng để sử dụng làm định danh với phạm vi tệp trong cả không gian tên thẻ thông thường và thẻ.
  • Mỗi tên macro trong bất kỳ điều khoản phụ nào sau đây (bao gồm các hướng thư viện trong tương lai) được dành riêng để sử dụng như được chỉ định nếu bao gồm bất kỳ tiêu đề liên quan nào của nó; trừ khi có quy định rõ ràng khác (xem 7.1.4).
  • Tất cả các mã định danh có liên kết ngoài trong bất kỳ điều khoản nào sau đây (bao gồm cả các hướng thư viện trong tương lai) luôn được dành riêng để sử dụng làm định danh với liên kết ngoài. 154
  • Mỗi mã định danh có phạm vi tệp được liệt kê trong bất kỳ điều khoản phụ nào sau đây (bao gồm các hướng thư viện trong tương lai) được dành riêng để sử dụng làm tên macro và làm định danh với phạm vi tệp trong cùng một không gian tên nếu có bất kỳ tiêu đề liên quan nào của nó.

Không có định danh khác được bảo lưu. Nếu chương trình khai báo hoặc định nghĩa một mã định danh trong ngữ cảnh được bảo lưu (ngoài sự cho phép của 7.1.4) hoặc định nghĩa một mã định danh dành riêng là tên macro, thì hành vi không được xác định.

Nếu chương trình xóa (với #undef) bất kỳ định nghĩa vĩ mô nào của mã định danh trong nhóm đầu tiên được liệt kê ở trên, hành vi không được xác định.

154) Danh mục các định danh dè chừng liên kết bên ngoài bao gồm errno, math_errhandling, setjmp, và va_end.

Những hạn chế khác có thể được áp dụng. Ví dụ: tiêu chuẩn POSIX dự trữ rất nhiều số nhận dạng có khả năng hiển thị trong mã thông thường:

  • Tên bắt đầu bằng Echữ in theo chữ số hoặc chữ in hoa:
    • có thể được sử dụng cho tên mã lỗi bổ sung.
  • Tên bắt đầu bằng ishoặc totheo sau là một chữ cái viết thường
    • có thể được sử dụng cho các chức năng kiểm tra và chuyển đổi ký tự bổ sung.
  • Tên bắt đầu LC_bằng chữ cái viết hoa
    • có thể được sử dụng cho các macro bổ sung chỉ định các thuộc tính miền địa phương.
  • Tên của tất cả các hàm toán học hiện có có fhoặc lđược bảo lưu
    • cho các hàm tương ứng hoạt động trên các đối số float và long tương ứng.
  • Tên bắt đầu SIGbằng chữ cái viết hoa được bảo lưu
    • cho tên tín hiệu bổ sung.
  • Tên bắt đầu SIG_bằng chữ cái viết hoa được bảo lưu
    • cho các hành động tín hiệu bổ sung.
  • Tên bắt đầu bằng str, memhoặc wcstheo sau là một chữ cái viết thường được bảo lưu
    • cho các hàm chuỗi và mảng bổ sung.
  • Tên bắt đầu bằng PRIhoặc SCNtheo sau bởi bất kỳ chữ cái viết thường hoặcX được bảo lưu
    • cho các macro định dạng bổ sung
  • Tên kết thúc bằng _tđược bảo lưu
    • cho tên loại bổ sung.

Mặc dù việc sử dụng các tên này cho mục đích riêng của bạn ngay bây giờ có thể không gây ra sự cố, nhưng chúng làm tăng khả năng xung đột với các phiên bản tương lai của tiêu chuẩn đó.


Cá nhân tôi chỉ không bắt đầu định danh với dấu gạch dưới. Bổ sung mới cho quy tắc của tôi: Không sử dụng dấu gạch dưới kép ở bất cứ đâu, điều này rất dễ vì tôi hiếm khi sử dụng dấu gạch dưới.

Sau khi thực hiện nghiên cứu về bài viết này, tôi không còn kết thúc các định danh của mình _t vì điều này được bảo lưu theo tiêu chuẩn POSIX.

Các quy tắc về bất kỳ định danh kết thúc với _tlàm tôi ngạc nhiên rất nhiều. Tôi nghĩ rằng đó là một tiêu chuẩn POSIX (chưa chắc chắn) đang tìm kiếm sự làm rõ và chương và câu chính thức. Đây là từ hướng dẫn sử dụng GNU libtool , liệt kê các tên dành riêng.

CesarB đã cung cấp liên kết sau đến các biểu tượng và ghi chú dành riêng cho POSIX 2004 'rằng nhiều tiền tố và hậu tố dành riêng khác ... có thể được tìm thấy ở đó'. Các biểu tượng dành riêng POSIX 2008 được xác định ở đây. Các hạn chế có phần nhiều sắc thái hơn so với những hạn chế ở trên.


14
Tiêu chuẩn C ++ không "nhập khẩu" C, phải không? Họ nhập các tiêu đề nhất định, nhưng không phải là toàn bộ ngôn ngữ, hoặc quy tắc đặt tên, theo như tôi biết. Nhưng vâng, một trong những điều làm tôi ngạc nhiên là tốt. Nhưng vì nó là C, nó chỉ có thể áp dụng cho ns toàn cầu. Nên an toàn khi sử dụng _t bên trong các lớp học khi tôi đọc nó
jalf

27
Tiêu chuẩn C ++ không "nhập khẩu" Tiêu chuẩn C. Nó tham chiếu Tiêu chuẩn C. Giới thiệu thư viện C ++ cho biết "Thư viện cũng cung cấp các tiện ích của Thư viện C chuẩn". Nó thực hiện điều đó bằng cách bao gồm các tiêu đề của thư viện C Standard với các thay đổi phù hợp, nhưng không phải bằng cách "nhập" nó. Tiêu chuẩn C ++ có một bộ quy tắc riêng mô tả các tên dành riêng. Nếu một tên dành riêng trong C nên được dành riêng trong C ++, đó là nơi để nói điều này. Nhưng Tiêu chuẩn C ++ không nói như vậy. Vì vậy, tôi không tin rằng những thứ dành riêng trong C được dành riêng trong C ++ - nhưng tôi cũng có thể sai.
Johannes Schaub - litb

8
Đây là những gì tôi tìm thấy về vấn đề "_t": n1256 (C99 TC3) nói: "Tên Typedef bắt đầu bằng int hoặc uint và kết thúc bằng _t" được bảo lưu. Tôi nghĩ rằng vẫn cho phép sử dụng các tên như "foo_t" - nhưng tôi nghĩ chúng sau đó được POSIX bảo lưu.
Johannes Schaub - litb

59
Vậy 'dung sai' được POSIX bảo lưu khi nó bắt đầu bằng 'đến' + một chữ cái viết thường? Tôi đặt cược rất nhiều mã phá vỡ quy tắc này!
Sjoerd

23
@LokiAstari, " Tiêu chuẩn C ++ được định nghĩa theo tiêu chuẩn C. Về cơ bản, nó nói C ++ là C với những khác biệt và bổ sung này. " Vô nghĩa! C ++ chỉ tham chiếu tiêu chuẩn C trong [basic.funduel] và thư viện. Nếu những gì bạn nói là đúng, thì C ++ nói điều đó ở đâu _Bool_Imaginarykhông tồn tại trong C ++? Ngôn ngữ C ++ được định nghĩa rõ ràng, không phải theo nghĩa "chỉnh sửa" thành C, nếu không, tiêu chuẩn có thể ngắn hơn nhiều!
Jonathan Wakely

198

Các quy tắc để tránh xung đột tên là cả trong tiêu chuẩn C ++ (xem sách Stroustrup) và được đề cập bởi các bậc thầy về C ++ (Sutter, v.v.).

Quy tắc cá nhân

Bởi vì tôi không muốn giải quyết các trường hợp và muốn một quy tắc đơn giản, tôi đã thiết kế một quy tắc cá nhân vừa đơn giản vừa chính xác:

Khi đặt tên cho một ký hiệu, bạn sẽ tránh xung đột với các thư viện trình biên dịch / OS / tiêu chuẩn nếu bạn:

  • không bao giờ bắt đầu một biểu tượng với dấu gạch dưới
  • không bao giờ đặt tên cho một biểu tượng với hai dấu gạch dưới liên tiếp bên trong.

Tất nhiên, đặt mã của bạn vào một không gian tên duy nhất cũng giúp tránh va chạm (nhưng sẽ không bảo vệ chống lại các macro ác)

Vài ví dụ

(Tôi sử dụng macro vì chúng gây ô nhiễm mã hơn cho các ký hiệu C / C ++, nhưng nó có thể là bất cứ thứ gì từ tên biến đến tên lớp)

#define _WRONG
#define __WRONG_AGAIN
#define RIGHT_
#define WRONG__WRONG
#define RIGHT_RIGHT
#define RIGHT_x_RIGHT

Trích xuất từ ​​bản nháp C ++ 0x

Từ tệp n3242.pdf (Tôi hy vọng văn bản tiêu chuẩn cuối cùng sẽ tương tự):

17.6.3.3.2 Tên toàn cầu [global.names]

Một số bộ tên và chữ ký chức năng luôn được dành riêng cho việc thực hiện:

- Mỗi tên có dấu gạch dưới kép _ _ hoặc bắt đầu bằng dấu gạch dưới theo sau là chữ cái viết hoa (2.12) được dành riêng cho việc thực hiện cho bất kỳ việc sử dụng nào.

- Mỗi tên bắt đầu bằng dấu gạch dưới được dành riêng cho việc triển khai để sử dụng làm tên trong không gian tên toàn cầu.

Nhưng cũng:

17.6.3.3.5 Hậu tố bằng chữ do người dùng định nghĩa [usrlit.suffix]

Các định danh hậu tố bằng chữ không bắt đầu bằng dấu gạch dưới được dành riêng cho tiêu chuẩn hóa trong tương lai.

Mệnh đề cuối cùng này gây nhầm lẫn, trừ khi bạn cho rằng một tên bắt đầu bằng một dấu gạch dưới và theo sau là một chữ cái viết thường sẽ là Ok nếu không được định nghĩa trong không gian tên toàn cầu ...


9
@Meysam: __WRONG_AGAIN__chứa hai dấu gạch dưới liên tiếp (hai ở đầu và hai ở cuối), vì vậy điều này là sai theo tiêu chuẩn.
paercebal

8
@ BЈовић: WRONG__WRONGchứa hai dấu gạch dưới liên tiếp (hai ở giữa), vì vậy điều này là sai theo tiêu chuẩn
paercebal

2
Đặt mã của bạn vào một không gian tên duy nhất cũng giúp tránh xung đột : nhưng điều này vẫn chưa đủ, vì mã định danh có thể va chạm với một từ khóa bất kể phạm vi (ví dụ: __attribute__đối với GCC).
Ruslan

1
Tại sao có bất kỳ vấn đề về việc có hai dấu gạch dưới liên tiếp ở giữa theo tiêu chuẩn? Hậu tố bằng chữ do người dùng định nghĩa áp dụng cho các giá trị theo nghĩa đen như 1234567Lhoặc 4.0f; IIRC này đề cập đến ohttp: //en.cppreference.com/w/cpp/lingu/user_literal
Jason S

2
Why is there any problem of having two consecutive underscores in the middle according to the standard?Bởi vì các tiêu chuẩn nói rằng những người được bảo lưu. Đây không phải là một lời khuyên về phong cách tốt hay xấu. Đó là một quyết định từ tiêu chuẩn. Tại sao họ quyết định điều này? Tôi đoán các trình biên dịch đầu tiên đã sử dụng các quy ước như vậy một cách không chính thức trước khi tiêu chuẩn hóa.
paercebal

38

Từ MSDN :

Việc sử dụng hai ký tự gạch dưới liên tiếp (__) ở đầu một mã định danh hoặc một dấu gạch dưới hàng đầu duy nhất theo sau là chữ in hoa, được dành riêng cho việc triển khai C ++ trong tất cả các phạm vi. Bạn nên tránh sử dụng một dấu gạch dưới hàng đầu theo sau là một chữ cái viết thường cho các tên có phạm vi tệp vì có thể xung đột với các định danh dành riêng hiện tại hoặc tương lai.

Điều này có nghĩa là bạn có thể sử dụng một dấu gạch dưới đơn làm tiền tố biến thành viên, miễn là nó được theo sau bởi một chữ cái viết thường.

Điều này rõ ràng được lấy từ phần 17.4.3.1.2 của tiêu chuẩn C ++, nhưng tôi không thể tìm thấy một nguồn gốc cho toàn bộ tiêu chuẩn trực tuyến.

Xem thêm câu hỏi này .


2
Tôi đã tìm thấy một văn bản tương tự trong n3092.pdf (bản nháp của tiêu chuẩn C ++ 0x) tại phần: "17.6.3.3.2 Tên toàn cầu"
paercebal

7
Thật thú vị, đây dường như là câu trả lời duy nhất có câu trả lời trực tiếp, ngắn gọn cho câu hỏi.
hyde

9
@hyde: Trên thực tế, không phải vậy, vì nó bỏ qua quy tắc để không có bất kỳ định danh nào có dấu gạch dưới hàng đầu trong không gian tên toàn cầu. Xem câu trả lời của Rogers . Tôi sẽ rất cảnh giác với các trích dẫn của các tài liệu MS VC như là một thẩm quyền đối với tiêu chuẩn C ++.
sbi

@sbi Tôi đã đề cập đến "bạn có thể sử dụng một dấu gạch dưới duy nhất làm tiền tố biến thành viên, miễn là nó được viết bằng chữ in thường " trong câu trả lời này, trả lời trực tiếp câu hỏi trên văn bản câu hỏi mà không bị chết đuối trong một bức tường của văn bản.
hyde

5
Đầu tiên, tôi vẫn coi việc thiếu bất kỳ gợi ý nào cho thấy quy tắc tương tự không áp dụng cho không gian tên toàn cầu là một thất bại. Tuy nhiên, điều tồi tệ hơn là các dấu gạch dưới liền kề bị cấm không chỉ ở đầu, mà bất cứ nơi nào trong, một định danh. Vì vậy, câu trả lời này không chỉ đơn thuần là bỏ qua một thực tế, mà thực sự khiến ít nhất một tuyên bố chủ động sai. Như tôi đã nói, đề cập đến các tài liệu MSVC là điều tôi sẽ không làm trừ khi câu hỏi chỉ là về VC.
sbi

25

Đối với phần khác của câu hỏi, thông thường đặt dấu gạch dưới ở cuối tên biến để không xung đột với bất kỳ nội bộ nào.

Tôi làm điều này ngay cả trong các lớp và không gian tên bởi vì sau đó tôi chỉ phải nhớ một quy tắc (so với "ở cuối tên trong phạm vi toàn cầu và bắt đầu tên ở mọi nơi khác").


2

Có, dấu gạch dưới có thể được sử dụng ở bất cứ đâu trong một định danh. Tôi tin rằng các quy tắc là: bất kỳ az, AZ, _ trong ký tự đầu tiên và những + 0-9 cho các ký tự sau.

Tiền tố gạch dưới là phổ biến trong mã C - một dấu gạch dưới đơn có nghĩa là "riêng tư" và dấu gạch dưới kép thường được dành cho sử dụng bởi trình biên dịch.


3
Chúng là phổ biến trong các thư viện. Chúng không nên phổ biến trong mã người dùng.
Martin York

43
Người làm thư viện viết bằng C, bạn biết.
John Millikin

7
"Có, dấu gạch dưới có thể được sử dụng ở bất cứ đâu trong một định danh." Điều này là sai đối với định danh toàn cầu. Xem câu trả lời của Rogers .
sbi
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.