jump to navigation

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檔裡,特別是當這個函式的定義不斷改變。忘記修改這個原型是非常容易的事,而且一個不相容的原型比沒用的原型還糟。

 

Advertisements

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* 會導致錯誤或疑惑

c-faq review and a chinese translation (1.4) January 24, 2007

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

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

1.4 What should the 64-bit type be on a machine that can support it?

The new C99 Standard specifies type long long as effectively being at least 64 bits, and this type has been implemented by a number of compilers for some time. (Others have implemented extensions such as __longlong.) On the other hand, it’s also appropriate to implement type short int as 16, int as 32, and long int as 64 bits, and some compilers do.See also questions 1.3 and 18.15d.

Additional links: Part of a proposal for long long for C9X by Alan Watson and Jutta Degener, succinctly outlining the arguments.

References: C9X Sec. 5.2.4.2.1, Sec. 6.1.2.5

為什麼機器應該要可以支援 64 位元的型態

新的C99標準定義 long long 為有效的 (至少) 64 位元,而這個型態也已被很多編譯器所包含。(其他的則實作延伸型態如 __longlong) 另一方面來說,實作 short int 16 bitsint 32 bitslong int 64 bits也比較適合,而某些編譯器也的確這麼做。

c-faq review and a chinese translation (1.3) January 24, 2007

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

1.3 Since C doesn’t define sizes exactly, I’ve been using typedefs like int16 and int32. I can then define these typedefs to be int, short, long, etc. depending on what machine I’m using. That should solve everything, right?

If you truly need control over exact type sizes, this is the right approach. There remain several things to be aware of:

  • There might not be an exact match on some machines. (There are, for example, 36-bit machines.)
  • A typedef like int16 or int32 accomplishes nothing if its intended meaning is “at least” the specified size, because types int and long are already essentially defined as being “at least 16 bits” and “at least 32 bits,” respectively.
  • Typedefs will never do anything about byte order problems (e.g. if you’re trying to interchange data or conform to externally-imposed storage layouts).
  • You no longer have to define your own typedefs, because the Standard header <inttypes.h> contains a complete set.

See also questions 10.16 and 20.5.

既然 C 語言不明確的定義資料型態的大小,我已經用 typedefs 例如 int16 int32。於是我可以依據我所使用的機器不同,來定義這些 typedefs int, short long …etc. 這樣應該可以解決問題,不是嗎?

如果你非常需要控制確切的資料型態大小,這是個正確的方法。然而還是有些該注意的事:

Ÿ 某些機器上或許沒有精確符合的大小,如:36-bit machine

Ÿ 如果目的是去定義該型態的最小 bit 數。那麼 typedefs int16 int32 並不能幫上什麼忙,因為資料型態 int long 基本上已經被定義為「至少16 bits」和「至少 32 bits

Ÿ Typedefs並不能處理 byte order 的問題 (e.g. 如果你試圖要和外掛的儲存結構交換資料,或使與其一致) (按:似乎指的是 big-endian little-endian)

Ÿ 你不再需要去定義你自己的typedefs,因為標準的表頭檔 (inttypes.h) 已經包含所有的集合。