เอกสารนี้อธิบายรูปแบบรหัส C ที่ Tilen Majerle ใช้ในโครงการและห้องสมุดของเขา
เริ่มต้นด้วยคำพูดจากเว็บไซต์นักพัฒนา GNOME
กฎที่สำคัญที่สุดเดียวเมื่อเขียนโค้ดคือ: ตรวจสอบรหัสโดยรอบและพยายามเลียนแบบ
ในฐานะที่เป็นผู้ดูแลมันน่าตกใจที่ได้รับแพตช์ที่เห็นได้ชัดในรูปแบบการเข้ารหัสที่แตกต่างกันไปยังรหัสโดยรอบ นี่คือการดูหมิ่นเช่นเดียวกับใครบางคนที่เดินเข้าไปในบ้านที่สะอาดตาด้วยรองเท้าโคลน
ดังนั้นไม่ว่าเอกสารนี้จะแนะนำอะไรถ้ามีรหัสที่เขียนอยู่แล้วและคุณกำลังทำการแก้ไขให้รักษาสไตล์ปัจจุบันให้สอดคล้องกันแม้ว่าจะไม่ใช่สไตล์ที่คุณชื่นชอบก็ตาม
VSCODE มาพร้อมกับเครื่องมือ clang-format
ที่ติดตั้งไว้ล่วงหน้า (ส่วนหนึ่งของแพ็คเกจ LLVM) ที่ได้รับการออกแบบเพื่อช่วยให้นักพัฒนาซอฟต์แวร์ด้วยเครื่องมือรูปแบบอัตโนมัติในระหว่างการพัฒนารหัส
ดังนั้นจึงอนุญาตให้ผู้ใช้จัดรูปแบบรหัสในการเปลี่ยนแปลงไฟล์ (และบันทึก) เมื่อบันทึกไฟล์ VSCODE จะพยายามเรียกใช้รูปแบบ clang และจัดรูปแบบรหัส กฎที่จะใช้อยู่ในไฟล์. .clang-format
หากรูปแบบเสียงดังก้องไม่พบกฎในเส้นทางของไฟล์ปัจจุบันมันจะไปจนถึงรูทจนกระทั่งพบหนึ่ง หากยังไม่มีให้ใช้งานกฎเริ่มต้นจะถูกใช้
ที่เก็บนี้มีไฟล์ .clang-format
ที่ทันสมัยอยู่เสมอพร้อมกฎที่จับคู่กัน คุณสามารถวางโฟลเดอร์ในรูทหรือโครงการของคุณหรือแม้แต่ในรูทของโครงการพัฒนาซอฟต์แวร์ของคุณ -> ใช้ไฟล์เดียวสำหรับทุกคน!
การกำหนดค่าบางอย่างจะต้องเปิดใช้งาน:
คำ หลัก จะต้อง ไม่จำเป็น ต้อง ไม่ควร แนะนำ ไม่ ควร แนะนำ ไม่แนะนำ อาจ และ เป็นทางเลือก ในเอกสารนี้จะตีความตามที่อธิบายไว้ใน BCP 14 [RFC2119] [RFC8174]
นี่คือกฎทั่วไปที่ชัดเจนที่สุดและสำคัญที่สุด โปรดตรวจสอบพวกเขาอย่างระมัดระวังก่อนที่คุณจะดำเนินการต่อกับบทอื่น ๆ
clang-format
กับไฟล์การจัดรูปแบบที่แนบมากับที่เก็บนี้ (เวอร์ชัน 15.x
เป็นขั้นต่ำ)C11
4
ช่องว่างต่อระดับเยื้อง1
ช่องว่างระหว่างคำหลักและวงเล็บเปิด /* OK */
if ( condition )
while ( condition )
for ( init ; condition ; step )
do {} while ( condition )
/* Wrong */
if ( condition )
while ( condition )
for ( init ; condition ; step )
do {} while ( condition )
int32_t a = sum ( 4 , 3 ); /* OK */
int32_t a = sum ( 4 , 3 ); /* Wrong */
__
หรือ _
คำนำหน้าสำหรับตัวแปร/ฟังก์ชั่น/มาโคร/ประเภท สิ่งนี้สงวนไว้สำหรับภาษา C เองprv_
ชื่อสำหรับฟังก์ชันโมดูลโมดูล (คงที่) อย่างเคร่งครัดlibname_int_
หรือ libnamei_
สำหรับฟังก์ชั่นภายในไลบรารีที่ไม่ควรใช้โดยแอปพลิเคชันผู้ใช้ในขณะที่พวกเขาจะต้องใช้กับโมดูลภายในไลบรารีที่แตกต่างกัน_
charfor
while
do
, switch
, if
, ... ) size_t i ;
for ( i = 0 ; i < 5 ; ++ i ) { /* OK */
}
for ( i = 0 ; i < 5 ; ++ i ){ /* Wrong */
}
for ( i = 0 ; i < 5 ; ++ i ) /* Wrong */
{
}
int32_t a ;
a = 3 + 4 ; /* OK */
for ( a = 0 ; a < 5 ; ++ a ) /* OK */
a = 3 + 4 ; /* Wrong */
a = 3 + 4 ; /* Wrong */
for ( a = 0 ; a < 5 ; ++ a ) /* Wrong */
func_name ( 5 , 4 ); /* OK */
func_name ( 4 , 3 ); /* Wrong */
global
เป็นค่าเริ่มต้นใด ๆ (หรือ NULL
) นำไปใช้ในฟังก์ชัน init
เฉพาะ (ถ้าจำเป็น) static int32_t a ; /* Wrong */
static int32_t b = 4 ; /* Wrong */
static int32_t a = 0 ; /* Wrong */
ในระบบฝังตัวเป็นเรื่องธรรมดามากที่ความทรงจำของ RAM จะกระจัดกระจายไปตามตำแหน่งหน่วยความจำที่แตกต่างกันในระบบ มันกลายเป็นเรื่องยากที่จะจัดการทุกกรณีโดยเฉพาะอย่างยิ่งเมื่อผู้ใช้ประกาศส่วน RAM ที่กำหนดเอง สคริปต์เริ่มต้นมีค่าใช้จ่ายในการตั้งค่าเริ่มต้น (.DATA และ .BSS) ในขณะที่ส่วนที่กำหนดเองอื่น ๆ อาจไม่เต็มด้วยค่าเริ่มต้นซึ่งนำไปสู่ตัวแปรที่มีค่าเริ่มต้นจะไม่มีผลกระทบใด ๆ
หากต้องการเป็นอิสระจากปัญหาดังกล่าวให้สร้างฟังก์ชั่น init สำหรับแต่ละโมดูลและใช้เพื่อตั้งค่าเริ่มต้นสำหรับตัวแปรทั้งหมดของคุณเช่น:
static int32_t a ; /* OK */
static int32_t b = 4 ; /* Wrong - this value may not be set at zero
if linker script&startup files are not properly handled */
void
my_module_init ( void ) {
a = 0 ;
b = 4 ;
}
void
my_func ( void ) {
/* 1 */
char a ; /* OK */
/* 2 */
char a , b ; /* OK */
/* 3 */
char a ;
char b ; /* Wrong, variable with char type already exists */
}
int
my_func ( void ) {
/* 1 */
my_struct_t my ; /* First custom structures */
my_struct_ptr_t * p ; /* Pointers too */
/* 2 */
uint32_t a ;
int32_t b ;
uint16_t c ;
int16_t g ;
char h ;
/* ... */
/* 3 */
double d ;
float f ;
}
typedef struct {
int a , b ;
} str_t ;
str_t s = {
. a = 1 ,
. b = 2 , /* Comma here */
}
/* Examples of "complex" structure, with or with missing several trailing commas, after clang-format runs the formatting */
static const my_struct_t my_var_1 = {
. type = TYPE1 ,
. type_data =
{
. type1 =
{
. par1 = 0 ,
. par2 = 1 , /* Trailing comma here */
}, /* Trailing comma here */
}, /* Trailing comma here */
};
static const my_struct_t my_var_2 = {. type = TYPE2 ,
. type_data = {
. type2 =
{
. par1 = 0 ,
. par2 = 1 ,
},
}}; /* Missing comma here */
static const my_struct_t my_var_3 = {. type = TYPE3 ,
. type_data = {. type3 = {
. par1 = 0 ,
. par2 = 1 ,
}}}; /* Missing 2 commas here */
/* No trailing commas - good only for small and simple structures */
static const my_struct_t my_var_4 = {. type = TYPE4 , . type_data = {. type4 = {. par1 = 0 , . par2 = 1 }}};
for
ลูป /* OK */
for ( size_t i = 0 ; i < 10 ; ++ i )
/* OK, if you need counter variable later */
size_t i ;
for ( i = 0 ; i < 10 ; ++ i ) {
if (...) {
break ;
}
}
if ( i == 10 ) {
}
/* Wrong */
size_t i ;
for ( i = 0 ; i < 10 ; ++ i ) ...
void
a ( void ) {
/* Avoid function calls when declaring variable */
int32_t a , b = sum ( 1 , 2 );
/* Use this */
int32_t a , b ;
b = sum ( 1 , 2 );
/* This is ok */
uint8_t a = 3 , b = 4 ;
}
char
, float
หรือ double
มักใช้ประเภทที่ประกาศในไลบรารี stdint.h
เช่น uint8_t
สำหรับ unsigned 8-bit
ฯลฯstdbool.h
ใช้ 1
หรือ 0
สำหรับ true
หรือ false
ตามลำดับ /* OK */
uint8_t status ;
status = 0 ;
/* Wrong */
#include <stdbool.h>
bool status = true;
true
เช่น if (check_func() == 1)
ใช้ if (check_func()) { ... }
NULL
เสมอ void * ptr ;
/* ... */
/* OK, compare against NULL */
if ( ptr == NULL || ptr != NULL ) {
}
/* Wrong */
if ( ptr || ! ptr ) {
}
int32_t a = 0 ;
...
a ++ ; /* Wrong */
++ a ; /* OK */
for ( size_t j = 0 ; j < 10 ; ++ j ) {} /* OK */
size_t
สำหรับตัวแปรความยาวหรือขนาดเสมอconst
สำหรับตัวชี้เสมอหากฟังก์ชั่นไม่ควรแก้ไขหน่วยความจำที่ชี้ไปที่ pointer
const
สำหรับพารามิเตอร์ฟังก์ชันหรือตัวแปรเสมอหากไม่ควรแก้ไข /* When d could be modified, data pointed to by d could not be modified */
void
my_func ( const void * d ) {
}
/* When d and data pointed to by d both could not be modified */
void
my_func ( const void * const d ) {
}
/* Not REQUIRED, it is advised */
void
my_func ( const size_t len ) {
}
/* When d should not be modified inside function, only data pointed to by d could be modified */
void
my_func ( void * const d ) {
}
void *
เสมออย่าใช้ uint8_t *
/*
* To send data, function should not modify memory pointed to by `data` variable
* thus `const` keyword is important
*
* To send generic data (or to write them to file)
* any type may be passed for data,
* thus use `void *`
*/
/* OK example */
void
send_data ( const void * data , size_t len ) { /* OK */
/* Do not cast `void *` or `const void *` */
const uint8_t * d = data ; /* Function handles proper type for internal usage */
}
void
send_data ( const void * data , int len ) { /* Wrong, not not use int */
}
sizeof
malloc
มาตรฐานและฟังก์ชั่น free
หรือหากไลบรารี/โครงการมีการจัดสรรหน่วยความจำที่กำหนดเองให้ใช้การใช้งาน /* OK */
#include <stdlib.h>
void
my_func ( size_t size ) {
int32_t * arr ;
arr = malloc ( sizeof ( * arr ) * n ); /* OK, Allocate memory */
arr = malloc ( sizeof * arr * n ); /* Wrong, brackets for sizeof operator are missing */
if ( arr == NULL ) {
/* FAIL, no memory */
}
free ( arr ); /* Free memory after usage */
}
/* Wrong */
void
my_func ( size_t size ) {
int32_t arr [ size ]; /* Wrong, do not use VLA */
}
boolean
boolean-treated
กับศูนย์หรือหนึ่งตัว ใช้ไม่ ( !
) แทน size_t length = 5 ; /* Counter variable */
uint8_t is_ok = 0 ; /* Boolean-treated variable */
if ( length ) /* Wrong, length is not treated as boolean */
if ( length > 0 ) /* OK, length is treated as counter variable containing multi values, not only 0 or 1 */
if ( length == 0 ) /* OK, length is treated as counter variable containing multi values, not only 0 or 1 */
if ( is_ok ) /* OK, variable is treated as boolean */
if (! is_ok ) /* OK, -||- */
if ( is_ok == 1 ) /* Wrong, never compare boolean variable against 1! */
if ( is_ok == 0 ) /* Wrong, use ! for negative check */
/* comment */
สำหรับความคิดเห็นเสมอแม้กระทั่งสำหรับความคิดเห็น บรรทัดเดียวC++
ด้วยคำหลัก extern
ในไฟล์ส่วนหัวstatic
force_redraw
อย่าใช้ forceRedraw
void *
เช่น uint8_t* ptr = (uint8_t *)func_returning_void_ptr();
เป็น void *
ได้รับการเลื่อนตำแหน่งอย่างปลอดภัยให้เป็นตัวชี้ประเภทอื่น ๆuint8_t* ptr = func_returning_void_ptr();
แทน<
และ >
สำหรับไลบรารีมาตรฐาน C รวมไฟล์เช่น #include <stdlib.h>
""
สำหรับไลบรารีที่กำหนดเองเสมอเช่น #include "my_library.h"
uint8_t* t = (uint8_t*)var_width_diff_type
//
ไม่ได้รับอนุญาต ใช้ /* comment */
แม้กระทั่งสำหรับความคิดเห็นบรรทัดเดียว //This is comment (wrong)
/* This is comment (ok) */
space+asterisk
สำหรับทุกบรรทัด /*
* This is multi-line comments,
* written in 2 lines (ok)
*/
/**
* Wrong, use double-asterisk only for doxygen documentation
*/
/*
* Single line comment without space before asterisk (wrong)
*/
/*
* Single line comment in multi-line configuration (wrong)
*/
/* Single line comment (ok) */
12
เยื้อง ( 12 * 4
ช่องว่าง) ชดเชยเมื่อแสดงความคิดเห็น หากคำสั่งมีขนาดใหญ่กว่า 12
เยื่อ 4-spaces
void
my_func ( void ) {
char a , b ;
a = call_func_returning_char_a ( a ); /* This is comment with 12*4 spaces indent from beginning of line */
b = call_func_returning_char_a_but_func_name_is_very_long ( a ); /* This is comment, aligned to 4-spaces indent */
}
_
อักขระ /* OK */
void my_func ( void );
void myfunc ( void );
/* Wrong */
void MYFunc ( void );
void myFunc ();
/* OK */
const char * my_func ( void );
my_struct_t * my_func ( int32_t a , int32_t b );
/* Wrong */
const char * my_func ( void );
my_struct_t * my_func ( void );
/* OK, function names aligned */
void set ( int32_t a );
my_type_t get ( void );
my_ptr_t * get_ptr ( void );
/* Wrong */
void set ( int32_t a );
const char * get ( void );
/* OK */
int32_t
foo ( void ) {
return 0 ;
}
/* OK */
static const char *
get_string ( void ) {
return "Hello world!rn" ;
}
/* Wrong */
int32_t foo ( void ) {
return 0 ;
}
_
อักขระ /* OK */
int32_t a ;
int32_t my_var ;
int32_t myvar ;
/* Wrong */
int32_t A ;
int32_t myVar ;
int32_t MYVar ;
type
void
foo ( void ) {
int32_t a , b ; /* OK */
char a ;
char b ; /* Wrong, char type already exists */
}
void
foo ( void ) {
int32_t a ;
a = bar ();
int32_t b ; /* Wrong, there is already executable statement */
}
int32_t a , b ;
a = foo ();
if ( a ) {
int32_t c , d ; /* OK, c and d are in if-statement scope */
c = foo ();
int32_t e ; /* Wrong, there was already executable statement inside block */
}
/* OK */
char * a ;
/* Wrong */
char * a ;
char * a ;
/* OK */
char * p , * n ;
_
อักขระระหว่างคำtypedef
เมื่อมีการประกาศโครงสร้างมันอาจใช้หนึ่งใน 3
ตัวเลือกที่แตกต่างกัน:
_t
หลังจากชื่อ struct struct_name {
char * a ;
char b ;
};
_t
หลังจากชื่อ typedef struct {
char * a ;
char b ;
} struct_name_t ;
_t
สำหรับชื่อพื้นฐานและ จะต้อง มีคำต่อท้าย _t
หลังจากชื่อสำหรับส่วน typedef typedef struct struct_name { /* No _t */
char * a ;
char b ;
char c ;
} struct_name_t ; /* _t */
ตัวอย่างของการประกาศที่ไม่ดีและการแก้ไขที่แนะนำของพวกเขา
/* a and b MUST be separated to 2 lines */
/* Name of structure with typedef MUST include _t suffix */
typedef struct {
int32_t a , b ;
} a ;
/* Corrected version */
typedef struct {
int32_t a ;
int32_t b ;
} a_t ;
/* Wrong name, it MUST not include _t suffix */
struct name_t {
int32_t a ;
int32_t b ;
};
/* Wrong parameters, MUST be all uppercase */
typedef enum {
MY_ENUM_TESTA ,
my_enum_testb ,
} my_enum_t ;
C99
/* OK */
a_t a = {
. a = 4 ,
. b = 5 ,
};
/* Wrong */
a_t a = { 1 , 2 };
_fn
ต่อท้าย /* Function accepts 2 parameters and returns uint8_t */
/* Name of typedef has `_fn` suffix */
typedef uint8_t ( * my_func_typedef_fn )( uint8_t p1 , const char * p2 );
1
คำสั่งซ้อนกัน1
ขนาดเยื้องสำหรับแต่ละรัง /* OK */
if ( c ) {
do_a ();
} else {
do_b ();
}
/* Wrong */
if ( c )
do_a ();
else
do_b ();
/* Wrong */
if ( c ) do_a ();
else do_b ();
if
หรือ if-else-if
else
จะต้องอยู่ในบรรทัดเดียวกับการปิดตัวยึดของคำสั่งแรก /* OK */
if ( a ) {
} else if ( b ) {
} else {
}
/* Wrong */
if ( a ) {
}
else {
}
/* Wrong */
if ( a ) {
}
else
{
}
do-while
while
ส่วนหนึ่งจะต้องอยู่ในบรรทัดเดียวกับการปิดตัวยึดของส่วนที่ do
/* OK */
do {
int32_t a ;
a = do_a ();
do_b ( a );
} while ( check ());
/* Wrong */
do
{
/* ... */
} while ( check ());
/* Wrong */
do {
/* ... */
}
while ( check ());
if ( a ) {
do_a ();
} else {
do_b ();
if ( c ) {
do_c ();
}
}
if ( a ) do_b ();
else do_c ();
if ( a ) do_a (); else do_b ();
while
do-while
for
ลูปจะต้องมีวงเล็บ /* OK */
while ( is_register_bit_set ()) {}
/* Wrong */
while ( is_register_bit_set ());
while ( is_register_bit_set ()) { }
while ( is_register_bit_set ()) {
}
while
(หรือ for
do-while
ฯลฯ ) ว่างเปล่า (อาจเป็นกรณีในการเขียนโปรแกรมแบบฝัง) ให้ใช้วงเล็บบรรทัดเดียวที่ว่างเปล่า /* Wait for bit to be set in embedded hardware unit */
volatile uint32_t * addr = HW_PERIPH_REGISTER_ADDR ;
/* Wait bit 13 to be ready */
while ( * addr & ( 1 << 13 )) {} /* OK, empty loop contains no spaces inside curly brackets */
while ( * addr & ( 1 << 13 )) { } /* Wrong */
while ( * addr & ( 1 << 13 )) { /* Wrong */
}
while ( * addr & ( 1 << 13 )); /* Wrong, curly brackets are missing. Can lead to compiler warnings or unintentional bugs */
for
do-while
while
/* Not recommended */
int32_t a = 0 ;
while ( a < 10 ) {
.
..
...
++ a ;
}
/* Better */
for ( size_t a = 0 ; a < 10 ; ++ a ) {
}
/* Better, if inc may not happen in every cycle */
for ( size_t a = 0 ; a < 10 ; ) {
if (...) {
++ a ;
}
}
if
คำสั่งอาจใช้สำหรับการทำงานที่ได้รับมอบหมายหรือการเรียกใช้ฟังก์ชัน /* OK */
int a = condition ? if_yes : if_no ; /* Assignment */
func_call ( condition ? if_yes : if_no ); /* Function call */
switch ( condition ? if_yes : if_no ) {...} /* OK */
/* Wrong, this code is not well maintenable */
condition ? call_to_function_a () : call_to_function_b ();
/* Rework to have better program flow */
if ( condition ) {
call_to_function_a ();
} else {
call_to_function_b ();
}
case
break
ในแต่ละ case
หรือคำสั่ง default
/* OK, every case has single indent */
/* OK, every break has additional indent */
switch ( check ()) {
case 0 :
do_a ();
break ;
case 1 :
do_b ();
break ;
default :
break ;
}
/* Wrong, case indent missing */
switch ( check ()) {
case 0 :
do_a ();
break ;
case 1 :
do_b ();
break ;
default :
break ;
}
/* Wrong */
switch ( check ()) {
case 0 :
do_a ();
break ; /* Wrong, break MUST have indent as it is under case */
case 1 :
do_b (); /* Wrong, indent under case is missing */
break ;
default :
break ;
}
default
เสมอ /* OK */
switch ( var ) {
case 0 :
do_job ();
break ;
default :
break ;
}
/* Wrong, default is missing */
switch ( var ) {
case 0 :
do_job ();
break ;
}
break
ภายในcase
switch ( a ) {
/* OK */
case 0 : {
int32_t a , b ;
char c ;
a = 5 ;
/* ... */
break ;
}
/* Wrong */
case 1 :
{
int32_t a ;
break ;
}
/* Wrong, break shall be inside */
case 2 : {
int32_t a ;
}
break ;
}
_
ตัวละครยกเว้นว่ามีการทำเครื่องหมายไว้อย่างชัดเจนว่าเป็นฟังก์ชั่นซึ่งอาจจะถูกแทนที่ในอนาคตด้วยไวยากรณ์ฟังก์ชั่นปกติในอนาคต /* OK */
#define SQUARE ( x ) ((x) * (x))
/* Wrong */
#define square ( x ) ((x) * (x))
/* OK */
#define MIN ( x , y ) ((x) < (y) ? (x) : (y))
/* Wrong */
#define MIN ( x , y ) x < y ? x : y
/* Wrong */
#define MIN ( x , y ) (x) < (y) ? (x) : (y)
#define SUM ( x , y ) (x) + (y)
/* Imagine result of this equation using wrong SUM implementation */
int32_t x = 5 * SUM ( 3 , 4 ); /* Expected result is 5 * 7 = 35 */
int32_t x = 5 * ( 3 ) + ( 4 ); /* It is evaluated to this, final result = 19 which is not what we expect */
/* Correct implementation */
#define MIN ( x , y ) ((x) < (y) ? (x) : (y))
#define SUM ( x , y ) ((x) + (y))
do {} while (0)
typedef struct {
int32_t px , py ;
} point_t ;
point_t p ; /* Define new point */
/* Wrong implementation */
/* Define macro to set point */
#define SET_POINT ( p , x , y ) (p)->px = (x); (p)->py = (y) /* 2 statements. Last one should not implement semicolon */
SET_POINT ( & p , 3 , 4 ); /* Set point to position 3, 4. This evaluates to... */
( & p ) -> px = ( 3 ); ( & p ) -> py = ( 4 ); /* ... to this. In this example this is not a problem. */
/* Consider this ugly code, however it is valid by C standard (not recommended) */
if ( a ) /* If a is true */
if ( b ) /* If b is true */
SET_POINT ( & p , 3 , 4 ); /* Set point to x = 3, y = 4 */
else
SET_POINT ( & p , 5 , 6 ); /* Set point to x = 5, y = 6 */
/* Evaluates to code below. Do you see the problem? */
if ( a )
if ( b )
( & p ) -> px = ( 3 ); ( & p ) -> py = ( 4 );
else
( & p ) -> px = ( 5 ); ( & p ) -> py = ( 6 );
/* Or if we rewrite it a little */
if ( a )
if ( b )
( & p ) -> px = ( 3 );
( & p ) -> py = ( 4 );
else
( & p ) -> px = ( 5 );
( & p ) -> py = ( 6 );
/*
* Ask yourself a question: To which `if` statement does the `else` keyword belong?
*
* Based on first part of code, answer is straight-forward. To inner `if` statement when we check `b` condition
* Actual answer: Compilation error as `else` belongs nowhere
*/
/* Better and correct implementation of macro */
#define SET_POINT ( p , x , y ) do { (p)->px = (x); (p)->py = (y); } while (0) /* 2 statements. No semicolon after while loop */
/* Or even better */
#define SET_POINT ( p , x , y ) do { /* Backslash indicates statement continues in new line */
( p ) -> px = ( x );
( p ) -> py = ( y );
} while ( 0 ) /* 2 statements. No semicolon after while loop */
/* Now original code evaluates to */
if ( a )
if ( b )
do { ( & p ) -> px = ( 3 ); ( & p ) -> py = ( 4 ); } while ( 0 );
else
do { ( & p ) -> px = ( 5 ); ( & p ) -> py = ( 6 ); } while ( 0 );
/* Every part of `if` or `else` contains only `1` inner statement (do-while), hence this is valid evaluation */
/* To make code perfect, use brackets for every if-ifelse-else statements */
if ( a ) { /* If a is true */
if ( b ) { /* If b is true */
SET_POINT ( & p , 3 , 4 ); /* Set point to x = 3, y = 4 */
} else {
SET_POINT ( & p , 5 , 6 ); /* Set point to x = 5, y = 6 */
}
}
#ifdef
หรือ #ifndef
ใช้ defined()
หรือ !defined()
แทน #ifdef XYZ
/* do something */
#endif /* XYZ */
if/elif/else/endif
เสมอ /* OK */
#if defined( XYZ )
/* Do if XYZ defined */
#else /* defined(XYZ) */
/* Do if XYZ not defined */
#endif /* !defined(XYZ) */
/* Wrong */
#if defined( XYZ )
/* Do if XYZ defined */
#else
/* Do if XYZ not defined */
#endif
#if
คำสั่ง /* OK */
#if defined( XYZ )
#if defined( ABC )
/* do when ABC defined */
#endif /* defined(ABC) */
#else /* defined(XYZ) */
/* Do when XYZ not defined */
#endif /* !defined(XYZ) */
/* Wrong */
#if defined( XYZ )
#if defined( ABC )
/* do when ABC defined */
#endif /* defined(ABC) */
#else /* defined(XYZ) */
/* Do when XYZ not defined */
#endif /* !defined(XYZ) */
รหัสที่บันทึกไว้ช่วยให้ Doxygen สามารถแยกวิเคราะห์และสร้างเอาต์พุต HTML/PDF/LaTex ได้ดังนั้นจึงเป็นเรื่องสำคัญมากที่จะต้องทำอย่างถูกต้องในระยะแรกของโครงการ
variables
functions
และ structures/enumerations
สำหรับ doxygen เสมออย่าใช้ @
5x4
( 5
แท็บ) เสมอจากจุดเริ่มต้นของบรรทัดสำหรับข้อความ /**
* brief Holds pointer to first entry in linked list
* Beginning of this text is 5 tabs (20 spaces) from beginning of line
*/
static
type_t * list ;
/**
* brief This is point struct
* note This structure is used to calculate all point
* related stuff
*/
typedef struct {
int32_t x ; /*!< Point X coordinate */
int32_t y ; /*!< Point Y coordinate */
int32_t size ; /*!< Point size.
Since comment is very big,
you may go to next line */
} point_t ;
/**
* brief Point color enumeration
*/
typedef enum {
COLOR_RED , /*!< Red color */
COLOR_GREEN , /*!< Green color */
COLOR_BLUE , /*!< Blue color */
} point_color_t ;
brief
และพารามิเตอร์ทั้งหมดin
หรือ out
สำหรับ อินพุต และ เอาต์พุต ตามลำดับreturn
หากส่งคืนบางสิ่ง สิ่งนี้ใช้ไม่ได้สำหรับฟังก์ชั่น void
note
หรือ warning
:
ระหว่างชื่อพารามิเตอร์และคำอธิบาย /**
* brief Sum `2` numbers
* param[in] a: First number
* param[in] b: Second number
* return Sum of input values
*/
int32_t
sum ( int32_t a , int32_t b ) {
return a + b ;
}
/**
* brief Sum `2` numbers and write it to pointer
* note This function does not return value, it stores it to pointer instead
* param[in] a: First number
* param[in] b: Second number
* param[out] result: Output variable used to save result
*/
void
void_sum ( int32_t a , int32_t b , int32_t * result ) {
* result = a + b ;
}
ref
เพื่อระบุว่าอันไหน /**
* brief My enumeration
*/
typedef enum {
MY_ERR , /*!< Error value */
MY_OK /*!< OK value */
} my_enum_t ;
/**
* brief Check some value
* return ref MY_OK on success, member of ref my_enum_t otherwise
*/
my_enum_t
check_value ( void ) {
return MY_OK ;
}
NULL
) สำหรับค่าคงที่หรือตัวเลข /**
* brief Get data from input array
* param[in] in: Input data
* return Pointer to output data on success, `NULL` otherwise
*/
const void *
get_data ( const void * in ) {
return in ;
}
hideinitializer
/**
* brief Get minimal value between `x` and `y`
* param[in] x: First value
* param[in] y: Second value
* return Minimal value between `x` and `y`
* hideinitializer
*/
#define MIN ( x , y ) ((x) < (y) ? (x) : (y))
file
และคำอธิบาย brief
ตามด้วยบรรทัดว่าง (เมื่อใช้ doxygen) /**
* file template.h
* brief Template include file
*/
/* Here is empty line */
/**
* file template.h
* brief Template include file
*/
/*
* Copyright (c) year FirstName LASTNAME
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* This file is part of library_name.
*
* Author: FirstName LASTNAME <[email protected]>
*/
#ifndef
C++
C++
ตรวจสอบextern
สำหรับตัวแปรโมดูลส่วนกลางในไฟล์ส่วนหัวกำหนดไว้ในไฟล์ต้นฉบับในภายหลัง /* file.h ... */
#ifndef ...
extern int32_t my_variable; /* This is global variable declaration in header */
#endif
/* file.c ... */
int32_t my_variable; /* Actually defined in source */
ไม่รวมไฟล์ .c
ในไฟล์ .c
อื่น
ไฟล์ .c
ควรรวมไฟล์ .h
ที่สอดคล้องกันก่อนอื่นเว้นแต่จะมีความจำเป็นอย่างอื่นอย่างชัดเจน
อย่ารวมการประกาศส่วนตัวของโมดูลในไฟล์ส่วนหัว
ตัวอย่างไฟล์ส่วนหัว (ไม่มีใบอนุญาตเพื่อประโยชน์ของตัวอย่าง)
/* License comes here */
#ifndef TEMPLATE_HDR_H
#define TEMPLATE_HDR_H
/* Include headers */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* File content here */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* TEMPLATE_HDR_H */
พื้นที่เก็บข้อมูลมาพร้อมกับไฟล์ .clang-format
เสมอไปซึ่งเป็นการกำหนดค่าอินพุตสำหรับเครื่องมือ clang-format
มันสามารถรวมเข้ากับ IDES เทคโนล่าสุดได้อย่างราบรื่นรวมถึง VSCODE การจัดรูปแบบจากนั้นจะเกิดขึ้นที่จุดในแต่ละไฟล์บันทึก
https://code.visualstudio.com/docs/cpp/cpp-ide#_code-formatting
Astyle เป็นซอฟต์แวร์ที่ยอดเยี่ยมที่สามารถช่วยในการจัดรูปแบบรหัสตามการกำหนดค่าอินพุต
ที่เก็บนี้มีไฟล์ astyle-code-format.cfg
ซึ่งสามารถใช้กับซอฟต์แวร์ AStyle
astyle --options="astyle-code-format.cfg" "input_path/*.c,*.h" "input_path2/*.c,*.h"
การกำหนดค่าสไตล์ศิลปะล้าสมัยและไม่อัปเดตอีกต่อไป
ที่เก็บประกอบด้วยไฟล์ eclipse-ext-kr-format.xml
ที่สามารถใช้กับเครื่องมือที่ใช้ Eclipse เพื่อตั้งค่าตัวเลือกรูปแบบ
มันขึ้นอยู่กับรูปแบบ K&R ที่มีการปรับเปลี่ยนเพื่อเคารพกฎข้างต้น คุณสามารถนำเข้าภายในการตั้งค่า Eclipse Preferences -> LANGUAGE -> Code Style -> Formatter