Quy ước đặt tên phổ biến nhất trong C là gì?


126

Các quy ước đặt tên thường được sử dụng trong C là gì? Tôi biết có ít nhất hai:

  1. GNU / linux / K&R với low_case_functions
  2. ? Tên ? với các chức năng UpperCaseFoo

Tôi đang nói về C chỉ ở đây. Hầu hết các dự án của chúng tôi là các hệ thống nhúng nhỏ trong đó chúng tôi sử dụng C.

Đây là cái mà tôi đang lên kế hoạch sử dụng cho dự án tiếp theo của mình:


C Quy ước đặt tên

Struct              TitleCase
Struct Members      lower_case or lowerCase

Enum                ETitleCase
Enum Members        ALL_CAPS or lowerCase

Public functions    pfx_TitleCase (pfx = two or three letter module prefix)
Private functions   TitleCase
Trivial variables   i,x,n,f etc...
Local variables     lower_case or lowerCase
Global variables    g_lowerCase or g_lower_case (searchable by g_ prefix)

7
Tôi sẽ không buộc tiền tố 'g_' trên các biến toàn cục; Tôi sẽ thực thi các tên có ý nghĩa (vì vậy client_locale chứ không phải cl_lc làm tên biến toàn cục). Classic C không sử dụng vỏ camel; Tôi đã viết mã trong trường hợp lạc đà trong C, và nó trông kỳ lạ (vì vậy tôi không làm như vậy nữa). Điều đó nói rằng, nó không sai - và tính nhất quán quan trọng hơn quy ước nào được sử dụng. Tránh các typedef đóng gói các con trỏ cấu trúc; hãy xem xét tiêu chuẩn C - 'FILE *' được viết như vậy, không phải FILE_PTR.
Jonathan Leffler

2
@Jonathan Leffler, có chuyện gì với g_ để biểu thị hình cầu? Trong các hệ thống nhúng, tôi đã gặp sự cố trước đây, trong đó khó theo dõi sự phụ thuộc giữa các mô-đun thông qua các vars toàn cục và extern g_somevar. Cá nhân tôi nghĩ rằng đó thường là một ý tưởng tồi, nhưng điều này thường được thực hiện vì lý do hiệu suất. Ví dụ, một cờ toàn cục được thiết lập bởi một ngắt cho biết rằng dữ liệu đã sẵn sàng.
JeffV

2
Đối với những gì nó đáng giá, quy ước đặt tên này chủ yếu được tách ra từ quy ước API PalmOS. Ngoài ra, nó tương tự như quy ước được sử dụng trong cuốn sách của O'Reilly: "Lập trình hệ thống nhúng với C và các công cụ phát triển GNU". Cá nhân tôi thích TitleCase trong tên hàm. Tôi đã nghĩ đến việc sử dụng LowerCamelCase trong các chức năng liên kết nội bộ (mà tôi gọi là private trong câu hỏi của mình).
JeffV

3
@Chris Lutz, tôi hoàn toàn đồng ý. Bất cứ nơi nào có thể các vars phải được giữ ở phạm vi hẹp nhất. Lưu ý rằng thực tế có ba phạm vi mà chúng ta đang thảo luận: cục bộ với một hàm, cục bộ cho một mô-đun (không có liên kết loại trừ với biến) và toàn cầu có liên kết bên ngoài. Thông thường có các biến "toàn cục đến một mô-đun" trong các hệ thống nhúng. Do đó, phải cẩn thận để xác định các khối cầu có liên kết bên ngoài để chúng có thể được giữ ở mức tối thiểu và các tương tác mô-đun được hiểu. Đây là nơi mà tiền tố "g_" rất hữu ích.
JeffV

46
Tôi thích đặt tiền tố cho các biến toàn cục bằng //.
plafer

Câu trả lời:


128

Điều quan trọng nhất ở đây là tính nhất quán. Điều đó nói rằng, tôi tuân theo quy ước mã hóa GTK +, có thể được tóm tắt như sau:

  1. Tất cả các macro và hằng số ở dạng chữ hoa: MAX_BUFFER_SIZE, TRACKING_ID_PREFIX.
  2. Cấu trúc tên và typedef trong camelcase: GtkWidget, TrackingOrder.
  3. Các hàm hoạt động trên struct: classic C style: gtk_widget_show(), tracking_order_process().
  4. Con trỏ: không có gì lạ ở đây: GtkWidget *foo, TrackingOrder *bar.
  5. Biến toàn cục: chỉ cần không sử dụng biến toàn cục. Họ thật ác độc.
  6. Các hàm có ở đó, nhưng không nên được gọi trực tiếp hoặc có cách sử dụng tối nghĩa, hoặc bất cứ điều gì: một hoặc nhiều dấu gạch dưới ở đầu: _refrobnicate_data_tables(), _destroy_cache().

13
Trong điểm thứ sáu, tôi thích sử dụng staticvà bỏ qua tiền tố mô-đun, vì vậy nếu gtk_widget_show()là một hàm có phạm vi tệp, nó sẽ trở nên đơn giản widget_show()với lớp lưu trữ tĩnh được thêm vào.
Tháng 8 Karlstrom

27
Lưu ý bổ sung về điểm 6: tiêu chuẩn C có một số quy tắc về việc đặt trước các tên bắt đầu _để triển khai và sử dụng trong tương lai. Có một vài trường hợp ngoại lệ đối với những cái tên bắt đầu bằng chữ cái _nhưng theo ý kiến ​​của tôi, nó không đáng để bạn phải gặp rắc rối khi ghi nhớ. Một quy tắc an toàn để thực hiện là không bao giờ sử dụng tên bắt đầu bằng _mã của bạn. Mục câu hỏi thường gặp về C có liên quan: c-faq.com/~scs/cgi-bin/faqcat.cgi?sec=decl#namespace
jw013

5
# 2 cụ thể hơn là trường hợp lạc đà trên hoặc trường hợp pascal . Camel case hoặc camel case thấp hơn sử dụng chữ thường trên chữ cái đầu tiên.
Clint Pachl

9
Điều gì về các biến địa phương nhiều từ? my_var hoặc myVar?
Dean Gurvitz

4
Global variables: just don't use global variables. They are evil.- trừ khi bạn đang làm việc trên dự án nhúng và bạn có 1024 byte RAM và CPU 8MHz.
Kamil

30

"Con trỏ cấu trúc" không phải là các thực thể cần điều khoản quy ước đặt tên để bao hàm chúng. Họ chỉ là struct WhatEver *. ĐỪNG che giấu sự thật rằng có một con trỏ liên quan đến một typedef thông minh và "rõ ràng". Nó không phục vụ mục đích nào, còn để nhập và phá hủy sự cân bằng giữa khai báo và truy cập.


29
+1 cho nội dung "không ẩn con trỏ" - mặc dù câu trả lời này chưa giải quyết được nhiều phần còn lại của câu hỏi (chưa).
Jonathan Leffler

1
@unwind, tôi có xu hướng đồng ý. Tuy nhiên, đôi khi một con trỏ không nhằm mục đích tham chiếu bên ngoài và nó có vai trò quan trọng hơn đối với người tiêu dùng hơn là một con trỏ thực tế tới một cấu trúc mà họ sẽ sử dụng. Đó là những gì tôi để lại TitleCasePtr cho. typedef struct {fields} MyStruct, * MyStructPtr;
JeffV

Tôi đang xóa TitleCasePtr, nó làm mất tập trung khỏi câu hỏi thực tế.
JeffV

1
-1 từ tôi vì khai báo kiểu con trỏ làm giảm sự lộn xộn, đặc biệt là trong chữ ký hàm và sự "mất cân bằng" giữa khai báo và truy cập chỉ hiển thị trong tệp thực thi - máy khách không (không nên) truy cập trực tiếp vào các thành viên trường.
Tháng 8 Karlstrom

1
@AugustKarlstrom Tốt thôi. Tuy nhiên, tôi không hiểu cái gì nên "chỉ" về tệp triển khai, đó không phải là mã? Tôi không giải thích câu hỏi chỉ là về tên "bên ngoài". Tất cả mã là "thực hiện" ở một số cấp độ.
thư giãn

17

Trước hết C không có chức năng công khai / riêng tư / ảo. Đó là C ++ và nó có các quy ước khác nhau. Trong C thường bạn có:

  • Hằng số trong ALL_CAPS
  • Dấu gạch dưới để phân cách các từ trong cấu trúc hoặc tên hàm, hiếm khi bạn thấy trường hợp lạc đà trong C;
  • struct, typedefs, union, member (of union và struct) và giá trị enum thường ở dạng chữ thường (theo kinh nghiệm của tôi) chứ không phải theo quy ước C ++ / Java / C # / etc về việc viết hoa chữ cái đầu tiên nhưng tôi đoán nó có thể trong C cũng vậy.

C ++ phức tạp hơn. Tôi đã thấy một sự kết hợp thực sự ở đây. Trường hợp lạc đà cho tên lớp hoặc chữ thường + dấu gạch dưới (trường hợp lạc đà phổ biến hơn theo kinh nghiệm của tôi). Các cấu trúc hiếm khi được sử dụng (và thường là do thư viện yêu cầu chúng, nếu không bạn sẽ sử dụng các lớp).


@cletus, tôi nhận ra điều đó. Riêng tôi có nghĩa là các chức năng không được hiển thị bên ngoài trong tiêu đề mô-đun và không nhằm mục đích sử dụng mã bên ngoài mô-đun. Công khai sẽ là các hàm API của mô-đun nhằm mục đích sử dụng bên ngoài.
JeffV

3
Bạn có thể coi các hàm tĩnh là riêng tư; câu hỏi không đề cập đến ảo. Nhưng +1 cho 'hiếm khi thấy vỏ lạc đà trong C'.
Jonathan Leffler

2
Tôi nghĩ Jeff muốn nói external linkageđến "chức năng công cộng" và internal linkage"chức năng riêng tư".
pmg

1
Tôi đã thấy các hằng số bắt đầu bằng ak cũng như trong: kBufferSize. Không chắc điều đó đến từ đâu.
JeffV

2
ALL_CAPScũng thường được sử dụng cho các giá trị enum.
caf,

14

Bạn biết đấy, tôi muốn giữ cho nó đơn giản, nhưng rõ ràng ... Vì vậy, đây là những gì tôi sử dụng, trong C:

  • Tầm thường biến : i,n,c(. Chỉ có một lá thư Nếu một bức thư không rõ ràng, sau đó làm cho nó một biến địa phương), vv ...
  • Biến cục bộ :lowerCamelCase
  • Biến toàn cục :g_lowerCamelCase
  • Biến Const :ALL_CAPS
  • Biến con trỏ : thêm a p_vào tiền tố. Đối với biến toàn cục gp_var, đối với biến cục bộ p_var, đối với biến const p_VAR. Nếu con trỏ xa được sử dụng thì hãy sử dụng fp_thay thế cho p_.
  • Cấu trúc : ModuleCamelCase(Mô-đun = tên mô-đun đầy đủ hoặc viết tắt 2-3 chữ cái, nhưng vẫn ở trong CamelCase.)
  • Cấu trúc các biến thành viên :lowerCamelCase
  • Enums :ModuleCamelCase
  • Giá trị Enum :ALL_CAPS
  • Chức năng công cộng :ModuleCamelCase
  • Chức năng Riêng tư :CamelCase
  • Macro :CamelCase

Tôi đã gõ cấu trúc của mình, nhưng sử dụng cùng một tên cho cả thẻ và typedef. Thẻ không được sử dụng phổ biến. Thay vào đó, bạn nên sử dụng typedef. Tôi cũng khai báo typedef trong tiêu đề mô-đun công cộng để đóng gói và để tôi có thể sử dụng tên typedef'd trong định nghĩa.

struct Ví dụ đầy đủ :

typdef struct TheName TheName;
struct TheName{
    int var;
    TheName *p_link;
};

Tôi không biết gì về khung công tác qt, nhưng bạn có thể viết mã của mình ở bất kỳ định dạng kiểu nào bạn muốn. Theo như tôi biết thì không có gì ngăn cản bạn với nó.
SeanRamey

10

Đồng thời viết mã bằng C #, java, C, C ++ và mục tiêu C , tôi đã áp dụng một quy ước đặt tên rất đơn giản và rõ ràng để đơn giản hóa cuộc sống của mình.

Trước hết, nó dựa vào sức mạnh của các IDE hiện đại (chẳng hạn như eclipse, Xcode ...), với khả năng lấy thông tin nhanh bằng cách di chuột hoặc nhấp ctrl ... Chấp nhận điều đó, tôi đã loại bỏ việc sử dụng bất kỳ tiền tố, hậu tố nào. và các điểm đánh dấu khác do IDE cung cấp.

Sau đó, quy ước:

  • Bất kỳ tên nào PHẢI là một câu có thể đọc được giải thích những gì bạn có. Giống như "đây là quy ước của tôi".
  • Sau đó, 4 phương pháp để lấy quy ước ra khỏi câu:
    1. NÀY_IS_MY_CONVENTION cho macro, thành viên enum
    2. ThisIsMyConvention cho tên tệp, tên đối tượng (lớp, struct, enum, union ...), tên hàm, tên phương thức, typedef
    3. this_is_my_convention các biến,
      tham số, cấu trúc và liên hợp toàn cục và cục bộ
    4. thisismyconvention [tùy chọn] các biến rất cục bộ và tạm thời (chẳng hạn như chỉ mục vòng lặp for ())

Và đó là nó.

Nó cho

class MyClass {
    enum TheEnumeration {
        FIRST_ELEMENT,
        SECOND_ELEMENT,
    }

    int class_variable;

    int MyMethod(int first_param, int second_parameter) {
        int local_variable;
        TheEnumeration local_enum;
        for(int myindex=0, myindex<class_variable, myindex++) {
             localEnum = FIRST_ELEMENT;
        }
    }
}

8

Tôi khuyên bạn không nên trộn trường hợp lạc đà và phân tách gạch dưới (giống như bạn đã đề xuất cho các thành viên cấu trúc). Điều này thật khó hiểu. Bạn sẽ nghĩ, này tôi có get_lengthnên tôi có lẽ nên có make_subsetvà sau đó bạn phát hiện ra nó thực sự makeSubset. Sử dụng nguyên tắc ít gây ngạc nhiên nhất và nhất quán.

Tôi thấy CamelCase hữu ích để nhập tên, như structs, typedefs và enums. Đó là về tất cả, mặc dù. Đối với tất cả các phần còn lại (tên hàm, tên thành viên cấu trúc, v.v.) tôi sử dụng underscore_separation.


1
Vâng, điều chính của bất kỳ quy ước đặt tên nào là khả năng dự đoán và tính nhất quán. Ngoài ra, vì bản thân thư viện C sử dụng tất cả các chữ thường với _ cho khoảng cách, tôi khuyên bạn nên sử dụng điều đó để bạn không phải đối phó với 2 quy ước đặt tên khác nhau trong một dự án (giả sử bạn không viết một trình bao bọc xung quanh libc để thực hiện nó phù hợp với cách đặt tên của bạn .. nhưng điều đó thật thô thiển)
Earlz

Nó cũng sử dụng typedefs với chữ " t" ở cuối, nhưng tôi không thấy ai đề xuất điều đó. Trên thực tế, thư viện chuẩn thậm chí còn không nhất quán: div_t (stdlib.h) là một cấu trúc và tm (time.h) cũng vậy. Ngoài ra, hãy nhìn vào các thành viên cấu trúc tm, tất cả chúng đều có tiền tố là tm có vẻ vô nghĩa và xấu xí (IMO).
JeffV

1
"Tôi thấy CamelCase hữu ích để gõ tên ..." Nếu bạn bắt đầu viết hoa, thì đó thực sự là PascalCase.
Tagc

7

Đây là một (dường như) không phổ biến, mà tôi thấy hữu ích: tên mô-đun trong CamelCase, sau đó là dấu gạch dưới, sau đó là hàm hoặc tên phạm vi tệp trong CamelCase. Ví dụ:

Bluetooth_Init()
CommsHub_Update()
Serial_TxBuffer[]

2
Không quá bất thường, nhưng rất hữu ích.
chux - Phục hồi Monica

3

Tôi bối rối bởi một điều: Bạn đang định tạo một quy ước đặt tên mới cho một dự án mới. Nói chung, bạn nên có một quy ước đặt tên cho toàn công ty hoặc toàn nhóm. Nếu bạn đã có các dự án có bất kỳ hình thức đặt tên nào, bạn không nên thay đổi quy ước cho một dự án mới. Nếu quy ước trên chỉ là mã hóa các thực hành hiện có của bạn, thì bạn là vàng. Nó càng khác với các tiêu chuẩn thực tế hiện có thì càng khó đạt được sự chia sẻ tư duy trong tiêu chuẩn mới.

Về đề xuất duy nhất mà tôi muốn thêm là tôi đã thích _t ở cuối các loại theo kiểu uint32_t và size_t. Nó rất C-ish đối với tôi mặc dù một số người có thể phàn nàn rằng nó chỉ là tiếng Hungary "ngược".


3
Chà, các quy ước ở đây ở khắp nơi và không nhất quán, đó là lý do tại sao tôi bắt đầu ghi lại một tài liệu. Ngoài ra, đó là lý do tại sao tôi hỏi. Để xem sự đồng thuận của cộng đồng là gì.
JeffV

Tôi hiểu nỗi đau đó. Nhưng phải có một số tập hợp con các quy ước hiện có của bạn là phổ biến nhất. Bạn nên bắt đầu ở đó chứ không phải trên một trang web Internet ngẫu nhiên. Ngoài ra, bạn nên hỏi các nhà phát triển khác của mình xem họ sẽ cho là gì.
jmucchiello

7
Tôi tin rằng các tên loại kết thúc bằng _t được dành riêng bởi tiêu chuẩn POSIX.
caf,

4
Tên hoàn thiện với _t được đặt trước. Xem gnu.org/software/libc/manual/html_node/Reserved-Names.html , "Các tên kết thúc bằng '_t' được dành riêng cho các tên loại bổ sung."
Étienne

2

Bạn cũng nên suy nghĩ về thứ tự của các từ để hoàn thành tên tự động dễ dàng hơn.

Một phương pháp hay: tên thư viện + tên mô-đun + hành động + chủ đề

Nếu một phần không liên quan chỉ cần bỏ qua nó, nhưng ít nhất một tên mô-đun và một hành động luôn phải được trình bày.

Ví dụ:

  • tên hàm: os_task_set_prio, list_get_size,avg_get
  • xác định (ở đây thường không có phần hành động ):OS_TASK_PRIO_MAX

0

Có thể có nhiều, chủ yếu là các IDE quyết định một số xu hướng và các quy ước C ++ cũng đang thúc đẩy. Đối với C thường:

  • UNDERSCORED_UPPER_CASE (định nghĩa macro, hằng số, thành viên enum)
  • underscored_lower_case (biến, hàm)
  • CamelCase (các loại tùy chỉnh: structs, enums, union)
  • uncappedCamelCase (kiểu Java của oppa)
  • UnderScored_CamelCase (biến, hàm dưới loại không gian tên)

Ký hiệu Hungary cho hình cầu là tốt nhưng không phù hợp với các loại. Và ngay cả đối với những cái tên tầm thường, hãy sử dụng ít nhất hai ký tự.


-1

Tôi nghĩ những điều đó có thể giúp ích cho người mới bắt đầu: Quy ước đặt tên của các biến trong c

  1. Bạn phải sử dụng Ký tự chữ cái (az, AZ), Chữ số (0-9) và Dưới điểm (_). Nó không cho phép sử dụng bất kỳ Ký tự đặc biệt nào như:%, $, #, @, v.v. Vì vậy, bạn có thể sử dụng user_name làm biến nhưng không thể sử dụng user & name .
  2. Không thể sử dụng khoảng trắng giữa các từ. Vì vậy, bạn có thể sử dụng user_name hoặc tên người dùng hoặc tên người dùng làm biến nhưng không thể sử dụng tên người dùng .
  3. Không thể bắt đầu đặt tên bằng chữ số. Vì vậy, bạn có thể sử dụng user1 hoặc user2 làm biến nhưng không thể sử dụng 1user .
  4. Đây là ngôn ngữ phân biệt chữ hoa chữ thường. Chữ hoa và chữ thường có ý nghĩa. Nếu bạn sử dụng một biến như tên người dùng thì bạn không thể sử dụng USERNAME hoặc Tên người dùng để sử dụng cho cha.
  5. Bạn không thể sử dụng bất kỳ từ khóa nào (char, int, if, for, while, v.v.) để khai báo biến.
  6. Tiêu chuẩn ANSI công nhận độ dài 31 ký tự cho một tên biến

Bài đăng này rõ ràng nhằm mục đích thảo luận về các quy ước đặt tên , không phải các hạn chế . Những gì bạn liệt kê là những thứ bạn thậm chí không thể làm vì đó là những lỗi cú pháp.
Đuổi theo
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.