STYLE(9)

명칭
style - 커널 원시 파일의 스타일 가이드

해설
이 파일은 FreeBSD 소스 트리의 커널 소스에 바람직한 스타일을 명기해 있습니다. 이것은 유저 랜드의 코드 스타일의 안내이기도 합니다. 예에 대해, 스타일 규칙의 대부분을 암묵적으로 사용하고 있습니다. style 하지만 이러한 사례에 대해 언급하고 있지 않다고 엄하게 책하기 전에, 주의해 예를 확인해 주세요. style (은)는 그러한 일에 대해서는 기술하고 있지 않습니다.

    /*
     * FreeBSD 를 위한 스타일 가이드입니다.
     * CSRG 의 KNF (Kernel Normal Form, 커널 표준 서식)에 근거하고 있습니다.
     *
     *	@(#) style	1.14 (Berkeley) 4/28/95
     * $FreeBSD: src/share/man/man9/style. 9, v 1.32. 2.19 2002/04/14 19:28:03 asmodai Exp $
     */
    /*
     * 매우 중요한 1 행의 코멘트는 이와 같이 합니다.
     */
    /* 대부분의 1 행의 코멘트는 이와 같이 합니다.  */
    /*
     * 복수행에 걸치는 코멘트는 이와 같이 합니다. 실제의 문장을 씁니다. 실제의
     * 단락으로 보이도록(듯이) 묻어 갑니다.
     */

저작권 헤더의 뒤에는 공행을 1 행 넣어 원시 파일에는 rcsid (을)를 붙입니다. 버전 콘트롤 시스템의 ID 태그는, 파일중에 1 개만 있어야 합니다 (이 파일에서는 다릅니다만). C/C++ 원시 파일 이외는 이 예에 따릅니다만, C/C++ 원시 파일은 이후의 예에 따릅니다. 외부로부터 입수한 파일의 모든 VCS (버전 콘트롤 시스템) 리버젼 식별자는, 존재하면 유지합니다. 이것에는, 파일의 와 력을 나타내는 복수의 ID 도 포함합니다. 일반적으로, ‘$’ 도 포함해, ID 는 그대로로 합니다. 외부로부터의 VCS ID 의 전에 "From" (을)를 붙이는 이유는 없습니다. 대부분의 비 FreeBSD 의 VCS ID 는, 코멘트중에서는 탭으로 인덴트 되고 있겠지요.

    #include <sys/cdefs.h>
    __RCSID("@(#) style	1.14 (Berkeley) 4/28/95");
    __FBSDID("$FreeBSD: src/share/man/man9/style. 9, v 1.32. 2.19 2002/04/14 19:28:03 asmodai Exp $");

헤더 파일의 전에, 공행을 1 행 붙입니다.

커널의 인클루드 파일 (즉, sys/*.h )(이)가 처음에 옵니다. 통상, <sys/types.h> 또는 <sys/param.h> 의 어느 쪽인지가 필요합니다만, 양쪽 모두는 필요없을 것입니다. <sys/types.h> 하 <sys/cdefs.h> (을)를 인클루드 하고 있어, 의존관계(dependencies)는 문제 없습니다.

    #include <sys/types.h>		/* 산괄호에 의한 비로컬 인클루드 */

네트워크 프로그램인 경우는, 다음에 네트워크 인클루드 파일을 둡니다.

    #include <net/if.h>
    #include <net/if_dl.h>
    #include <net/route.h>
    #include <netinet/in.h>
    #include <protocols/rwhod.h>

커널용의 파일에는, /usr/include 안의 파일을 사용하지 말아 주세요.

그리고 공행을 두어, /usr 인클루드 파일을 계속합니다. /usr 인클루드 파일은 알파벳순서에 정렬 되고 있어야 합니다.

    #include <stdio.h>

글로벌인 패스명은 /usr/include/paths.h 그리고 정의되고 있습니다. 프로그램에 로컬인 패스명은 로컬 디렉토리의 "pathnames.h" 에 넣습니다.

    #include <paths.h>

그리고, 공행이 있어, 유저 인클루드 파일이 옵니다.

    #include "pathnames.h"		/* " " 에 의한 로컬 인클루드 */

어플리케이션 인터페이스를 실장하고 있는 경우를 제외해, 실장의 이름 공간에서 #define 하거나 이름을 정의하거나 해서는 안됩니다.

“안전하지 않다” 매크로 (부작용을 가지고 있는 것)의 이름과 분명한 정수의 매크로의 이름은 모두 대문자입니다. 식과 같이 전개되는 매크로는, 단일의 토큰으로 할까 외측에 괄호를 붙입니다. #define (와)과 매크로명의 사이에 탭 캐릭터를 1 개 넣습니다. 매크로가 있는 함수의 인 라인 전개인 경우는, 함수명은 모두 소문자로, 매크로는 모두 대문자의 같은 이름을 가집니다. 매크로가 1 행 이상 필요한 경우는, brace ‘({’ (와)과 ‘}’) (을)를 사용합니다. backslash는 오른쪽맞춤 합니다. 이렇게 하면(자) 읽기 쉬워집니다. 매크로가 복합문을 캡슐화하는 경우에는, 그것을 do 루프로 둘러쌉니다. 이것에 의해, if 문장으로 안전하게 사용할 수 있습니다. 마지막 문장의 종단의 세미콜론은, 매크로는 아니고 매크로의 실시시에 붙일 수 있어야 합니다. 이것에 의해, 정서기나 에디터로 문법 해석하기 쉬워집니다.

    #define	MACRO(x, y) do {						\
    	variable = (x) + (y);						\
    	(y) += 2;							\
    } while(0)

열거치는 모두 대문자를 사용합니다.

    enum enumtype { ONE, TWO } et;

구조체 중(안)에서 변수를 선언할 때에는, 사용순서, 사이즈순서, 알파벳순서에 정렬 해 선언합니다. 최초의 구분은 통상 적용합니다만, 예외가 있습니다. 각 선언은, 각각 독립한 행에서 실시합니다. 구조체의 이름의 위치를, 당신의 판단으로 읽기 쉽게, 탭 1 개 또는 2 개를 사용해 가지런히 해 주세요. 대부분의 멤버를 가지런히 하는데 충분하면, 단일의 탭을 사용해야 합니다. 매우 긴 형태의 뒤의 이름은, 단일의 공백에서 단락지어져야 합니다.

중요한 구조체는, 그것이 사용되는 파일의 선두에서 선언되는지, 복수의 원시 파일로 사용되는 경우는 다른 헤더 파일로 선언되어야 합니다. 구조체가 헤더 파일로 선언되고 있는 경우에는, 그것들 구조체의 사용은, 선언과는 나눌 수 있어야 하는 것으로, 한편 extern (이어)여야 합니다.

    struct foo {
    	struct foo	*next;		/* 사용중의 foo 의 리스트 */
    	struct mumble	amumble;	/* mumble 의 코멘트 */
    	int		bar;		/* 코멘트를 가지런히 하려 하고 있습니다 */
    	struct verylongtypename *baz;	/* 탭 2 개에는 들어가지 않습니다 */
    };
    struct foo *foohead;			/* 글로벌인 foo 리스트의 선두 */

가능한 때에는 반드시, 당신 자신으로 리스트를 조작하는 것이 아니라, queue(3) 매크로를 사용해 주세요. 따라서, 전의 예를 보다 좋게 쓰면(자) 다음과 같이 됩니다.

    #include <sys/queue.h>
    struct foo {
    	LIST_ENTRY(foo)	link;		/* foo 리스트에 큐 매크로를 사용 */
    	struct mumble	amumble;	/* mumble 의 코멘트 */
    	int		bar;		/* 코멘트를 가지런히 하려 하고 있습니다 */
    	struct verylongtypename *baz;	/* 탭 2 개에는 들어가지 않습니다 */
    };
    LIST_HEAD(, foo) foohead;		/* 글로벌인 foo 리스트의 선두 */

구조체의 형태에 typedef 를 사용하는 일은 피해 주세요. 사용해 버리면(자), 구조체에의 포인터를 불투명 (opaque)에 사용하는 것이, 어플리케이션에 있어 불가능이 됩니다. 통상의 struct 태그를 사용하면(자), 이것이 가능해져, 한편 유익합니다. 규약이 typedef (을)를 요구하는 경우에는, 그 이름을 구조체 태그에 일치시킵니다. 표준 C 또는 에 의해 명시된 것을 제외해서는, “_t” 그리고 끝나는 typedef 를 피해 주세요.

    /* 구조체명과 typedef 를 일치시킵니다 */
    typedef struct bar {
    	int	level;
    } BAR;
    typedef	int		foo;		/* 이것은 foo 입니다 */
    typedef	const long	baz;		/* 이것은 baz 입니다 */

모든 함수는 어디선가 prototype 됩니다.

사적인 함수 (즉, 다른 어디에서라도 사용되지 않는 함수등)의 함수 prototype는, 최초의 소스 모듈의 선두에 놓여집니다. 단일의 소스 모듈에 로컬인 함수는, static 그리고 선언되어야 합니다.

커널의 다른 부분으로부터 사용되는 함수는, 관련이 있는 인클루드 파일 중(안)에서 prototype 됩니다.

복수의 모듈로 로컬에 사용되는 함수는, "extern.h" 등의 분리한 헤더 파일안에 놓여집니다.

일반적으로 원시 파일이 K&R 구약 성서 컴파일러로 컴파일 가능한 (나무) 때에게만, 인클루드 파일 <sys/cdefs.h> 의 __P 매크로를 사용합니다. 새로운 코드에서의 __P 매크로의 사용은 반대되고 있습니다만, 기존의 파일에 대한 수정은 그 파일의 규약과 시종 일관 하고 있어야 합니다.

파일의 50% 인가 그 이상을 말려들게 한 수정의 경우는, 일반적으로 코드는 “새로운 코드” (으)로 간주할 수가 있습니다. 이것은 기존의 코드의 관례를 깨어, 현재의 style 가이드 라인을 사용하는데 충분합니다.

커널은 파라미터의 형태에 관련지을 수 있었던 이름을 가집니다. 예를 들면, 커널내에서 이와 같이 사용합니다.

    void	function(int fd);

유저 랜드의 어플리케이션에 대해서 보이는 헤더 파일 중(안)에서는, 가시의 prototype는, 형태를 수반했다 “보호되었다” (언더스코어로 개시한다) 이름을 사용하는지, 형태만으로 이름을 사용하지 않는가의 어느 쪽인지가 필요합니다. 보호된 이름의 사용이 보다 바람직합니다. 예를 들면, 이와 같이 사용합니다.

    void	function(int);

또는

    void	function(int _fd);

prototype는 함수명의 행 가지런히 하고를 행하기 위해서(때문에), 탭의 뒤에 추가의 스페이스 캐릭터를 두어도 상관하지 않습니다.

    static char	*function(int _arg, const char *_arg2, struct foo *_arg3,
    		    struct bar *_arg4);
    static void	 usage(void);
    /*
     * 모든 주요한 routine는 그것이 무엇을 하는지를 간결하게 기술했다
     * 코멘트를 가져야 합니다. "main" routine의 전의 코멘트는
     * 그 프로그램이 무엇을 하는지를 기술해야 합니다.
     */
    int
    main(int argc, char *argv[])
    {
    	long num;
    	int ch;
    	char *ep;

일관성을 위해서(때문에), 옵션의 해석에는 getopt(3) 하지만 사용되어야 합니다. getopt(3) 호출과 switch 문장에서는, 옵션을 정렬 해야 합니다만, switch 문장의 cascade의 일부의 경우는 예외입니다. switch 문장의 cascade 요소는 FALLTHROUGH 코멘트를 가져야 합니다. 수치의 인수는 정밀도가 체크되어야 합니다. 도달할 수 없는 코드는 NOTREACHED 코멘트를 가져야 합니다.

    	while ((ch = getopt(argc, argv, "abn:")) ! = -1)
    		switch (ch) {		/* switch 를 인덴트 */
    		case 'a':		/* case 는 인덴트 하지 않는다 */
    			aflag = 1;
    			/* FALLTHROUGH */
    		case 'b':
    			bflag = 1;
    			break;
    		case 'n':
    			num = strtol(optarg, &ep, 10);
    			if (num <= 0 || *ep ! = '\0') {
    				warnx("illegal number, -n argument -- %s",
    				    optarg);
    				usage();
    			}
    			break;
    		case '? ':
    		default:
    			usage();
    			/* NOTREACHED */
    		}
    	argc -= optind;
    	argv += optind;

예약어(reserved word) (if, while, for, return, switch) 의 후에 스페이스를 들어갈 수 있습니다. 아무것도 따르지 않는가 다만 1 살의 후미를 수반하는 제어문은, brace를 사용하지 않습니다. 1 살의 문장이 복수행인 문장의 경우에는, 이것은 용서됩니다. 엔들레스 루프는 while (은)는 아니고 for 그리고 행합니다.

    	for (p = buf; *p ! = '\0'; ++p)
    		;	/* 굳이 이루어 */
    	for (;;)
    		stmt;
    	for (;;) {
    		z = a + really + long + statement + that + needs +
    		    two lines + gets + indented + four + spaces +
    		    on + the + second + and + subsequent + lines;
    	}
    	for (;;) {
    		if (cond)
    			stmt;
    	}
    	if (val ! = NULL)
    		val = realloc(val, newsize);

for 루프의 각부는 하늘인 채 남겨도 상관하지 않습니다. 비정상으로 복잡한 routine가 아닌 한은, 블록안에 선언을 두어 되지 않습니다.

    	for (; cnt < 15; cnt++) {
    		stmt1;
    		stmt2;
    	}

인덴트는 8 캐릭터의 탭입니다. 제 2 레벨의 인덴트는 4 캐릭터의 스페이스입니다. 긴 분을 되풀이할 필요가 있는 경우, 오퍼레이터를 줄 끝에 둡니다.

    	while (cnt < 20 && this_variable_name_is_too_long_for_its_own_good &&
    	    ep ! = NULL)
    		z = a + really + long + statement + that + needs +
    		    two lines + gets + indented + four + spaces +
    		    on + the + second + and + subsequent + lines;

공백 캐릭터를 줄 끝에 추가 해서는 안됩니다. 또, 인덴트를 형성하기 위해서는, 탭과 그 후에 스페이스만을 사용합니다. 탭이 낳는 이상의 스페이스나, 탭의 전의 스페이스는 사용하지 않습니다.

brace의 종료와 개시는 else (와)과 같은 행에 놓여집니다. 필요하지 않은 brace는 생략해도 상관하지 않습니다.

    	if (test)
    		stmt;
    	else if (bar) {
    		stmt;
    		stmt;
    	} else
    		stmt;

함수명의 뒤는 스페이스를 비우지 않습니다. 콤마의 뒤에는 스페이스를 가집니다. ‘(’ 또는 ‘[’ 의 뒤 또는 ‘’] 또는 ‘’) 의 전에는 스페이스를 비우지 않습니다.

    	error = function(a1, a2);
    	if (error ! = 0)
    		exit(error);

단항 연산자는 스페이스를 요구합니다만, 2항연산자는 요구합니다. 우선 순위가 요구하는 경우 또는 문장이 괄호없이는 혼란하는 경우 이외는, 괄호는 사용하지 않습니다. 타인은 당신보다 혼란하기 쉬울지도 모른다고 하는 것을 기억해 둬 주세요. 당신은 이하를 이해할 수 있습니까?

    	a = b->c[0] + ~d == (e || f) || g && h ?  i : j >> 1;
    	k = ! (l & FLAGS);

성공시에는 0 으로, 또는 sysexits(3) 에 미리 정의되어 있는 값으로 exit 해야 합니다.

    	exit(EX_OK);	/*
    			 * "Exit 0 on success. " (성공시는 0 으로 종료)
    			 * (와)과 같이 명백한 코멘트는 피해 주세요
    			 */
    }

함수의 형태는, 함수 자신에게 선행하는 행에 있어야 합니다.

    static char *
    function(int a1, int a2, float fl, int a4)
    {

함수 중(안)에서 변수를 선언할 때에는, 사이즈순서에, 다음에 알파벳순서에 정렬 해 선언합니다. 1 행에 복수의 선언은 가능합니다. 행이 흘러넘치는 경우는, 형태의 예약어(reserved word)를 재차 사용합니다.

선언시에 변수를 초기화하는 것에 의해 코드를 불명료하게 하지 않게 주의해 주세요. 이 기능은 잘 생각해 사용해 주세요. 초기화에 함수 호출을 사용하지 말아 주세요.

    	struct foo one, *two;
    	double three;
    	int *four, five;
    	char *six, seven, eight, nine, ten, eleven, twelve;
    	four = myfunction();

다른 함수의 내부에서 함수를 선언하지 말아 주세요. ANSI C 에 의하면, 이러한 선언은, 선언의 네스팅에 의하지 않고, 파일 스코프가 됩니다. 로컬 스코프로 보이는 것의 중에 파일의 선언을 숨기는 것은 바람직하지 않고, 좋은 컴파일러는 불평을 늘어 놓습니다.

캐스트와 sizeof 연산자의 뒤에는 스페이스를 계속하지 않습니다. 이 규칙은 indent(1) 하지만 이해하지 않는 것에 주의해 주세요.

NULL (은)는, 선호되는 누르포인타 정수입니다. 컴파일러가 형태를 알고 있는 문맥, 예를 들면 대입에서는, (type *)0 또는 (type *)NULL 대신에, NULL (을)를 사용합니다. 다른 문맥에서는, 특히 모든 함수의 인수에서는, (type *)NULL (을)를 사용합니다. (함수의 prototype가 스코프외일지도 모르는 경우에, 캐스트는 여러가지 인수에 있어 필수로, 그 외의 인수에 있어서도 필요합니다. ) 포인터는 NULL (와)과 비교합니다. 예를 들면,

    ! (p = f())

(이)가 아니고, 이와 같이 사용합니다.

    (p = f()) == NULL

진리치가 아닌 경우, 테스트에는! (을)를 사용하지 말아 주세요. 예를 들면, 아래와 같이 사용합니다.

    if (*p == '\0')

아래와 같이는 사용하지 않습니다.

    if (! *p)

void * (을)를 돌려주는 routine에서는, 반환값을 어느 포인터형에도 캐스트 해 되지 않습니다.

err(3) 또는 warn(3) (을)를 사용해, 마음대로 만들지 말아 주세요.

    	if ((four = malloc(sizeof(struct foo))) == NULL)
    		err(1, (char *) NULL);
    	if ((six = (int *) overflow()) == NULL)
    		errx(1, "number overflowed");
    	return (eight);
    }

낡은 스타일의 함수 선언은 이와 같이 되어 있습니다.

    static char *
    function(a1, a2, fl, a4)
    	int a1, a2;	/* int 형도 선언합니다, 디폴트로 하지 않는 것 */
    	float fl;	/* double 와 float 의 차이에 조심해 주세요 */
    	int a4;		/* 나온 순서에 선언합니다 */
    {

당신이 명확하게 K&R 와의 호환성을 필요로 하는 경우 이외는, ANSI 의 함수 선언을 사용해 주세요. 긴 파라미터 리스트의 즉시에는, 4 개의 공백에 의한 통상의 인덴트를 붙입니다.

가변 개수의 인수는 이와 같이 합니다.

    #include <stdarg.h>
    void
    vaf(const char *fmt, ...)
    {
    	va_list ap;
    	va_start(ap, fmt);
    	STUFF;
    	va_end(ap);
    	/* void 형의 함수에 return 는 불필요합니다 */
    }
    static void
    usage()
    {
    	/* 함수가 로컬 변수를 가지지 않는 경우, 공행을 넣습니다 */

fputs(3), puts(3), putchar(3) 등이 아니고, printf(3) (을)를 사용해 주세요. 이것은 빠르고 대체로는 예뻐, 말할 필요도 없이 시시한 버그를 피합니다.

사용법 (usage)의 문장은 메뉴얼 페이지의 SYNOPSIS (서식)의 같아야 합니다. 사용법의 문장은, 다음의 구조여야 합니다:

1.  오퍼랜드가 없는 옵션이, 최초로 알파벳순서에, 1 조의 대괄호 ‘([’ (와)과 ‘’]) 그리고 묶어집니다.

2.  옵션과 그 오퍼랜드가 이것도 알파벳순서에 계속되어, 각각의 옵션과 그 인수를 1 조의 대괄호로 묶습니다.

3.  필수의 인수 (만약 있으면) 하지만 계속되어, 명령행으로 지정되어야 할 순서로 일람 됩니다.

4.  마지막으로, 모든 임의의 인수가 지정되어야 할 순서로, 모두 대괄호안에 일람 됩니다.

세로의 곧은 선 (‘|’) (은)는, “양자택일” 의 옵션 또는 인수를 분할해, 동시에 사용하는 옵션과 인수는, 단일의 대괄호로 묶습니다.

    "usage: f [-aDde] [-b b_arg] [-m m_arg] req1 req2 [opt1 [opt2]]\n"
    "usage: f [-a | -b] [-c [-dEe] [-n number]]\n"

    	(void) fprintf(stderr, "usage: f [-ab]\n");
    	exit(EX_USAGE);
    }

메뉴얼 페이지의 옵션 기술은, 순수한 알파벳순서 이어야 하는 것에 주의해 주세요. 즉, 옵션이 인수를 취하는지 아닌지에 관련되지 않는다고 하는 것입니다. 알파벳순서는, 전술의 대문자 소문자의 순서를 고려에 넣어야 합니다.

새로운 중심적인 커널의 코드는, 적당히 style 가이드에 따라야 합니다. 써드파티가 메인트넌스하는 모듈이나 디바이스 드라이버를 위한 가이드 라인은 보다 완만합니다만, 최저한 내부적으로는 그들의 일관한 스타일이어야 합니다.

소스 리포지터리(repository)의 문체의 변경 (공백 캐릭터의 변경을 포함한다)은 곤란해, 정당한 이유없이는 피해야 합니다. 리포지터리(repository)안의 대체로 KNF style 에 적합하고 있는 코드는, 이 적합으로부터 떨어져 되지 않습니다.

가능한 때에는 언제라도, 코드는 코드 checker (예를 들면, lint(1) 또는 gcc -Wall )(을)를 통과해, 발생하는 경고는 최소한이 되어야 합니다.

관련 항목
indent(1), lint(1), err(3), sysexits(3), warn(3)

역사
이 페이지는 4.4BSD Lite2 릴리스의 src/admin/style/style 파일에 크게 기초를 두고 있어, 현재의 실장과 FreeBSD 프로젝트의 요망을 반영해, 빈번하게 갱신하고 있습니다.