→ james732:第一個問題,要先知道 #include 做了什麼事情 06/09 01:04
→ james732:第二個問題可找關鍵字「file scope」 06/09 01:06
推 loveflames:因為兩個unit都有Variable,看warning就知道了吧 06/09 01:08
→ loveflames:變成重複定義 06/09 01:10
→ tinlans:因為 extern int Variable 是宣告, 06/09 01:11
→ tinlans:int Variable = 0 是定義。 06/09 01:11
推 avhacker:正解是:不要用 XD 06/09 11:08
> -------------------------------------------------------------------------- <
作者: Trumen (真好多人) 看板: C_and_CPP
標題: Re: [問題] global variable的問題
時間: Wed Jun 9 10:14:55 2010
※ 引述《Trumen (真好多人)》之銘言:
: 請問通常global variable會宣告在.cpp還是.h呢?
: 之前曾經將global variable寫在.h
: 但add Thread.cpp to project後compiler會出現warning訊息:
: (ex: in main.h, int Variable = 0;)
: [Linker Warning] Public symbol '_Variable' defined in both module D:\TRP
: CODE\TRP 5.1\MAIN.OBJ and D:\TRP CODE\TRP 5.1\THREAD.OBJ
: 後來查了網路,發現要寫成
: (in main.h) extern int Variable;
: (in main.cpp) int Variable = 0;
: 這樣子才不會有warning message產生,當中的原因是什麼呢?
: 還有若是我將原本在main.h的變數改成 static int Variable = 0;
: 也可以避免warning message產生,這又是為什麼呢?
: (可以請問將variable設static的意義是?)
: 謝謝!
http://delphi.ktop.com.tw/board.php?cid=168&fid=912&tid=29349
找到問題所在了,分享一下~
=========================================================================
原作者並沒有把他的Code List出來, 只說了他用了Global Value.
因此, 以這種描述, 有兩種可能的code.
a.h, case 1
static int A;
static int B;
sttaic int C;
a.h, case 2:
int A;
int B;
int C;
但是, 因為你把變數宣告在 include file 裡.
要知道, # 開頭的 command 都會被 preprocessor 處理掉, 因此, 假設你寫的是:
A.cpp:
#include "a.h"
int main(void)
{
...
return 0;
}
B.cpp:
#include "a.h"
void somefunc(void)
{
...
}
在 a.h case 1 裡, preprocessor 將會把程式展開像這樣:
a.cpp:
static int A;
static int B;
sttaic int C;
int main(void)
{
...
return 0;
}
b.cpp:
static int A;
static int B;
sttaic int C;
void somefunc(void)
{
...
}
在 a.h case 2 裡, preprocessor 將會把程式展開像這樣:
a.cpp:
int A;
int B;
int C;
int main(void)
{
...
return 0;
}
b.cpp:
int A;
int B;
int C;
void somefunc(void)
{
...
}
這兩個有什麼不一樣呢?
注意到那個 static 嗎? 根據C/C++語言規則, 定義為 static variable 的變數, 可視區
域僅限於宣告區域, 但是存在的壽命卻相當於 external variable 變數.
如果我沒記錯的話, static 變數的記憶空間配置是在程式載入初始時期 (就是 main()
被執行之前), 而他的內含值設定則是在該敘述第一次被執行到的時候, 且, 也只會設這
麼一次, 如果沒有指定特殊值, 則會被設定為零. 且這個記憶體將會保證在離開 main()
之前會一直都是存在的.
而另一種寫法叫作 external variable. 根據 C/C++ 語言規則, 凡是 external
variable 變數, 其空間配置和內容初始化時期是在 main() 被執行前, 如果沒有指定特
殊值, 則會被設定為零. 且這個記憶體將會保證在離開 main() 之前會一直都是存在的.
如果是 external variable, 則無論你宣告多少次, 都應該只會有一份實體存在. 換句話
說, 你在 external variable 可視的領域內作了任何修改, 都應該反應到同一個記憶體
空間才對.
但是正常來說, external variable 大都宣告在實作檔 (.c/.cpp) 中, 然後在 header
file 中宣告 extern type var_name, 然後含入這個 headrer file, 以便讓 compiler
知道有這一個 external variable. 或是說讓這個 external variable 的視野擴張到這
個實作檔裡.
如果說像 case 2 那樣寫, 沒有宣告 extern, 而是都宣告成 external variable 的話.
當編譯時期, compiler 只要看得到reference symbol 就好, 他才不管實際空間位置配置
, 所有實作檔中, symbol 是否重覆等問題. (只要同一個實作檔中沒有衝突, compiler就
不管)
前面說的問題是 linker 要去處理的. 因此, 理論上 case 2 的寫法, 應該會引發 link
time error (或是 Warning, 這可能要看 error level 的設定, 或是 linker 的實作判
定).
所以我直覺認為, 原作者應該是採用 case 1 的寫法才對. 因為 case 2 的作法, 如果沒
有任何錯誤 (duplication symbol 一類的錯誤), 至少應該會有警告.
我實在不太相信 borland c++ 會這麼蠢, 連個警告都不給才是. (要是真的, 那我真的無
話可說...)
case 1 的寫法, 你每一個 golbal variable 事實上都是不同的實體, 所以
compiler/linker都不會提出任何質疑.
不信的話你可以去dump symbol, 他們應該都會被冠上一些奇怪的前置名稱才對. (為了避
免linker出來叫)
=============================================================================
所以問題是在*.cpp include "main.h"時 main.h有global變數
使得所有*.cpp都會宣告該變數
所以在main.h內使用extern宣告變數的話,變數就不會重複定義了
而 static 本來就是 local 的,只因為它放在整個 *.cpp 的 global 區,但它
也只限此 *.cpp 使用,對於別的 *.cpp 有同樣的宣告並不會有任何影響。
以下是我的問題:
1. 如果用static宣告在main.h裡的變數的話
是否會變成所有*.cpp都會產生static變數呢? 這樣是不是有點占空間阿?
(我只想要在main.cpp有此變數,用static寫法妥當嗎?)
(因為我用extern會發生一些奇怪的問題...只能用static)
2. (跟global variabla無關的問題)
如果main.h 有#include "function.h"
則如果我把 function.h改成function.cpp
在main.h 變成#include "function.cpp" 前後會有什麼差嗎?
謝謝各位前輩不吝指教~!
> -------------------------------------------------------------------------- <
作者: LPH66 (-858993460) 看板: C_and_CPP
標題: Re: [問題] global variable的問題
時間: Wed Jun 9 11:55:02 2010
※ 引述《Trumen (真好多人)》之銘言:
: 所以問題是在*.cpp include "main.h"時 main.h有global變數
: 使得所有*.cpp都會宣告該變數
: 所以在main.h內使用extern宣告變數的話,變數就不會重複定義了
: 而 static 本來就是 local 的,只因為它放在整個 *.cpp 的 global 區,但它
: 也只限此 *.cpp 使用,對於別的 *.cpp 有同樣的宣告並不會有任何影響。
: 以下是我的問題:
: 1. 如果用static宣告在main.h裡的變數的話
: 是否會變成所有*.cpp都會產生static變數呢? 這樣是不是有點占空間阿?
: (我只想要在main.cpp有此變數,用static寫法妥當嗎?)
: (因為我用extern會發生一些奇怪的問題...只能用static)
是的
至於你的需求...你可以考慮一下你這個變數要在哪裡可以看得到
如果只有 main.cpp 需要的話 那你就寫個 static int global; 丟在 main.cpp 就好
除非你要在別的 .cpp 裡也要看到這個變數才需要宣告 extern
不過下 extern 就相當於和 compiler 講說
「這個變數現在在哪我不知道 但 linker 會知道 所以請留個位置給 linker 填」
所以你必須要在其中一個 .cpp 實際宣告
這樣 linker 看到了這個變數實際上在哪個地方 才會做正確的連結動作
(在這層意義上來說, function declaration 其實預設都是 extern
也因此我們才能夠以宣告的方式 (不管是直接寫或 #include)
使用寫在別的地方 (包含同一個檔案的後方) 的 function)
: 2. (跟global variabla無關的問題)
: 如果main.h 有#include "function.h"
: 則如果我把 function.h改成function.cpp
: 在main.h 變成#include "function.cpp" 前後會有什麼差嗎?
這你就要了解到底 #include 做了什麼事
#include 做的事其實很單純
就是讓 compiler 幫你 copy-paste 那個檔的內容到你寫 #include 的地方
那麼你就要看那個檔案的內容如果 copy-paste 到這裡來會發生什麼事
還有就是 習慣上我們通常不會去引入 .cpp 檔
因為每個 .cpp 都是獨立的 compilation unit
整個 compile & link 的架構是這個樣子
compile link
.cpp ==========> .obj ───┐
│
.cpp ==========> .obj ───┼→ .exe
│
.cpp ==========> .obj ───┘
如果你引入了一個 .cpp 的話 你的架構就會變成
compile link
.cpp ==========> .obj ───┐
│
.cpp ==========> .obj ───┴→ .exe
↑include
.cpp
原本是互相對等的各個 .cpp 出現了包含的情形
雖然程式一樣可以 compile 可以跑
但架構上有一個 .cpp 和別人不一樣就是怪怪的
而且萬一一不小心把那個 .cpp 也當做一個 compilation unit 的話
compile link
.cpp ==========> .obj
.cpp ==========> .obj
↑include >→相同名字的東西在不同的 compilation unit 出現
.cpp ==========> .obj 容易有 link error
因為 linker 不知道這名字到底是指哪一個
(linker 並不會知道它們其實是一樣的東西)
因此習慣上我們並不會引入一個 .cpp 檔
而 .h (header file) 其實只是把共同的宣告給提出來寫在別的檔案而已
這也就是為什麼有些地方會教說「.h檔只要寫宣告別寫實作」
因為寫上實作就有可能會發生上面的情形 道理是一樣的
(template那種麻煩鬼先丟一邊去)
那如果有些時候一些東西真的沒辦法得要別寫一個檔案的話
個人習慣是會用別的副檔名 (例如 .inc) 表示這個檔案是用來被某個 source 引入的
這樣既不會和 .cpp 的定位搞混 也不會搶了 .h 慣例上的意義(ie.宣告用)這樣
> -------------------------------------------------------------------------- <
作者: motocyman (無言了) 看板: C_and_CPP
標題: Re: [問題] global variable的問題
時間: Wed Jun 9 13:43:37 2010
: 1. 如果用static宣告在main.h裡的變數的話
: 是否會變成所有*.cpp都會產生static變數呢? 這樣是不是有點占空間阿?
是一定占空間,想像一下你沒有要用到,卻還宣告一下 private int temp;
除非聰明的compiler會不會幫你省?!…
: (我只想要在main.cpp有此變數,用static寫法妥當嗎?)
在書上有看到說
如果只有這個 main.cpp or main.c檔會用到的變數,其它不會用到的話
就放在自己的.cpp .c檔裡就好了
所以 static variable static function 就別放在.h檔
※ 引述《LPH66 (-858993460)》之銘言:
: ※ 引述《Trumen (真好多人)》之銘言:
: compile link
: .cpp ==========> .obj ───┐
: │
: .cpp ==========> .obj ───┼→ .exe
: │
: .cpp ==========> .obj ───┘
: 如果你引入了一個 .cpp 的話 你的架構就會變成
: compile link
: .cpp ==========> .obj ───┐
: │
: .cpp ==========> .obj ───┴→ .exe
: ↑include
: .cpp
: 原本是互相對等的各個 .cpp 出現了包含的情形
: 雖然程式一樣可以 compile 可以跑
: 但架構上有一個 .cpp 和別人不一樣就是怪怪的
: 而且萬一一不小心把那個 .cpp 也當做一個 compilation unit 的話
: compile link
: .cpp ==========> .obj
: .cpp ==========> .obj
: ↑include >→相同名字的東西在不同的 compilation unit 出現
: .cpp ==========> .obj 容易有 link error
: 因為 linker 不知道這名字到底是指哪一個
: (linker 並不會知道它們其實是一樣的東西)
答案上面兩篇都有提過了。 以上恕刪部份
從別的角度來看,觀察一下一些 linux的 source code的header檔
裡面的變數,幾乎都是 static、extern的變數,而不會看到 int variable;
====================
你的這個header檔,只被1個.c檔include,自然沒問題,但機會很小
只要被2個以上的.c檔include,就會linker error,原因上一篇有提到(圖解很棒)
深入一點來看
站在compiler的角度來看,我就把foo翻成_foo,自然沒問題,沒理由擋你
但站在linker來看,它想把全部obj檔link起來的話,看到多個_foo當然會叫
有遇過考題,考說 .h檔內寫個int a; 被a.c b.c include 請問會如何 怎麼解決
1. 過的了沒問題 2. compiler過不了 3. linker過不了
自然就是用 static解 or extern
另外,其實這問題就等同於在同一個c project裡,兩個.c檔都寫 int a; 一樣
就是multiple defination
以上都是用c的角度去看,會有這問題。
以c++來說,習慣上,variable、function都會被class封裝起來,就不會這問題
> -------------------------------------------------------------------------- <
作者: tinlans ( ) 看板: C_and_CPP
標題: Re: [問題] global variable的問題
時間: Wed Jun 9 23:58:04 2010
※ 引述《Trumen (真好多人)》之銘言:
: http://delphi.ktop.com.tw/board.php?cid=168&fid=912&tid=29349
: 找到問題所在了,分享一下~
: =========================================================================
: 原作者並沒有把他的Code List出來, 只說了他用了Global Value.
: 因此, 以這種描述, 有兩種可能的code.
: a.h, case 1
: static int A;
: static int B;
: sttaic int C;
: a.h, case 2:
: int A;
: int B;
: int C;
: 但是, 因為你把變數宣告在 include file 裡.
不對,
上面兩種 case 通通都是宣告+定義,
慣例上簡稱為定義,
但不能簡稱為宣告。
重複宣告只要每次宣告都一致,
無論幾次都可以:
extern int A;
extern int A;
extern int A;
int A;
上面這樣叫宣告三次、定義一次,
編譯和連結都不會發生問題。
宣告只是告訴 compiler 如何看待那個識別字,
譬如你寫 A = 1 但 A 的 type 並未宣告,
compiler 就不知道它是整數還是浮點數,
因為整數和浮點數的表示形式不同。
如果 A 是 double 型別,
compiler 還必須先把 1 轉成 double 的 1.0。
把宣告抽到 header 只是維持宣告的一致性,
以變數宣告來說,
這麼做是避免某個變數在不同編譯單元被不同的方式解讀。
: 要知道, # 開頭的 command 都會被 preprocessor 處理掉, 因此, 假設你寫的是:
並不是 # 開頭都會,
像是 #pragma 就會留到 compiler 那邊才處理,
當然這是題外話。
: A.cpp:
: #include "a.h"
: int main(void)
: {
: ...
: return 0;
: }
: B.cpp:
: #include "a.h"
: void somefunc(void)
: {
: ...
: }
: 在 a.h case 1 裡, preprocessor 將會把程式展開像這樣:
: a.cpp:
: static int A;
: static int B;
: sttaic int C;
: int main(void)
: {
: ...
: return 0;
: }
: b.cpp:
: static int A;
: static int B;
: sttaic int C;
: void somefunc(void)
: {
: ...
: }
: 在 a.h case 2 裡, preprocessor 將會把程式展開像這樣:
: a.cpp:
: int A;
: int B;
: int C;
: int main(void)
: {
: ...
: return 0;
: }
: b.cpp:
: int A;
: int B;
: int C;
: void somefunc(void)
: {
: ...
: }
: 這兩個有什麼不一樣呢?
: 注意到那個 static 嗎? 根據C/C++語言規則, 定義為 static variable 的變數, 可視區
: 域僅限於宣告區域, 但是存在的壽命卻相當於 external variable 變數.
: 如果我沒記錯的話, static 變數的記憶空間配置是在程式載入初始時期 (就是 main()
: 被執行之前), 而他的內含值設定則是在該敘述第一次被執行到的時候, 且, 也只會設這
: 麼一次, 如果沒有指定特殊值, 則會被設定為零. 且這個記憶體將會保證在離開 main()
: 之前會一直都是存在的.
上面這段文字的部分講的是 static local variable,
但是上面他列的 code 顯然是 global variable,
所以顯然說得不對。
這人把 lifetime、scope、linkage 全部混在一起講,
而且還講錯。
所以奉勸你還是乖乖唸書比較實在,
網路上的別人講的東西幾乎都沒被驗證過。
加上真正懂的人也未必在回你文的時候狀態很好,
有時不小心打錯幾個字,
回文的時候漏看部分引文,
或是往前修文的時候不小心斷開原本應該連貫的東西,
都會造成你很大的誤會。
: 而另一種寫法叫作 external variable. 根據 C/C++ 語言規則, 凡是 external
: variable 變數, 其空間配置和內容初始化時期是在 main() 被執行前, 如果沒有指定特
: 殊值, 則會被設定為零. 且這個記憶體將會保證在離開 main() 之前會一直都是存在的.
: 如果是 external variable, 則無論你宣告多少次, 都應該只會有一份實體存在. 換句話
只要是 global variable 就是這種特性,
跟什麼 static/extern 這種 linkage 特性無關。
: 說, 你在 external variable 可視的領域內作了任何修改, 都應該反應到同一個記憶體
: 空間才對.
這邊才勉強算是說對。
: 但是正常來說, external variable 大都宣告在實作檔 (.c/.cpp) 中, 然後在 header
: file 中宣告 extern type var_name, 然後含入這個 headrer file, 以便讓 compiler
: 知道有這一個 external variable. 或是說讓這個 external variable 的視野擴張到這
: 個實作檔裡.
帶有 external linkage 性質的 global variable 大都「定義」在實作檔中。
而且不是打了 extern 就代表純宣告,
一旦你給了初值又會變成定義,
如:
extern int A = 1;
現在的 compiler 會針對這種 case 發 warning,
因為正常來說不會這樣寫。
: 如果說像 case 2 那樣寫, 沒有宣告 extern, 而是都宣告成 external variable 的話.
: 當編譯時期, compiler 只要看得到reference symbol 就好, 他才不管實際空間位置配置
: , 所有實作檔中, symbol 是否重覆等問題. (只要同一個實作檔中沒有衝突, compiler就
: 不管)
case 2 那個單純就是因為兩個檔案都放了定義,
這段第一行不知道他在扯什麼。
: 前面說的問題是 linker 要去處理的. 因此, 理論上 case 2 的寫法, 應該會引發 link
: time error (或是 Warning, 這可能要看 error level 的設定, 或是 linker 的實作判
: 定).
對 C++ 來說一定是 linking time error,
如果是 C 則完全不可能 error。
case 2 的狀況在 C 語言來說,
它是屬於 common variable (可用沒設初值判定),
A B C 三個 symbol 在外部符號表中會帶 common 屬性,
所以不會發生問題。
雖然這也是題外話,
但因為這個人前面一直講 C/C++,
所以我還是稍微在這裡區別一下。
: 所以我直覺認為, 原作者應該是採用 case 1 的寫法才對. 因為 case 2 的作法, 如果沒
: 有任何錯誤 (duplication symbol 一類的錯誤), 至少應該會有警告.
: 我實在不太相信 borland c++ 會這麼蠢, 連個警告都不給才是. (要是真的, 那我真的無
: 話可說...)
: case 1 的寫法, 你每一個 golbal variable 事實上都是不同的實體, 所以
: compiler/linker都不會提出任何質疑.
這邊沒問題,
都是不同實體,
所以值也沒辦法在編譯單元之間傳遞。
: 不信的話你可以去dump symbol, 他們應該都會被冠上一些奇怪的前置名稱才對. (為了避
: 免linker出來叫)
dump 出來未必有前置名稱,
通常有前置名稱的是 static local variable。
帶前置名稱通常只是為了要避開跟 static global variable 衝到名稱,
當然也可以幫 static global variable 冠一些前置名稱來避免衝到,
這都是做法上的自由。
現在比較實際的做法是讓那個 symbol 帶 local 屬性,
這樣 linker 就算看到不同 object file 裡有相同名稱也不會有事。
: =============================================================================
: 所以問題是在*.cpp include "main.h"時 main.h有global變數
: 使得所有*.cpp都會宣告該變數
: 所以在main.h內使用extern宣告變數的話,變數就不會重複定義了
因為 main.h 你放了定義而不是純宣告,
所以才會變成重複定義。
: 而 static 本來就是 local 的,只因為它放在整個 *.cpp 的 global 區,但它
: 也只限此 *.cpp 使用,對於別的 *.cpp 有同樣的宣告並不會有任何影響。
可以這樣說。
: 以下是我的問題:
: 1. 如果用static宣告在main.h裡的變數的話
: 是否會變成所有*.cpp都會產生static變數呢? 這樣是不是有點占空間阿?
當然會。
: (我只想要在main.cpp有此變數,用static寫法妥當嗎?)
只在 main.cpp 用,
那就用 static,
但是請寫在 main.cpp 裡面。
不讓其它編譯單元知道的東西你擺在 header file 幹嘛。
: (因為我用extern會發生一些奇怪的問題...只能用static)
我覺得單純是你沒分清宣告跟定義,
又靠著用錯誤嘗試法和一些不可靠的資料來解決基礎問題,
才會出現所謂奇怪的問題。
使用工具去解決問題的方法沒有絕對的對或錯之分,
所以可以去翻翻討論區或是互相討論。
但是工具基礎使用方法卻是對錯分明 (像是英文第三人稱單數後面的動詞變化),
查說明書或教科書會是最簡單又有效而穩固的。
總不能為了不想搞懂第三人稱單數後面的動詞變化,
就乾脆以後第三人稱永遠用複數而不用單數。
這樣雖然問題表面上是解決了,
但是以後你句子給別人的感覺就是會很奇怪。
雖然這種比喻看似誇張,
但在寫程式的圈子裡用這種方式去迴避基礎問題的人卻超乎想像的多,
特別是那些解法相當容易在網路上流傳。
: 2. (跟global variabla無關的問題)
: 如果main.h 有#include "function.h"
: 則如果我把 function.h改成function.cpp
: 在main.h 變成#include "function.cpp" 前後會有什麼差嗎?
宣告:
void foo();
定義:
void foo()
{
}
你把定義放在 function.h,
那有兩個 .cpp 去 include 它就穩死。
當然你也可以用 static 甚至是 inline 去解。
可是用了 static 就沒必要放在 header,
而 inline 則是為了改進 performance 才會用到。
如果只是為了解決衝 symbol 的問題去用 inline,
奇怪的問題又可能會發生。