Linux kernel coding style | Linux內核編碼風格html
This is a short document describing the preferred coding style for the linux kernel. Coding style is very personal, and I won't force my views on anybody, but this is what goes for anything that I have to be able to maintain, and I'd prefer it for most other things too. Please at least consider the points made here.
本文是一個簡短的文檔,描述了Linux內核的首選的編碼風格。編碼風格是很是我的化的東東,不強迫任何人接受個人觀點。可是,這是我必須可以堅持的東西,也但願它對其餘大多數事情也有幫助。請至少考慮一下本文談及的要點。
First off, I'd suggest printing out a copy of the GNU coding standards, and NOT read it. Burn them, it's a great symbolic gesture.
首先,建議把GNU的編碼標準打印一份出來,而後不要閱讀,直接燒掉。這是一個了不得的象徵姿態。
Anyway, here goes:
言歸正轉:linux
1) Indentation | 縮進程序員
Tabs are 8 characters, and thus indentations are also 8 characters. There are heretic movements that try to make indentations 4 (or even 2!) characters deep, and that is akin to trying to define the value of PI to be 3.
製表符(Tab)佔8個字符的位置,所以縮進也是佔8個字符的位置。將縮進寬度設置爲4(甚至爲2!)被認爲是一種異教徒發起的運動,相似於將PI的值定義爲3。
Rationale: The whole idea behind indentation is to clearly define where a block of control starts and ends. Especially when you've been looking at your screen for 20 straight hours, you'll find it a lot easier to see how the indentation works if you have large indentations.
原理: 隱藏在縮進背後的思想是清楚地定義一個控制塊的開始和結束的位置。尤爲是當你盯着屏幕看了20個小時以後,你會發現,看到的縮進若是有較大的缺口的話,看起來就更容易。
Now, some people will claim that having 8-character indentations makes the code move too far to the right, and makes it hard to read on a 80-character terminal screen. The answer to that is that if you need more than 3 levels of indentation, you're screwed anyway, and should fix your program.
那麼,有些人會抱怨8個字符的縮進使得代碼向右邊移動得太遠了,對一個寬度爲80個字符的終端屏幕來講,讓人很難閱讀下去。答案是,若是你須要超過3個級別的縮進,不管如何,你都要想辦法了,並且應該修正你的程序。
In short, 8-char indents make things easier to read, and have the added benefit of warning you when you're nesting your functions too deep. Heed that warning.
簡而言之,寬度爲8個字符的縮進更適合閱讀,並且帶來額外的好處,那就是當你的函數嵌套太深時給出警告。請認真對待這種警告。
The preferred way to ease multiple indentation levels in a switch statement is to align the switch and its subordinate case labels in the same column instead of double-indenting the case labels. E.g.:
在switch語句中消除多級縮進的首選方法是,使case標籤與switch對齊,用單縮進代替雙縮進。例如:express
switch (suffix) { case 'G': case 'g': mem <<= 30; break; case 'M': case 'm': mem <<= 20; break; case 'K': case 'k': mem <<= 10; /* fall through */ default: break; }
Don’t put multiple statements on a single line unless you have something to hide:
不要把多個語句放在一行中,除非你有什麼須要隱藏起來:編程
if (condition) do_this; do_something_everytime;
Don’t put multiple assignments on a single line either. Kernel coding style is super simple. Avoid tricky expressions.
不要把多個變量分配放在同一行中。內核編碼風格可謂超級簡單。並且須要避免花裏胡哨的表達。
Outside of comments, documentation and except in Kconfig, spaces are never used for indentation, and the above example is deliberately broken.
除註釋、文檔和Kconfig除外,不要使用空格來縮進。上面的例子之因此破了例,由於是故意的。
Get a decent editor and don’t leave whitespace at the end of lines.
使用一個好的編輯器,請不要在行尾留空白(空格或Tab鍵)。vim
2) Breaking long lines and strings | 打破過長的代碼行和字符串api
Coding style is all about readability and maintainability using commonly available tools.
編碼風格是關於使用經常使用工具時的可讀性和可維護性。
The limit on the length of lines is 80 columns and this is a strongly preferred limit.
單行代碼的寬度限制爲80列,這是一個強有力且爲首選的限制。
Statements longer than 80 columns will be broken into sensible chunks, unless exceeding 80 columns significantly increases readability and does not hide information. Descendants are always substantially shorter than the parent and are placed substantially to the right. The same applies to function headers with a long argument list. However, never break user-visible strings such as printk messages, because that breaks the ability to grep for them.
超過80列的語句將被分解爲更明智的代碼塊,除非超過80列顯著地增長可讀性而且沒有隱藏信息。後代語句老是比父語句短得多,大致上都被放置在右邊。這一樣適用於帶有長參數列表的函數頭。然而,請永遠不要中斷用戶可見的字符串,例如printk的消息,由於一旦中斷了,就打破了他們被grep到的能力。數組
3) Placing Braces and Spaces | 括號和空格的位置緩存
The other issue that always comes up in C styling is the placement of braces. Unlike the indent size, there are few technical reasons to choose one placement strategy over the other, but the preferred way, as shown to us by the prophets Kernighan and Ritchie, is to put the opening brace last on the line, and put the closing brace first, thusly:
常常出如今C編碼風格中的另外一個問題就是大括號({})的位置問題。與縮進大小不一樣的是,在選擇放置大括號的時候,鮮有技術上的緣由,而是基於某種偏好。例如,咱們的先知(C語言教父)K&R,就是把‘{’放在一行的結尾,而把‘}’放在一行的開始。數據結構
if (x is true) { we do y }
This applies to all non-function statement blocks (if, switch, for, while, do). E.g.:
這適用於全部非函數語句塊(if, switch, for, while, do)。例如:
switch (action) { case KOBJ_ADD: return "add"; case KOBJ_REMOVE: return "remove"; case KOBJ_CHANGE: return "change"; default: return NULL; }
However, there is one special case, namely functions: they have the opening brace at the beginning of the next line, thus:
可是,有一個例外,那就是函數:開括號'{'放置在函數的下一行的開頭,所以:
int function(int x) { body of function }
Heretic people all over the world have claimed that this inconsistency is ... well ... inconsistent, but all right-thinking people know that (a) K&R are right and (b) K&R are right. Besides, functions are special anyway (you can't nest them in C).
分佈在世界各地的異教徒們都認爲這種不一致很差。可是,全部堅持正確思考的人們都知道(a)K&R是正確的,(b)K&R是正確的。此外,不管如何函數都是特殊的(你不能在C語言中嵌套它們)。
Note that the closing brace is empty on a line of its own, except in the cases where it is followed by a continuation of the same statement, ie a while in a do-statement or an else in an if-statement, like this:
注意右大括號'}'自己是空的,除非後面的語句是連續的,也就是說,在do...while...的while中或if...else...的else中,例如:
do { body of do-loop } while (condition);
and 和
if (x == y) { .. } else if (x > y) { ... } else { .... }
Rationale: K&R.
理論依據: K&R。
Also, note that this brace-placement also minimizes the number of empty (or almost empty) lines, without any loss of readability. Thus, as the supply of new-lines on your screen is not a renewable resource (think 25-line terminal screens here), you have more empty lines to put comments on.
另外,注意這種大括號的放置方法也減小了空行數(或幾乎爲空的行數),沒有損失任何可讀性。所以,因爲屏幕上新行的供應不是可再生資源(此處考慮25-行終端屏幕),因此您有更多的空行用於存放註釋。
Do not unnecessarily use braces where a single statement will do.
只有一行的語句塊不用大括號。
if (condition) action();
and 和
if (condition) do_this(); else do_that();
This does not apply if only one branch of a conditional statement is a single statement; in the latter case use braces in both branches:
若是if用了大括號, 那麼else即便只有一行也要用大括號。
if (condition) { do_this(); do_that(); } else { otherwise(); }
3.1) Spaces | 空格
Linux kernel style for use of spaces depends (mostly) on function-versus-keyword usage. Use a space after (most) keywords. The notable exceptions are sizeof, typeof, alignof, and __attribute__, which look somewhat like functions (and are usually used with parentheses in Linux, although they are not required in the language, as in: sizeof info after struct fileinfo info; is declared).
Linux內核編碼風格在使用空白的問題上主要取決於函數v.s.關鍵字。在(大部分)關鍵字的後面要使用空格。明顯的例外是sizeof、typeof、alignof和__attribute__,這看起來有點像函數(在Linux中,一般在這些關鍵字後添加括號,雖然他們不是C語言所必須的。例如:在C語言中,聲明struct fileinfo info以後,可使用sizeof info取得結構體變量info的長度。固然,咱們的編碼風格則是使用sizeof(info)去取得結構體變量info的長度)。
So use a space after these keywords:
在這些關鍵字後面要添加一個空格:
if, switch, case, for, do, while
but not with sizeof, typeof, alignof, or __attribute__. E.g.,
可是不要在sizeof, typeof, alignof, 或__attribute__後邊添加空格。例如:
s = sizeof(struct file);
Do not add spaces around (inside) parenthesized expressions. This example is bad:
在用括號'()'括起來的表達式的先後不要添加空格。下面這個例子就很糟糕:
s = sizeof( struct file );
When declaring pointer data or a function that returns a pointer type, the preferred use of * is adjacent to the data name or function name and not adjacent to the type name. Examples:
在聲明一個指針數據或一個返回值爲指針類型的函數時,讓*號與數據名或函數名相鄰,而不是與類型名相鄰。例如:
char *linux_banner; unsigned long long memparse(char *ptr, char **retptr); char *match_strdup(substring_t *s);
Use one space around (on each side of) most binary and ternary operators, such as any of these:
在大多數二目和三目運算符的每一個邊上加一個空格,例以下面的運算符中的其中任何一個:
= + - < > * / % | & ^ <= >= == != ? :
but no space after unary operators:
可是,在單目運算符以後不加空格:
& * + - ~ ! sizeof typeof alignof __attribute__ defined
no space before the postfix increment & decrement unary operators:
在做爲後綴的++和--單目運算符前面不加空格:
++ --
no space after the prefix increment & decrement unary operators:
在做爲前綴的++和--單目運算符後面不加空格:
++ --
and no space around the . and -> structure member operators.
在結構體成員操做符.和->的先後都不加空格。
Do not leave trailing whitespace at the ends of lines. Some editors with smart indentation will insert whitespace at the beginning of new lines as appropriate, so you can start typing the next line of code right away. However, some such editors do not remove the whitespace if you end up not putting a line of code there, such as if you leave a blank line. As a result, you end up with lines containing trailing whitespace.
每行代碼以後不要有多餘的空格。一些支持智能縮進的編輯器將在新起一行的開始插入空格以方便輸入下一行代碼。然而,一些這樣的編輯器並不刪除行尾的空格,好比你留下了一個空白行。其結果就是,你的代碼中包含了空白行。
Git will warn you about patches that introduce trailing whitespace, and can optionally strip the trailing whitespace for you; however, if applying a series of patches, this may make later patches in the series fail by changing their context lines.
Git會警告你的補丁包含有行尾空格,你能夠選擇性地刪除掉行尾空格;然而,若是打一系列補丁的話,這可能會使在此係列中的後續的補丁打失敗,由於刪除空白行改變了他們的上下文代碼行。
4) Naming | 命名
C is a Spartan language, and so should your naming be. Unlike Modula-2 and Pascal programmers, C programmers do not use cute names like ThisVariableIsATemporaryCounter. A C programmer would call that variable tmp, which is much easier to write, and not the least more difficult to understand.
C語言是一種斯巴達式的(嚴於自律的)語言,那麼命名也應如此。C程序員跟Modula-2和Pascal的程序員不同,不使用諸如ThisVariableIsATemporaryCounter同樣可愛的變量名。C程序員會給這個變量命名爲tmp,該名字更容易書寫,並且也不難理解。
HOWEVER, while mixed-case names are frowned upon, descriptive names for global variables are a must. To call a global function foo is a shooting offense.
然而,雖然混合的命名方式不盡人意,可是給全局變量命名的時候給出一個描述性名稱是必須的。給一個全局的函數命名爲foo是至關要不得的(容易挨槍子)。
GLOBAL variables (to be used only if you really need them) need to have descriptive names, as do global functions. If you have a function that counts the number of active users, you should call that count_active_users() or similar, you should not call it cntusr().
全局變量(只在真正須要時才使用)須要有描述性的名字,全局函數也同樣。若是你有一個函數用來計算活躍用戶的數量,你應該叫它爲相似count_active_users()的名字,不該該命名爲cntusr()。
Encoding the type of a function into the name (so-called Hungarian notation) is brain damaged - the compiler knows the types anyway and can check those, and it only confuses the programmer. No wonder MicroSoft makes buggy programs.
將函數的類型包含到函數名中(所謂的匈牙利命名法)簡直就是腦殘的作法,只會讓程序員感到困惑,由於編譯器知道類型並且能夠檢查類型好不啦?!好吧,難怪微軟的程序老是bug接連不斷。
LOCAL variable names should be short, and to the point. If you have some random integer loop counter, it should probably be called i. Calling it loop_counter is non-productive, if there is no chance of it being mis-understood. Similarly, tmp can be just about any type of variable that is used to hold a temporary value.
局部變量名應當簡短並且直抵要點。若是你有一些隨機整數循環計數器,應該命名它們爲i。將它們命名爲loop_counter,無助於提升生產力,若是沒有機會被人誤解的話。相似地,tmp能夠用於任意類型的臨時變量命名。
If you are afraid to mix up your local variable names, you have another problem, which is called the function-growth-hormone-imbalance syndrome. See chapter 6 (Functions).
若是您擔憂局部變量名會混淆,那麼您存在另外一個問題,那就是函數增加激素不平衡綜合症。請參加見第6章(函數)。
5) Typedefs
Please don't use things like vps_t. It's a mistake to use typedef for structures and pointers. When you see a
請不要使用諸如vps_t之類的類型定義。對結構體和指針使用typedef是一個錯誤。當你看到一個
vps_t a;
in the source, what does it mean? In contrast, if it says
在源代碼中,知道a是啥意思嗎? 相反,若是是
struct virtual_container *a;
you can actually tell what a is.
你就能夠知道a是什麼了。
Lots of people think that typedefs help readability. Not so. They are useful only for:
不少人認爲typedef有助於提升可讀性。其實否則。 typede只對以下情形有用:
徹底不透明的對象(typedef被用來隱藏對象)。
Example: pte_t etc. opaque objects that you can only access using the proper accessor functions.
例如: pte_t等。 對於不透明的對象,你只能使用適當的函數去訪問它。
!NOTE
Opaqueness and accessor functions are not good in themselves. The reason we have them for things like pte_t etc. is that there really is absolutely zero portably accessible information there.
清晰的整數類型,這一抽象有助於避免混淆整數是int或是long。
u8/u16/u32 are perfectly fine typedefs, although they fit into category (d) better than here.
u8/u16/u32是完美的typedefs, 雖然把他們歸類到(d)比在這裏(b)要合適。
!NOTE
Again - there needs to be a reason for this. If something is unsigned long, then there’s no reason to do typedef unsigned long myflags_t;
but if there is a clear reason for why it under certain circumstances might be an unsigned int and under other configurations might be unsigned long, then by all means go ahead and use a typedef.
可是,若是有一個明確的理由說明爲何它在某些狀況下多是一個unsigned int,而在其餘配置中多是一個unsigned long的話,那就絕不猶豫地經過各類手段去使用typedef。
當使用kernel的sparse工具作變量類型檢查時, 能夠用typedef定義一個類型。
在某些特殊狀況下,能夠用typedef定義等價於C99標準中的新類型。
Although it would only take a short amount of time for the eyes and brain to become accustomed to the standard types like uint32_t, some people object to their use anyway.
對於像uint32_t之類的標準類型,雖然只須要花費少許的時間讓眼睛和大腦去習慣和適應,可是一些人反正就是反對使用它們。
Therefore, the Linux-specific u8/u16/u32/u64 types and their signed equivalents which are identical to standard types are permitted – although they are not mandatory in new code of your own.
所以,Linux特定的u8/u16/u32/u64類型與標準類型相同,雖然不強制性在新代碼中使用它們。
When editing existing code which already uses one or the other set of types, you should conform to the existing choices in that code.
在編輯已經使用一個或一組其餘類型的已有的代碼時,應當遵照該代碼中業已存在的選擇。
Maybe there are other cases too, but the rule should basically be to NEVER EVER use a typedef unless you can clearly match one of those rules.
也許還有其餘的狀況,可是規則應該基本上是毫不使用typedef,除非你能夠很清楚地匹配到一個規則。
In general, a pointer, or a struct that has elements that can reasonably be directly accessed should never be a typedef.
通常狀況下,對於指針或能夠直接訪問其元素的結構體,永遠不要使用typedef去定義。
6) Functions | 函數
Functions should be short and sweet, and do just one thing. They should fit on one or two screenfuls of text (the ISO/ANSI screen size is 80x24, as we all know), and do one thing and do that well.
函數應當設計得簡潔,一個函數只作一件事兒。一個函數的文本至多佔兩屏(咱們知道,ISO/ANSI屏幕尺寸是80*24),並且只作一件事兒並把事情作好。
The maximum length of a function is inversely proportional to the complexity and indentation level of that function. So, if you have a conceptually simple function that is just one long (but simple) case-statement, where you have to do lots of small things for a lot of different cases, it's OK to have a longer function.
函數的最大長度與該函數的複雜度和縮進級別成反比。因此,若是你有一個概念上簡單的函數,它只是一個很長(但很簡單)的case語句,你必須在不少不一樣的狀況下作不少小的事情,那麼函數較長的話也能夠接受。
However, if you have a complex function, and you suspect that a less-than-gifted first-year high-school student might not even understand what the function is all about, you should adhere to the maximum limits all the more closely. Use helper functions with descriptive names (you can ask the compiler to in-line them if you think it's performance-critical, and it will probably do a better job of it than you would have done).
然而,若是你有一個複雜的函數,而且你懷疑一個不太有天賦的高中一年級的學生甚至可能理解不了,那麼你應該更嚴格地遵照最大的函數長度。使用具備描述性名稱的幫助函數(若是你認爲性能很關鍵,可讓編譯器把它們內聯起來,這樣作可能比你作得更好)。
Another measure of the function is the number of local variables. They shouldn't exceed 5-10, or you're doing something wrong. Re-think the function, and split it into smaller pieces. A human brain can generally easily keep track of about 7 different things, anything more and it gets confused. You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now.
函數的另外一個度量就是使用的局部變量的個數。局部變量的個數不能超過5~10個,不然你就是沒作對的事情。從新思考一下函數實現,並將其分割成多個小塊實現。一般人腦能較容易地跟蹤大約7種不一樣的東西,更多的話腦子都會變得混亂起來。儘管你不懷疑本身的聰明程度,但也許你也想知道你兩週前作了些什麼。
In source files, separate functions with one blank line. If the function is exported, the EXPORT macro for it should follow immediately after the closing function brace line. E.g.:
在源文件中,函數之間使用一個空白行進行分隔。若是函數是能夠被外部調用的,那麼EXPORT宏緊隨函數的'}'所在的行。 例如:
int system_is_up(void) { return system_state == SYSTEM_RUNNING; } EXPORT_SYMBOL(system_is_up);
In function prototypes, include parameter names with their data types. Although this is not required by the C language, it is preferred in Linux because it is a simple way to add valuable information for the reader.
在函數原型中,須要包含參數名稱及其數據類型。雖然這不是C語言所要求的,但它在Linux中更受歡迎,由於它是爲讀者添加有價值的信息的一種簡單方式。
7) Centralized exiting of functions | 函數的集中退出
Albeit deprecated by some people, the equivalent of the goto statement is used frequently by compilers in form of the unconditional jump instruction.
儘管有人反對使用goto語句,可是編譯器倒是在以無條件跳轉指令的方式頻繁地使用goto語句的等價形式。
The goto statement comes in handy when a function exits from multiple locations and some common work such as cleanup has to be done. If there is no cleanup needed then just return directly.
當一個函數從多個位置退出時,使用goto語句作一些常見的工做就很方便,好比cleanup。若是不須要清理的話,直接返回便可。
Choose label names which say what the goto does or why the goto exists. An example of a good name could be out_free_buffer: if the goto frees buffer. Avoid using GW-BASIC names like err1: and err2:, as you would have to renumber them if you ever add or remove exit paths, and they make correctness difficult to verify anyway.
選擇合適的標籤名稱,告訴goto語句作什麼或爲何要使用goto語句。若是是釋放緩衝區的話,一個好的標籤名字的例子多是out_free_buffer。咱們要避免使用GW-BASIC的標籤名,諸如err1和err2之類的。由於你可能須要對它們進行從新編號,若是增長或減小一條路徑的話。不管如何,使用err1之類的標籤名使得正確性難以覈實。
The rationale for using gotos is:
使用goto語句的基本原理:
int fun(int a) { int result = 0; char *buffer; buffer = kmalloc(SIZE, GFP_KERNEL); if (!buffer) return -ENOMEM; if (condition1) { while (loop1) { ... } result = 1; goto out_free_buffer; } ... out_free_buffer: kfree(buffer); return result; }
A common type of bug to be aware of is one err bugs which look like this:
一個常見的錯誤處理bug看起來是這樣的:
err: kfree(foo->bar); kfree(foo); return ret;
The bug in this code is that on some exit paths foo is NULL. Normally the fix for this is to split it up into two error labels err_free_bar: and err_free_foo::
在上面的代碼中存在的bug是在某些退出路徑中foo爲空指針。 一般的fix就是將錯誤便籤分割成兩個,err_free_bar和err_free_foo:
err_free_bar: kfree(foo->bar); err_free_foo: kfree(foo); return ret;
Ideally you should simulate errors to test all exit paths.
理想狀況下,你應該模擬錯誤來測試全部的退出路徑。 (P.S. 這彷佛是不可能地:-))
8) Commenting | 註釋
Comments are good, but there is also a danger of over-commenting. NEVER try to explain HOW your code works in a comment: it's much better to write the code so that the working is obvious, and it's a waste of time to explain badly written code.
寫註釋是好的,可是註釋過多就很差了。永遠不要在註釋中解釋你的代碼是如何工做的:寫出一目瞭然的代碼最好不過,試圖解釋寫得很差的代碼簡直就是浪費時間。
Generally, you want your comments to tell WHAT your code does, not HOW. Also, try to avoid putting comments inside a function body: if the function is so complex that you need to separately comment parts of it, you should probably go back to chapter 6 for a while. You can make small comments to note or warn about something particularly clever (or ugly), but try to avoid excess. Instead, put the comments at the head of the function, telling people what it does, and possibly WHY it does it.
一般地,經過註釋告訴別人你的代碼是作什麼的,而不是如何作的。此外,儘可能避免在函數體內插入註釋:若是你的函數太複雜以致於須要單獨寫一打斷註釋的話,那麼你可能須要返回到第6章。能夠對一些特別聰明(或醜陋)的東西作些小的註釋或警告,可是儘可能避免過分地註釋。相反,把註釋放在函數頭,告訴人們該函數作了什麼,以及爲何要這麼作。
When commenting the kernel API functions, please use the kernel-doc format. See the files at Documentation/doc-guide/ and scripts/kernel-doc for details.
當給kernel API函數寫註釋的時候,請使用kernel-doc格式。詳情請參見Documentation/doc-guide/和scripts/kernel-doc。
The preferred style for long (multi-line) comments is:
多行註釋的首選風格是這樣的:
/* * This is the preferred style for multi-line * comments in the Linux kernel source code. * Please use it consistently. * * Description: A column of asterisks on the left side, * with beginning and ending almost-blank lines. */
For files in net/ and drivers/net/ the preferred style for long (multi-line) comments is a little different.
對net/和drivers/net/中的文件,多行註釋的首選風格稍微有點兒不一樣。
/* The preferred comment style for files in net/ and drivers/net * looks like this. * * It is nearly the same as the generally preferred comment style, * but there is no initial almost-blank line. */
It's also important to comment data, whether they are basic types or derived types. To this end, use just one data declaration per line (no commas for multiple data declarations). This leaves you room for a small comment on each item, explaining its use.
給數據寫註釋也是很重要的,不管這些數據是基本類型仍是派生類型。爲此,每一行只聲明一個數據(對於多個數據聲明來講,不使用逗號)。這就爲每一項提供了一個用來解釋其用途而寫短註釋的空間。
9) You've made a mess of it | 搞亂了咋整?(控制縮進的方法)
That's OK, we all do. You've probably been told by your long-time Unix user helper that GNU emacs automatically formats the C sources for you, and you've noticed that yes, it does do that, but the defaults it uses are less than desirable (in fact, they are worse than random typing - an infinite number of monkeys typing into GNU emacs would never make a good program).
沒關係,咱們都搞亂過。你長期使用的Unix用戶幫手可能已經告訴過你,GNU Emacs能自動格式化你的C源代碼,並且你已經注意到它確實也是這樣作了,可是它的默認作法都不理想(事實上,它們比隨機打字更糟。在GNU emacs裏打字的無數只猴子永遠不會寫出一個好的程序)。
So, you can either get rid of GNU emacs, or change it to use saner values. To do the latter, you can stick the following in your .emacs file:
所以,要麼擺脫GNU emacs, 要麼讓GNU emacs使用更加心智健全的值。若是選擇後者,那麼就堅持下面的.emacs文件就行了:
(defun c-lineup-arglist-tabs-only (ignored) "Line up argument lists by tabs, not spaces" (let* ((anchor (c-langelem-pos c-syntactic-element)) (column (c-langelem-2nd-pos c-syntactic-element)) (offset (- (1+ column) anchor)) (steps (floor offset c-basic-offset))) (* (max steps 1) c-basic-offset))) (add-hook 'c-mode-common-hook (lambda () ;; Add kernel style (c-add-style "linux-tabs-only" '("linux" (c-offsets-alist (arglist-cont-nonempty c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only)))))) (add-hook 'c-mode-hook (lambda () (let ((filename (buffer-file-name))) ;; Enable kernel mode for the appropriate files (when (and filename (string-match (expand-file-name "~/src/linux-trees") filename)) (setq indent-tabs-mode t) (setq show-trailing-whitespace t) (c-set-style "linux-tabs-only")))))
This will make emacs go better with the kernel coding style for C files below ~/src/linux-trees.
這將使emacs更好地適應內核編碼風格,在編輯~/src/linux-trees下面的C文件的時候。
But even if you fail in getting emacs to do sane formatting, not everything is lost: use indent.
可是,即便你不能使emacs作心智健全的排版,也不是全部的一切都丟失了:使用縮進。
Now, again, GNU indent has the same brain-dead settings that GNU emacs has, which is why you need to give it a few command line options. However, that's not too bad, because even the makers of GNU indent recognize the authority of K&R (the GNU people aren't evil, they are just severely misguided in this matter), so you just give indent the options -kr -i8 (stands for K&R, 8 character indents), or use scripts/Lindent, which indents in the latest style.
如今不得不重提一下,GNU縮進具備與GNU emacs同樣腦殘的設置,這就是爲何須要給它一些命令行選項。然而,這並非太壞,由於GNU縮進製造者認可K&R的權威(GNU的人並不邪惡,在這個問題上他們只是被嚴重誤導了),因此你只要給出縮進選項
-kr -i8(表明K&R,8字符縮進)就好,或使用採用最新縮進風格的腳本/Lindent。
indent has a lot of options, and especially when it comes to comment re-formatting you may want to take a look at the man page. But remember: indent is not a fix for bad programming.
縮進有不少選項,尤爲是在從新格式化註釋的時候,你可能須要看一下手冊。可是請記住:縮進並非對不良編程的修復。
10) Kconfig configuration files | Kconfig配置文件
For all of the Kconfig* configuration files throughout the source tree, the indentation is somewhat different. Lines under a config definition are indented with one tab, while help text is indented an additional two spaces. Example:
在源代碼樹中,全部的Kconfig配置文件,縮進有一些不一樣。配置定義行用一個tab縮進,而幫助文本在增長額外的兩個空格進行縮進。例如:
config AUDIT bool "Auditing support" depends on NET help Enable auditing infrastructure that can be used with another kernel subsystem, such as SELinux (which requires this for logging of avc messages output). Does not do system-call auditing without CONFIG_AUDITSYSCALL.
Seriously dangerous features (such as write support for certain filesystems) should advertise this prominently in their prompt string:
對那些至關危險的特性(例如對某些文件系統進行寫支持),應該在其提示字符串予以特別強調:
config ADFS_FS_RW bool "ADFS write support (DANGEROUS)" depends on ADFS_FS ...
For full documentation on the configuration files, see the file Documentation/kbuild/kconfig-language.txt.
有關配置文件的完整文檔,請參見Documentation/kbuild/kconfig-language.txt。
11) Data structures | 數據結構
Data structures that have visibility outside the single-threaded environment they are created and destroyed in should always have reference counts. In the kernel, garbage collection doesn't exist (and outside the kernel garbage collection is slow and inefficient), which means that you absolutely have to reference count all your uses.
建立和銷燬在單線程環境以外具備可見性的數據結構老是應該具備引用計數。在內核中,不存在垃圾回收(在內核外,垃圾回收是緩慢和低效的),這意味着你絕對必須使用引用計數。
Reference counting means that you can avoid locking, and allows multiple users to have access to the data structure in parallel - and not having to worry about the structure suddenly going away from under them just because they slept or did something else for a while.
引用計數意味着能夠避免鎖定,並容許多個用戶並行地訪問同一個數據結構,而沒必要擔憂用戶在睡眠了或作了一件別的事情後數據結構會忽然消失掉。
Note that locking is not a replacement for reference counting. Locking is used to keep data structures coherent, while reference counting is a memory management technique. Usually both are needed, and they are not to be confused with each other.
注意鎖不是對引用計數的替代。鎖用於保持數據結構的一致性,而引用計數是一種內存管理技術。一般二者都是須要的,不該該將它們相互混淆。
Many data structures can indeed have two levels of reference counting, when there are users of different classes. The subclass count counts the number of subclass users, and decrements the global count just once when the subclass count goes to zero.
當有不一樣類別的用戶時,事實上許多數據結構能夠有兩級引用計數。子類計數器統計類的用戶數,當子類計數器爲靈時對全局計數器減一。
Examples of this kind of multi-level-reference-counting can be found in memory management (struct mm_struct: mm_users and mm_count), and in filesystem code (struct super_block: s_count and s_active).
使用這種多層次的引用計數的例子能夠內存管理(struct mm_struct:mm_users和mm_count),和文件系統代碼(struct super_block:s_count和s_active)中找到。
Remember: if another thread can find your data structure, and you don't have a reference count on it, you almost certainly have a bug.
記住:若是另外一個線程能夠找到你的數據結構,而你並無一個引用計數,那麼幾乎確定你有一個bug。
12) Macros, Enums and RTL | 宏, 枚舉類型和RTL
Names of macros defining constants and labels in enums are capitalized.
宏定義常量或者枚舉的標籤時使用大寫字母。
#define CONSTANT 0x12345
Enums are preferred when defining several related constants.
當定義多個相關的常量時,首選枚舉類型。
CAPITALIZED macro names are appreciated but macros resembling functions may be named in lower case.
提倡使用大寫的宏名稱,但相似於函數的宏則能夠用小寫。
Generally, inline functions are preferable to macros resembling functions.
通常來講,內聯函數比相似函數的宏更可取。
Macros with multiple statements should be enclosed in a do - while block:
宏定義多行語句時要放入do - while中, 此時宏的名稱用小寫。
#define macrofun(a, b, c) \ do { \ if (a == 5) \ do_this(b, c); \ } while (0)
Things to avoid when using macros:
在使用宏時要避免的事項:
1. macros that affect control flow:
#define FOO(x) \ do { \ if (blah(x) < 0) \ return -EBUGGERED; \ } while (0)
is a very bad idea. It looks like a function call but exits the calling function; don’t break the internal parsers of those who will read the code.
使用影響控制流的宏是很差的。它看起來像一個函數調用,但卻退出函數;不要破壞那些讀取代碼的內部解析器。
2. macros that depend on having a local variable with a magic name:
#define FOO(val) bar(index, val)
might look like a good thing, but it’s confusing as hell when one reads the code and it’s prone to breakage from seemingly innocent changes.
依賴於具備神奇名稱的局部變量的宏貌似不錯,可是當別人讀代碼則會感到困惑,很容易在看似無辜的變化中受挫。
3. macros with arguments that are used as l-values: FOO(x) = y; will bite you if somebody e.g. turns FOO into an inline function.
做爲左值參數的宏:foo(x)= Y; 會刺痛你,若是有人將FOO改爲內聯函數的話。
4. forgetting about precedence: macros defining constants using expressions must enclose the expression in parentheses. Beware of similar issues with macros using parameters.
忘掉優先級吧:使用表達式定義常量的宏必須用括號把表達式括起來。使用參數的宏也存在相似的問題。
#define CONSTANT 0x4000 #define CONSTEXP (CONSTANT | 3)
5. namespace collisions when defining local variables in macros resembling functions:
#define FOO(x) \ ({ \ typeof(x) ret; \ ret = calc_ret(x); \ (ret); \ })
ret is a common name for a local variable - __foo_ret is less likely to collide with an existing variable.
在定義一個相似函數的宏的時候可能存在命名空間衝突。 ret是一個給局部變量命名很常見的名字,那麼__foo_ret跟存在的局部變量衝突的可能性就極小了。
The cpp manual deals with macros exhaustively. The gcc internals manual also covers RTL which is used frequently with assembly language in the kernel.
cpp手冊對宏講得很詳盡。gcc內部手冊還涵蓋了RTL, RTL被頻繁地和內核編程中使用到的彙編語言一塊兒使用。
13) Printing kernel messages | 打印內核消息
Kernel developers like to be seen as literate. Do mind the spelling of kernel messages to make a good impression. Do not use crippled words like dont; use do not or don't instead. Make the messages concise, clear, and unambiguous.
內核開發人員喜歡被看做是有文化的人。請注意內核消息的拼寫,給人留下一個好印象。不要使用殘缺不全的詞例如don't,要使用do not或don't。內核消息的書寫要點是簡潔、明瞭、不模棱兩可。
Kernel messages do not have to be terminated with a period.
內核消息沒必要以句號終止。
Printing numbers in parentheses (%d) adds no value and should be avoided.
在括號內的打印數字(%d)沒有意義,應當予以免。
There are a number of driver model diagnostic macros in <linux/device.h> which you should use to make sure messages are matched to the right device and driver, and are tagged with the right level: dev_err(), dev_warn(), dev_info(), and so forth. For messages that aren’t associated with a particular device, <linux/printk.h> defines pr_notice(), pr_info(), pr_warn(), pr_err(), etc.
在<linux/device.h>中存在着一些設備驅動模型診斷宏。應當使用它們以確保內核消息被匹配到正確的設備及驅動程序上,並貼上正確的標籤。這些宏是: dev_err(), dev_warn(), dev_info()等等。對於那些不關聯到任何特定設備的內核消息, <linux/printk.h>中定義了一些宏供使用,這些宏是:pr_notice(), pr_info(), pr_warn()等等。
Coming up with good debugging messages can be quite a challenge; and once you have them, they can be a huge help for remote troubleshooting. However debug message printing is handled differently than printing other non-debug messages. While the other pr_XXX() functions print unconditionally, pr_debug() does not; it is compiled out by default, unless either DEBUG is defined or CONFIG_DYNAMIC_DEBUG is set. That is true for dev_dbg() also, and a related convention uses VERBOSE_DEBUG to add dev_vdbg() messages to the ones already enabled by DEBUG.
打印出好的調試消息是一個很大的挑戰;一旦擁有了它們,就能夠對遠程故障排除提供巨大的幫助。然而,打印調試消息不一樣於打印其餘非調試消息。pr_XXX()之類的函數將無條件打印內核消息,pr_debug()則否則。默認狀況下是不編譯調試信息的,除非DEBUG被定義或者設置了CONFIG_DYNAMIC_DEBUG。對dev_dbg()也是這樣處理的,相應的約定是使用VERBOSE_DEBUG,將dev_vdebug()打印的消息增長到已經啓用了DEBUG的調試消息中。
Many subsystems have Kconfig debug options to turn on -DDEBUG in the corresponding Makefile; in other cases specific files #define DEBUG. And when a debug message should be unconditionally printed, such as if it is already inside a debug-related #ifdef section, printk(KERN_DEBUG ...) can be used.
許多內核子系統有Kconfig調試選項,用來在相應的Makefile中打開-DDEBUG;在其餘狀況下,則是使用#DEBUG。當調試消息應當被無條件打印時,例如:若是它已經位於調試相關的#ifdef部分時,可使用printk(KERN_DEBUG ...)。
14) Allocating memory | 內存分配
The kernel provides the following general purpose memory allocators: kmalloc(), kzalloc(), kmalloc_array(), kcalloc(), vmalloc(), and vzalloc(). Please refer to the API documentation for further information about them.
內核提供了幾個通用的內存分配器:kmalloc(),kzalloc(),kmalloc_array(),kcalloc(),vmalloc(),和vzalloc()。如欲獲取進一步的信息,請參閱相應的API文檔。
The preferred form for passing a size of a struct is the following:
傳遞一個結構體大小的首選形式以下:
p = kmalloc(sizeof(*p), ...);
The alternative form where struct name is spelled out hurts readability and introduces an opportunity for a bug when the pointer variable type is changed but the corresponding sizeof that is passed to a memory allocator is not.
另外一種可供選擇的形式是拼寫出結構體名稱。但這種形式傷害了可讀性,並且引入了一個出bug的機會,在當指針變量類型改變了而對應的傳遞給內存分配器的尺寸並無改變的時候。
Casting the return value which is a void pointer is redundant. The conversion from void pointer to any other pointer type is guaranteed by the C programming language.
強制轉換一個空指針的返回值是多餘的。C語言自己就可以保證將從空指針轉換爲任何其餘類型的指針。
The preferred form for allocating an array is the following:
分配一個數組的首選形式是這樣的:
p = kmalloc_array(n, sizeof(...), ...);
The preferred form for allocating a zeroed array is the following:
分配一個數組並將數組初始化爲零的首選形式是這樣的:
p = kcalloc(n, sizeof(...), ...);
Both forms check for overflow on the allocation size n * sizeof(...), and return NULL if that occurred.
這兩種分配形式都會檢查內存溢出,在分配尺寸爲n * sizeof(...)的時候,若是發生溢出,則返回NULL。
15) The inline disease | 內聯的弊端
There appears to be a common misperception that gcc has a magic 「make me faster」 speedup option called inline. While the use of inlines can be appropriate (for example as a means of replacing macros, see Chapter 12), it very often is not. Abundant use of the inline keyword leads to a much bigger kernel, which in turn slows the system as a whole down, due to a bigger icache footprint for the CPU and simply because there is less memory available for the pagecache. Just think about it; a pagecache miss causes a disk seek, which easily takes 5 milliseconds. There are a LOT of cpu cycles that can go into these 5 milliseconds.
彷佛存在一個廣泛的誤解,那就是gcc有一個魔法"讓我跑得更快"的加速選項,該魔法稱之爲內聯。而有時候使用內聯函數是合適的(例如替換宏,方法見12章),但每每是不那麼恰當的。大量使用inline關鍵字會致使內核變得更大,從而減緩了系統做爲一個總體的運行效率,由於用來作內存頁緩存的內存減小了,致使對CPU指令作緩存的足跡變得更大了。想一想吧,一個內存頁面緩存未命中,致使一次磁盤尋道,很容易就消耗掉5毫秒。那麼在5毫秒裏,捲入了不少CPU週期。
A reasonable rule of thumb is to not put inline at functions that have more than 3 lines of code in them. An exception to this rule are the cases where a parameter is known to be a compiletime constant, and as a result of this constantness you know the compiler will be able to optimize most of your function away at compile time. For a good example of this later case, see the kmalloc() inline function.
一個合理的經驗法則是不要將在3行以上的代碼內聯到函數中。這個規則的一個例外是其中一個參數是編譯時常數。因爲這種不變性,編譯器在編譯時會優化你的大部分函數。對於後一種狀況,一個很好的例子就是kmalloc()內聯函數。
Often people argue that adding inline to functions that are static and used only once is always a win since there is no space tradeoff. While this is technically correct, gcc is capable of inlining these automatically without help, and the maintenance issue of removing the inline when a second user appears outweighs the potential value of the hint that tells gcc to do something it would have done anyway.
一般人們認爲,添加內聯到那些只使用一次的靜態函數老是無可爭議的,由於沒有空間折衷。雖然這在技術上是正確的,gcc可以自動內聯而無需幫助。但在移除內聯的維護問題上,當第二個用戶出現時,意識不到gcc提供的潛在的價值(gcc會作一些原本就會作的事情)。(P.S. 不是很理解這句話,因此估摸翻譯一下,Orz...)
16) Function return values and names | 函數返回值和名稱
Functions can return values of many different kinds, and one of the most common is a value indicating whether the function succeeded or failed. Such a value can be represented as an error-code integer (-Exxx = failure, 0 = success) or a succeeded boolean (0 = failure, non-zero = success).
函數能夠返回多種不一樣類型的值,其中最多見的一個就是表示函數是否調用成功的值。這樣的值能夠表示爲一個錯誤代碼整數(-Exxx=失敗,0=成功或一個布爾值(零=失敗,非零=成功)。
Mixing up these two sorts of representations is a fertile source of difficult-to-find bugs. If the C language included a strong distinction between integers and booleans then the compiler would find these mistakes for us... but it doesn’t. To help prevent such bugs, always follow this convention:
混合這兩種返回值表示法將致使很是難以定位的bug的出現。若是C語言支持對整數和布爾值進行強有力的區分的話,則編譯器會爲咱們找到這些錯誤之間的最大的區別,可是,沒有。爲了防止這種錯誤出現,請老是遵循這一約定:
If the name of a function is an action or an imperative command, the function should return an error-code integer. If the name is a predicate, the function should return a "succeeded" boolean.
For example, add work is a command, and the add_work() function returns 0 for success or -EBUSY for failure. In the same way, PCI device present is a predicate, and the pci_dev_present() function returns 1 if it succeeds in finding a matching device or 0 if it doesn’t.
例如: add work是一條命令,函數add_work()在執行成功時返回0,失敗時則返回-EBUSY。一樣地,PCI dev present是一個謂詞,函數pci_dev_present()返回1表示成功找到了相匹配的設備,不然返回0。
All EXPORTed functions must respect this convention, and so should all public functions. Private (static) functions need not, but it is recommended that they do.
全部導出函數都必須遵照此約定,全部公共函數也應遵照此約定。雖然私有(靜態)函數不須要遵循此約定,但建議也這麼作。
Functions whose return value is the actual result of a computation, rather than an indication of whether the computation succeeded, are not subject to this rule. Generally they indicate failure by returning some out-of-range result. Typical examples would be functions that return pointers; they use NULL or the ERR_PTR mechanism to report failure.
當函數的返回值是計算的實際結果,而不是計算是否成功的指示時,不受該規則的約束。一般地,它們經過返回一些超出範圍的結果表示失敗。典型的例子就是函數返回指針,使用NULL或ERR_PTR機制報告失敗。
17) Don't re-invent the kernel macros | 不要從新發明內核宏
The header file include/linux/kernel.h contains a number of macros that you should use, rather than explicitly coding some variant of them yourself. For example, if you need to calculate the length of an array, take advantage of the macro
在頭文件include/linux/kernel.h中,包含了一系列的宏,應當使用那些宏,而不是顯式地再寫一些那些宏的變體。例如,若是你想計算數組長度,用下面的宏就好
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
Similarly, if you need to calculate the size of some structure member, use
相似地,若是你想計算某個結構體成員的尺寸,使用下面的宏
#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))
There are also min() and max() macros that do strict type checking if you need them. Feel free to peruse that header file to see what else is already defined that you shouldn’t reproduce in your code.
若是你須要的話,作了嚴格的類型的檢查的宏min()和max()也是可供使用的。隨時閱讀內核頭文件,看看別人已經定義好了的宏,不要在你的代碼中重複發明宏。
18) Editor modelines and other cruft | 編輯器模式行和其餘
Some editors can interpret configuration information embedded in source files, indicated with special markers. For example, emacs interprets lines marked like this:
一些編輯器能夠解釋嵌入在源文件中的配置信息,用特殊標記表示。例如,emacs這樣解釋標記:
-*- mode: c -*-
Or like this:
或像這樣:
/* Local Variables: compile-command: "gcc -DMAGIC_DEBUG_FLAG foo.c" End: */
Vim interprets markers that look like this:
Vim解釋標記則是這樣的:
/* vim:set sw=8 noet */
Do not include any of these in source files. People have their own personal editor configurations, and your source files should not override them. This includes markers for indentation and mode configuration. People may use their own custom mode, or may have some other magic method for making indentation work correctly.
不要在源文件中包含任何這些東東。每一個人都有本身的編輯器配置,而且你的源文件不該該覆蓋它們。這包括縮進標記和模式配置。人們可使用他們本身的自定義模式,或者有一些其餘的神奇方法來保證縮進是正確的。
19) Inline assembly | 內聯彙編
In architecture-specific code, you may need to use inline assembly to interface with CPU or platform functionality. Don't hesitate to do so when necessary. However, don't use inline assembly gratuitously when C can do the job. You can and should poke hardware from C when possible.
在與特定架構相關的代碼中,可能須要使用內聯彙編來與CPU或平臺功能進行交互。若有必要,不要猶豫。可是,不要使用內聯彙編來作C語言能夠作的工做。只要能用C語言操做硬件,就必須使用C語言。
Consider writing simple helper functions that wrap common bits of inline assembly, rather than repeatedly writing them with slight variations. Remember that inline assembly can use C parameters.
編寫簡單的助手函數,這些函數封裝了內聯彙編,而不是重複地編寫他們,在有輕微的變化的時候。請記住,內聯彙編可使用C語言的參數。
Large, non-trivial assembly functions should go in .S files, with corresponding C prototypes defined in C header files. The C prototypes for assembly functions should use asmlinkage.
大型的、不是不重要的彙編函數應該寫在.S文件中,而在C頭文件中定義相應的C函數原型。爲彙編函數寫的C函數原型應當使用asmlinkage。
You may need to mark your asm statement as volatile, to prevent GCC from removing it if GCC doesn't notice any side effects. You don't always need to do so, though, and doing so unnecessarily can limit optimization.
可能須要將彙編語句標記爲volatile,以防止被gcc刪除,若是gcc沒有注意到任何反作用的話。然而,你並不老是須要這樣作,由於當這麼作是沒必要要的時候作了,會限制代碼優化。
When writing a single inline assembly statement containing multiple instructions, put each instruction on a separate line in a separate quoted string, and end each string except the last with \n\t to properly indent the next instruction in the assembly output:
在編寫包含多個指令的單個內聯彙編語句時,將每一個指令置於單獨引用的字符串中的單獨行中,除最後一個字符串外每一個字符串以\n\t結尾。同時,在彙編代碼輸出中正確地縮進下一條指令:
asm ("magic %reg1, #42\n\t" "more_magic %reg2, %reg3" : /* outputs */ : /* inputs */ : /* clobbers */);
20) Conditional Compilation | 條件編譯
Wherever possible, don't use preprocessor conditionals (#if, #ifdef) in .c files; doing so makes code harder to read and logic harder to follow. Instead, use such conditionals in a header file defining functions for use in those .c files, providing no-op stub versions in the #else case, and then call those functions unconditionally from .c files. The compiler will avoid generating any code for the stub calls, producing identical results, but the logic will remain easy to follow.
在C源文件中,儘量地不要使用預編譯語句(#if, #ifdef);這樣作將使代碼難以閱讀,且在邏輯上難以遵循。相反地,在頭文件中定義.c文件裏的函數中須要使用到的那些條件,在#else分支中提供空操做的票根(菸頭)版本。那麼接下來無條件地調用.c文件裏的那些函數。這樣編譯器對那些票根調用不產生任何代碼,提供一樣的結果,可是在邏輯上就至關容易遵循了。
Prefer to compile out entire functions, rather than portions of functions or portions of expressions. Rather than putting an ifdef in an expression, factor out part or all of the expression into a separate helper function and apply the conditional to that function.
寧願編譯整個函數,也不要編譯函數的一部分或表達式的一部分。不該該在一個表達式中使用#ifdef,應當分解出部分表達式或所有表達式,將他們裝入一個單獨的輔助功能函數,而後對那個函數應用條件編譯。
If you have a function or variable which may potentially go unused in a particular configuration, and the compiler would warn about its definition going unused, mark the definition as __maybe_unused rather than wrapping it in a preprocessor conditional. (However, if a function or variable always goes unused, delete it.)
若是你有一個函數或變量可能會在一個特定的配置不被使用,並且編譯器會警告那個定義將未使用,將那個定義標記成__maybe_unused而不是包裝到一個預處理條件中去。(可是,若是函數或變量老是未被使用,則絕不留情地予以刪除之。)
Within code, where possible, use the IS_ENABLED macro to convert a Kconfig symbol into a C boolean expression, and use it in a normal C conditional:
在代碼中,只要有可能,請使用IS_ENABLED宏將Kconfig符號轉換成C的布爾表達式,而且在一個正常的條件語句中予以使用:
if (IS_ENABLED(CONFIG_SOMETHING)) { ... }
The compiler will constant-fold the conditional away, and include or exclude the block of code just as with an #ifdef, so this will not add any runtime overhead. However, this approach still allows the C compiler to see the code inside the block, and check it for correctness (syntax, types, symbol references, etc). Thus, you still have to use an #ifdef if the code inside the block references symbols that will not exist if the condition is not met.
編譯器可以摺疊條件,經過#ifdef, 包括或排除代碼塊,因此這不會增長任何運行時的開銷。可是,這一方法仍然容許C編譯器看到塊內的代碼,並檢查其正確性(語法、類型、符號引用等)。所以,你仍是要使用#ifdef,在若是條件不知足則塊引用符號不存在的狀況下。
At the end of any non-trivial #if or #ifdef block (more than a few lines), place a comment after the #endif on the same line, noting the conditional expression used. For instance:
在任何#if或#ifdef塊(多行)的末尾,請在同一行的#endif加個註釋,註明使用的條件表達式。例如:
#ifdef CONFIG_SOMETHING ... #endif /* CONFIG_SOMETHING */
Appendix I) References | 附錄I 參考資料
The C Programming Language, Second Edition by Brian W. Kernighan and Dennis M. Ritchie. Prentice Hall, Inc., 1988. ISBN 0-13-110362-8 (paperback), 0-13-110370-9 (hardback).
The Practice of Programming by Brian W. Kernighan and Rob Pike. Addison-Wesley, Inc., 1999. ISBN 0-201-61586-X.
GNU manuals - where in compliance with K&R and this text - for cpp, gcc, gcc internals and indent, all available from http://www.gnu.org/manual/
WG14 is the international standardization working group for the programming language C, URL: http://www.open-std.org/JTC1/SC22/WG14/
Kernel process/coding-style.rst, by greg@kroah.com at OLS 2002: http://www.kroah.com/linux/talks/ols_2002_kernel_codingstyle_talk/html/
Nothing can be accomplished without norms or standards.