jump to navigation

c-faq review and a chinese translation (1.10) February 2, 2007

Posted by TSAI HONG-BIN in Programming.
add a comment

plz refer to http://c-faq.com/decl/static.html

1.10 Do all declarations for the same static function or variable have to include the storage class static?


The language in the Standard does not quite require this (what’s most important is that the first declaration contain static), but the rules are rather intricate, and are slightly different for functions than for data objects. (There has also been a lot of historical variation in this area.) Therefore, it’s safest if static appears consistently in the definition and all declarations.Additional links: An article by Jutta Degener explaining the subtly different rules for static variables versus static functions.References: ISO Sec. 6.1.2.2
Rationale Sec. 3.1.2.2
H&S Sec. 4.3 p. 75

所有static 的函式或變數都必須在宣告時加上儲存類別 static 嗎?(按:storage class: auto, register (變數存在 register 而非 ram), static (compile time即完成初始化,只能在本地域使用), extern (變數在其他源碼裡定義), typedef)

標準的語言規範裡並沒有要求,但最重要的是第一個宣告必須含static,但實際運作上的規則有點複雜,而且對於函式和資料物件(按:指變數)的處理有所不同。(還包括一些歷史上的變化) 因此,最安全的方法就是一致地讓 static 同時出現在宣告及定義的部份。

補充 Jutta Degener 解釋static變數和static函式的不同:

Example:

/* object */ /* function */

case 1 –>

int o1; int f1(); /* external linkage */
static int o1; static int f1(); /* ERROR, both have external linkage */

case 2 –>

static int o2; static int f2(); /* internal linkage */
int o2; /* ERROR, o2 has internal linkage */
int f2(); /* OK, picks up internal linkage */

case 3 –>

static int o3; static int f3(); /* internal linkage */
extern int o3; extern int f3(); /* OK, both pick up internal linkage */

The difference is case (2); where functions do pick up a previous linkage even without “extern”, objects don’t.

Advertisements

c-faq review and a chinese translation (1.9) February 1, 2007

Posted by TSAI HONG-BIN in Programming.
add a comment

plz refer to http://c-faq.com/decl/semiglobal.html

1.9 How can I make a sort of “semi-global” variable, that is, one that’s private to a few functions spread across a few source files?

You can’t do this in C. If it’s impossible or inconvenient to put all the functions in the same source file, there are two usual solutions:

  1. Pick a unique prefix for the names of all functions and global variables in a library or package of related routines, and warn users of the package not to define or use any symbols with names matching that prefix other than those documented as being for public consumption. (In other words, an undocumented but otherwise global symbol with a name matching that prefix is, by convention, “private.”)
  2. Use a name beginning with an underscore, since such names shouldn’t be used by ordinary code. (See question 1.29 for more information, and for a description of the “no man’s land” between the user and implementation namespaces.)

It may also be possible to use special linker invocations to adjust the visibility of names, but any such techniques are outside of the scope of the C language.

1.9 我如何可以製造一個「半全域」的變數,也就是說,一個對少數函式而言屬於隱域,卻可以在程式碼之間傳遞的變數?

你無法在c語言裡這麼做。同時,也不可能將所有的函式放在同一個源碼檔裡頭,也不會比較方便。不過有兩個常用的解法:

1. 替你所包裝的函式庫裡的函式或變數取一個特別的前綴(prefix),並警告使用者不要定義或使用和該前綴相同的名字,已標明為public的除外。(也就是說,沒有標明public的全域變數,且名字和前綴相符者就是private)

2. 名字前加上下畫線(underscore) (按: __VAR),但這種命名不應使用在一般的程式碼裡。(請參考問題1.29)

也可以使用特別的連結器來調整變數名稱的可見度,但這種功能是屬於C語言的範疇之外的。

c-faq review and a chinese translation (1.7) January 26, 2007

Posted by TSAI HONG-BIN in Programming.
add a comment

please refer to http://c-faq.com/decl/decldef.html

1.7 What’s the best way to declare and define global variables and functions?

 

First, though there can be many declarations (and in many translation units) of a single global variable or function, there must be exactly one definition. (Strictly speaking, it is also possible to have zero definitions, since it is permissible to have external declarations, without any matching definitions, for functions or variables which are never used.) For global variables, the definition is the declaration that actually allocates space, and provides an initialization value, if any. For functions, the definition is the “declaration” that provides the function body. For example, these are declarations:

extern int i;

extern int f();

and these are definitions:

int i = 0;

int f(){
return 1;
}

(Actually, the keyword extern is optional in function declarations; see question 1.11.)

 

When you need to share variables or functions across several source files, you will of course want to ensure that all definitions and declarations are consistent. The best arrangement is to place each definition in some relevant .c file. Then, put an external declaration in a header (“.h”) file, and #include it wherever the declaration is needed. The .c file containing the definition should also #include the same header file, so the compiler can check that the definition matches the declarations.

 

This rule promotes a high degree of portability: it is consistent with the requirements of the ANSI C Standard, and is also consistent with most pre-ANSI compilers and linkers. (Unix compilers and linkers typically use a “common model” which allows multiple definitions, as long as at most one is initialized; this behavior is mentioned as a “common extension” by the ANSI Standard, no pun intended. A few very old systems might once have required an explicit initializer to distinguish a definition from an external declaration.)

 

It is possible to use preprocessor tricks to arrange that a line like

DEFINE(int, i);

need only be entered once in one header file, and turned into a definition or a declaration depending on the setting of some macro, but it’s not clear if this is worth the trouble, especially since it’s usually a better idea to keep global variables to a minimum.

 

It’s not just a good idea to put global declarations in header files: if you want the compiler to be able to catch inconsistent declarations for you, you must place them in header files. In particular, never place a prototype for an external function in a .c file–if the definition of the function ever changes, it would be too easy to forget to change the prototype, and an incompatible prototype is worse than useless.

See also questions 1.24, 10.6, 17.2, and 18.8.

References: K&R1 Sec. 4.5 pp. 76-7
K&R2 Sec. 4.4 pp. 80-1
ISO Sec. 6.1.2.2, Sec. 6.7, Sec. 6.7.2, Sec. G.5.11
Rationale Sec. 3.1.2.2
H&S Sec. 4.8 pp. 101-104, Sec. 9.2.3 p. 267
CT&P Sec. 4.2 pp. 54-56

 

宣告及定義全域變數和函式最好的方法是什麼?

首先,雖然一個全域變數或函式可以宣告多次,但只能有一個定義。(註腳:嚴格說來,沒有定義也是有可能的。因為對於從未使用的變數或函式來說,在外部的宣告時沒有提供相對應的定義是被允許的。) 對全域變數來說,其定義指的是實際在配置空間,並提供初始值的宣告部份。至於函式,其定義即為提供函式內容的宣告部份。舉例來說,以下是宣告:

extern int I;

extern int f();

而以下即是定義:

int i = 0;

int f( ){
return 1;

}

事實上,extern 在函式宣告時是選擇性的。

若你需要在數個不同的原始碼檔案間分享變數或函式,你要確定所有的宣告和定義一致。最好的方法是把定義放在某個固定的 .c 檔裡,然後把外部宣告放進表頭檔,當需要使用該變數時 #include 進來。那個包含定義的 .c 檔應該也 #include 同一個表頭檔,如此編譯器可以檢查定義和宣告相符。

 

這個原則是提供高度的可攜性,不僅和ANSI C的標準一致,同時和大部份 pre-ANSI 的編譯器和連結器一致。 (UNIX 下的編譯器和連結器基本上是使用”common model”,使允許(對全域變數)具多重定義,但最多只有一個可以被初始化。這個動作在ANSI 標準裡被稱為”common extension”。一些非常舊的系統可能要求明確的初始器來從外部宣告中分辨出確切的定義)

 

使用預先處理的手法來安排像

DEFINE(int, i);

的程式碼是可行的,只需要在表頭檔輸入一次,即成為根據某個巨集所設計好的定義或宣告,但這樣做值不值得,會不會產生問題還待考慮,尤其是,普遍認為全域變數越少越好。

 

將全域的宣告放在表頭檔並不只是好主意而已:如果你希望編譯器能為你截取到不一致的宣告,你必須將它們放在表頭檔。特別是,不要將某個外部函式的原型 (按:指的是函式的定義部份) 放在一個.c檔裡,特別是當這個函式的定義不斷改變。忘記修改這個原型是非常容易的事,而且一個不相容的原型比沒用的原型還糟。

 

c-faq review and a chinese translation (1.6) January 25, 2007

Posted by TSAI HONG-BIN in Programming.
add a comment

please refer to http://c-faq.com/decl/mimic.html

1.6 I’m trying to declare a pointer and allocate some space for it, but it’s not working. What’s wrong with this code?

char *p;

*p = malloc(10);

The pointer you declared is p, not *p. See question 4.2.

我試著宣告一個指標並且配置一些空間給它,但失敗了。我這樣寫有什麼問題嗎?

char *p;
*p = malloc(10);

你所宣告的指標是 p 不是 *p,請見 section 4.2

c-faq review and a chinese translation (1.5) January 25, 2007

Posted by TSAI HONG-BIN in Programming.
1 comment so far

please refer to http://c-faq.com/decl/charstarws.html

1.5 What’s wrong with this declaration?

char* p1, p2;

I get errors when I try to use p2.

Nothing is wrong with the declaration–except that it doesn’t do what you probably want. The * in a pointer declaration is not part of the base type; it is part of the declarator containing the name being declared (see question 1.21). That is, in C, the syntax and interpretation of a declaration is not really

type identifier ;

but rather

base_type thing_that_gives_base_type ;

where “thing_that_gives_base_type”–the declarator–is either a simple identifier, or a notation like *p or a[10] or f() indicating that the variable being declared is a pointer to, array of, or function returning that base_type. (Of course, more complicated declarators are possible as well.)

 

In the declaration as written in the question, no matter what the whitespace suggests, the base type is char and the first declarator is “* p1”, and since the declarator contains a *, it declares p1 as a pointer-to-char. The declarator for p2, however, contains nothing but p2, so p2 is declared as a plain char, probably not what was intended. To declare two pointers within the same declaration, use

char *p1, *p2;

Since the * is part of the declarator, it’s best to use whitespace as shown; writing char* invites mistakes and confusion.

See also question 1.13.

Additional links: Bjarne Stroustrup’s opinion

 

 

這樣宣告有什麼錯?

char* p1, p2;

為什麼我在使用 p2 時收到錯誤訊息

 

 

宣告的部份沒有錯,除了這樣宣告可能不會如你所想像的樣子運作。 * 用意在宣告指標,它不是 base type (按:指int, char, bool, …etc.),而是包含欲宣告的型態名字 (按:指 p1, p2) declarator。也就是說,在C語言裡,對於宣告的語法和解讀不是單單

type identifier;

而是

base_type thing_that_gives_base_type;

這裡的 “thing_that_gives_base_type”若不是單純的識別符 (identifier) ,就是類似 *p, a[10], f() (按:指標、陣列和函式) 的註記,用來指出欲宣告的變數,會指向base_type型態、是一個base_type型態的陣列或base_type型態的函式回傳值。(當然,也可能有更複雜的declarators)

在問題裡的宣告,不論看到幾個空格, base type char 而第一個 declaratory “* p1”,既然declarator 包含 *,它會將p1宣告為char的指標 (按:指向一個char 型態的資料),而p2沒有declarator,它被宣告為單純的char型態,或許不是提問人的原意。要在一行裡宣告兩個指標,應該寫為

char *p1, *p2;

既然 * declarator 的一部份,使用時的空格也最好如上所示(按:跟在變數的前面),寫成 char* 會導致錯誤或疑惑