聲明與定義(Declaration and Definition)
html
開始這篇文章以前,咱們先弄懂變量的declaration和definition的區別,即變量的聲明和定義的區別。數組
通常狀況下,咱們這樣簡單的分辨聲明與定義的區別:創建存儲空間的聲明稱之爲「定義」,而把不須要創建存儲空間的稱之爲「聲明」。electron
其實更爲準確地描述的話,變量的聲明能夠分爲兩種狀況:ide
(1)一種是須要創建存儲空間的。例如:int a;在聲明的時候就已經創建了存儲空間。這種聲明是定義性聲明(defining declaration)。即咱們平時所說的「定義」;函數
(2)另外一種是不須要創建存儲空間的,只是告訴編譯器某變量已經在別處定義過了。例如:extern int a;其中,變量a是在別處定義的。這種聲明是引用性聲明(referning declaration)。即咱們平時所說的「聲明」。工具
因此,從廣義的角度來講,聲明中包含着定義,可是並不是全部的聲明都是定義。即,定義性聲明既是定義又是聲明,而引用性聲明只是聲明。例如,int a;它既是定義又是聲明,而extern int a;就只是聲明而不是定義。再來看具體的例子:佈局
int a; // 定義性聲明,分配存儲空間,初值不肯定 flex
int b = 0; // 定義性聲明,分配存儲空間,賦初值 ui
extern int c; // 引用性聲明,不分配存儲空間,只是告訴編譯器變量c在別處分配過了 spa
C語言類型(C Types)
C語言將類型分爲三類(C99 6.2.5):
Types are partitioned into object types(types that fully describe objects), function types(types that describe functions), and incomplete types(types that describe objects but lack information needed to determine their sizes).
(1)對象類型(object types):對象的大小(size)、內存佈局(layout)和對齊方式(alignment requirement)都很清楚的對象。
(2)不完整類型(incomplete types):與對象類型相反,包括那些類型信息不完整的對象類型(incompletely-defined object type)以及空類型(void)。
(3)函數類型(function types):這個很好理解,描述函數的類型 -- 描述函數的返回值和參數狀況。
這裏咱們詳細瞭解下不完整類型。先看哪些狀況下一個類型是不完整類型:
(1)具體的成員還沒定義的結構體(共用體)
(2)沒有指定維度的數組
(3)void類型(it is an incomplete type that cannot be completed)
sizeof操做符不能夠用於不完整類型,也不能夠定義不完整類型的數組
爲何要有不完整類型
或者說不完整類型有哪些做用,C裏爲何要有不完整類型?
能夠這麼說,C的不完整類型是提供給C實現封裝抽象的惟一工具(這樣說,不知道是否是有些武斷,歡迎批評指正哈)。
舉個例子,咱們能夠在list.c中定義
struct __list {
struct __list *prev;
struct __list *next;
viud *data;
};
在list.h中這樣:
typedef struct __list *list_t;
這樣的話,鏈表結構的具體定義對用戶來講就是透明的了,不能直接的訪問結構成員,只能提供相應的接口來供訪問,這樣作的好處顯而易見,能夠防止用戶隨意破壞模塊內部的抽象數據類型。
不完整類型的缺點
(1)使用不完整類型的話,咱們也就只能使用指向該不完整類型的指針了,由於指針類型是平臺相關的,即在特定的平臺上指針變量的大小是已知的。
(2)在不完整類型尚未完整以前,sizeof操做符是獲取不了該類型的大小的。
(3)頭文件中咱們也是不能夠使用inline函數的,由於類型是不完整的,在inline函數中若是訪問成員的話,編譯器會報錯。
前置聲明(forward declaration)
維基百科上的定義是:
In computer programming, a forward declaration is a declaration of an identifier (denoting an entity such as a type, a variable, or a function) for which the programmer has not yet given a complete definition. It is required for a compiler to know the type of an identifier (size for memory allocation, type for type checking, such as signature of functions), but not a particular value it holds (in case of variables) or definition (in the case of functions), and is useful for one-pass compilers. Forward declaration is used in languages that require declaration before use; it is necessary for mutual recursion in such languages, as it is impossible to define these functions without a forward reference in one definition. It is also useful to allow flexible code organization, for example if one wishes to place the main body at the top, and called functions below it.
咱們能夠從上面定義中提取出以下信息:
(1)前置聲明是針對類型,變量或者函數而言的
(2)前置聲明是個不完整的類型
(3)前置聲明會加快程序的編譯時間
其實上面的typedef struct __list *list_t;就是創建在前置聲明基礎上的。
前置聲明有哪些做用
(1)前置聲明能夠有效的避免頭文件循環包含的問題,看下面的例子
// circle.h
#include "point.h"
struct circle {
struct coordinate center;
};
// point.h
#include "circle.h"
struct coordinate {
struct circle cir;
};
#include "circle.h"
int main(int argc, const char *argv[])
{
struct circle cir;
return 0;
}
若是編譯這個程序,你會發現由於頭文件循環包含而發生編譯錯誤,即便修改頭文件以下也仍是不行:
#ifndef __CIRCLE_H__
#define __CIRCLE_H__
// circle.h
#include "point.h"
struct circle {
struct coordinate center;
};
#endif
#ifndef __POINT_H__
#define __POINT_H__
// point.h
#include "circle.h"
struct coordinate {
struct circle cir;
};
#endif
這個時候就能夠使用前置聲明輕鬆的解決這個問題,可是必需要使用指向不完整類型的指針了。
#ifndef __CIRCLE_H__
#define __CIRCLE_H__
// circle.h
//#include "point.h"
struct coordinate;
struct circle {
struct coordinate *center;
};
#endif
#ifndef __POINT_H__
#define __POINT_H__
// point.h
//#include "circle.h"
struct circle;
struct coordinate {
struct circle *cir;
};
#endif
能夠發現咱們連頭文件都不用包含的,這就能夠縮短編譯的時間了。
由於前置聲明是個不完整類型,全部不完整類型的優缺點和注意事項徹底適用於前置聲明。
參考連接:
http://blog.csdn.net/xiaoyusmile/article/details/5420252
http://blog.csdn.net/tonywearme/article/details/8136530
http://blog.csdn.net/candcplusplus/article/details/38498707
http://blog.sina.com.cn/s/blog_6f70a9530101en3a.html
http://www.tuicool.com/articles/RbYVnq