歡迎辭歡迎來到“篤志以礪,決起而飛”! 如果您是第一次來到本站,建議訪問 本站導讀以便更快地了解本站。 如果您喜歡本站, 歡迎訂閱。 | 這款小工具軟件是仙劍5的音樂提取軟件,可以將《仙劍奇俠傳五》中的全部背景配樂和系統的特殊音效從已經安裝的正版遊戲的 pkg 包中提取並導出到您所指定的位置。支持簡體中文和繁體中文兩種界面語言。 請注意:這款軟件嚴禁用於任何商業目的,所提取出的音樂僅限於在您購買正版《仙劍奇俠傳五》所在的計算機上聆聽,禁止上傳至互聯網或非法複製給其他單位或個人。因此產生的版權糾紛,作者不負任何責任。 注意:如果您使用的是 Windows 7、Windows Vista ,直接下載後運行即可。如果您是 Windows XP 系統並且下載後無法正常運行,請先下載 .NET Framework 2.0。 本軟件的界面截圖如下。  簡體版截圖  繁體版截圖 下載地址單擊此處下載。 當你還在震驚的時候,我就要告訴你,沒錯,這篇文章就是寫給你看的。茫茫人海之中,我們相遇了。我們相遇在這月黑風高的夜晚。雖然此刻,你那迷茫的眼神似乎並沒有意識到我想表達些什麼,是的,雖然此刻,你熱的和我一樣難受,但就在此刻,我要寫篇文章送給你,因為我已經愛上了你。 沒錯。雖然你那水汪汪的眼睛望着我手中的棍棒,甚至有一絲殺氣。雖然你現在放鬆着身體,如一個民工一樣躺在地上,享受着武漢夏季一陣陣的熱浪襲來,既沒有注意自己的形象,也似乎沒有什麼出眾之處,而我們雖然有過幾面之緣,甚至連話都還沒有說過。但是現在我要為你寫些什麼了。 第一次見到你,是在一個平凡的夜晚,我走在校園的小路上,在圖書館旁的草叢與樹林之間,我遇到了你。你一下子就吸引了我的目光。你長得不高,但身材還挺好,一雙大耳朵似乎擁有無限的潛能。你長的不白,是個標準的黃種人,但也不黑。我和你相視一笑。 從此我就經常到這裡和你約會。記得有一次天降大雨,我一個人跑到天文台去,看你會不會過來。果然這裡一個人影都沒有,我感到一絲遺憾。記得《南屏晚鐘》中曾有這樣幾句話: 青青草木,子現南屏。 殷其雷,何斯違斯。 君子既去,其心也失。 美其子,寤寐思服。
是啊,這和現在的景象是何等的相似!只有我和寂寞的音樂聲,卻少了那個熟悉的身影。昏黃的燈光下,沒有了你的倩影。或許失去了才會知道珍惜。我一時有些擔心。這麼大的雨,你是否找到了躲雨的地方?我開始後悔我沒有去問你是不是無家可歸。 是的,我開始自責。雖然我屢次與你相約在月黑風高的昏黃燈光照射的樹林之中,可是我都沒能問你是不是無家可歸。一個大家閨秀怎麼可能在這種情況下隨便出門呢?如果你只是一個浪子,那我們是不是註定要分離呢?浪子,是四海為家的。 你雖然不是沉魚落雁,閉月羞花,然而不瘦不胖也不黑,如果你肯幹活,定是有人家會收養你的。如果運氣好,也許可以有一個很帥的小夥子看上你。生命對你應該還是很美好的。 可是你沒有來。“君子既去,其心也失”。 就這樣,我們相思相守,我卻不了解你,我們也只是最熟悉的陌生人。 我想起在幾年之前,雖然那時的我遠離江城,也曾經有緣碰到過幾個如你一般的夥伴。其中一個和你最像的,也是一個無家可歸的傢伙。我把她養在家裡,她開始時什麼都不會,我買奶給她喝,教她去廁所,教她下樓梯……逐漸地她把我當成了自己的家人。雖然她是如此的弱小,然而她卻屢次想在陌生人之前保護我。我愛她。 恍惚間又到了這個月黑風高的夜晚,我拿着那根陪我很久的棍棒又來找你。今天天氣很好,你躺在老位置等我。我輕撫你的狗頭,說:“想死我了!”然後對着你,吹出我最愛的姑蘇行。 (這篇文章主要涉及以下概念:指針、引用、內存地址、棧、堆、彙編、反彙編、寄存器、堆棧空間、賦值構造函數。) 不得不說我們學院的剛哥的 C++ 考試題就是不一樣…考完試之後,有一道題被很多同學議論頗多,這就是寫運行結果的第一題。這道題的題目大約如下。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| #include <iostream>
using namespace std;
class A{
int i;
public:
A(int x=0){
i=x;
cout << "In Constructor" << endl;
}
A(const A& a){
i=a.i;
cout << "In Copy Constructor" << endl;
}
~A(){
cout << "In Destructor" << endl;
}
};
A func(A a){
return a;
}
int main(){
A a;
A b = 1;
A c = func(a);
return 0;
} |
這道題目的正確答案是,兩個 In Constructor,兩個 In Copy Constructor,四個 In Destructor。這本身並沒有什麼疑問。 但是,如果我把這個程序給稍微給改一下,就會產生疑惑。 將 main 函數的內容改為如下: 1
2
3
4
5
6
| int main(){
A a;
A b;
b = func(a);
return 0;
} |
並且在 return 0; 設置斷點,運行,我們會發現輸出結果如下。 In Constructor In Constructor In Copy Constructor In Copy Constructor In Destructor In Destructor
我們分析一下,A a; 時產生了一次構造函數調用,A b; 時產生第二次。func(a) 複製實參時,調用複製構造函數,在func函數返回值的時候,由於返回的是類的對象本身,產生了一次複製構造函數調用。b 隨後被賦值為 func(a) 的返回值,所以實參析構, 析構函數被調用。問題是,後面的另一個析構函數調用是如何產生的? 經過分析我們可以發現,func(a) 產生了一個臨時的對象 tmp,隨後 tmp 被賦值給 b,隨後 tmp 被析構。 隨後我們把 main 函數改為如下: 1
2
3
4
5
| int main(){
A a;
A b = func(a);
return 0;
} |
這段代碼和上面代碼的區別是,b 從先定義後賦值改成了直接定義和初始化。 這時候我們會發現,tmp 的析構不存在了,func 返回時產生的 a 的副本是直接產生在 b 的地址上的。也就是說,不存在臨時的對象。 這時我們會很好奇。func 的返回值究竟是如何傳遞的?A b = func(a); 這樣的語句是如何能夠讓 func(a) 的返回值直接寫入到了 b 的地址空間呢?畢竟,返回值所創建的那個對象副本是在 func 作用域和棧空間里的,而 b 是 main 函數的局部變量,保存在 main 的堆棧空間。 於是我猜測,對 func 的調用壓入了一個隱含參數,這個參數的內容是指向返回值的地址的,func 的返回值就保存在給定的地址中。這樣,在函數內部就可以訪問到這個空間並保存函數的返回值。在 b = func(a); 語句中,b 已經初始化,這時要使用賦值構造函數重新構造 b,所以需要給賦值構造函數傳遞一個參數,就必須存在一個臨時變量 tmp 的地址,於是隱含參數所包含的地址就是編譯器創建的臨時對象的地址。 從此,可以分析出,func 的返回值是保存在一塊新的內存區域的;如果用戶將這個返回值直接用作初始化,那麼這塊區域就是被初始化函數的內存區域;如果不是,那麼編譯器會在調用方所在函數的棧空間里創建一個臨時的內存區域,用於保存返回值,由於臨時對象的唯一作用是作為賦值構造函數的參數傳遞給operator=函數從而將 b 重寫為 a 的副本,所以這必將導致一個結果,就是當 b 被賦值完畢之後,這個無名的臨時對象就無用了。 那麼,編譯器又如何處理了這個無名的臨時對象呢?正常人的想法是,這個變量應該和其他的同屬於這個棧的變量們一起,在函數作用域結束之前被析構。然而上述的運行結果告訴我們,這個臨時變量 tmp 是在之前就已經析構了的。從以上內容可以得到的結論是,編譯器對於函數調用,應該是在調用之前分配返回值的內存空間,並在調用後將這個空間立即處理掉(直接分配到局部變量的地址上的情況除外) 事實真的如此嗎?我寫了下面這樣一個程序進行驗證。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
#include <iostream>
using namespace std;
int time = 0;
class TestClass
{
public:
TestClass()
{
thistime = rand() % 1000;
ortime = -1;
cout << "[" << thistime << "] : created " << endl;
}
TestClass(TestClass &a)
{
thistime = rand() % 1000;
ortime = a.thistime;
cout << "[" << thistime << "] : created (from " << ortime << ")" << endl;
}
TestClass& operator= (const TestClass& a)
{
this->thistime = rand() % 1000;
this->ortime = a.thistime;
cout << "[" << thistime << "] : assigned (from " << a.thistime << ")" << endl;
return *this;
}
~TestClass()
{
cout << "[" << thistime << "] : destroyed " << endl;
}
int thistime;
int ortime;
};
TestClass func(TestClass test)
{
cout << "[" << test.thistime << "] : func called " << endl;
return test;
}
int main()
{
TestClass a;
TestClass b;
b = func(a);
cout << b.thistime << endl;
TestClass c = func(b);
cin.get();
return 0;
} |
這個程序的運行結果如下: [41] : created [467] : created [334] : created (from 41) [334] : func called [500] : created (from 334) [334] : destroyed [169] : assigned (from 500) [500] : destroyed 169 [724] : created (from 169) [724] : func called [478] : created (from 724) [724] : destroyed
運行結果分析如下: 1、在本程序剛啟動的時候,創建了一個 41 號的類(這個是變量 a)。 2、又創建了一個 467 號類(變量 b)。 3、到了 b = func(a),首先 a 被複制構造函數創建了一個副本到 func 的實參里,這個是 from 41 的 334 號類產生的原因。這時,程序為 func 的返回值也分配好了一個臨時的內存空間,位於 main 函數的堆棧空間(frame)里,並通過隱式參數傳遞到 func。 4、然後開始執行 func 函數體。輸出“[334] : func called”,這個函數產生了一個返回值 a。由於是類的對象,所以創建了一個副本,也就是調用複製構造函數,創建了 500 號類(from 334),並保存到通過隱式參數傳遞過來的臨時的內存空間地址中。func 調用結束,334 這個實參沒用了,所以 destroyed 了。 5、臨時空間中的500 號類被傳遞到 operator = (賦值構造函數)中,並用其重新構造了 b,此時 原來的 467 號類 b 變成了 169 號類。但其實還是原來的那個變量,地址不變,只是內容變了。 6、臨時空間用完了,隨即被編譯器生成的代碼釋放,此時調用了 500 號的析構函數(也解釋了最開始的問題)。 7、繼續執行 func(b) ,同理,b (169號)先被複製成 func 的實參 724號類,然後 724 複製了一個副本,產生返回值,這是 478 號。由於變量 c 是直接初始化而不是賦值的, 所以區別就來了,c的地址直接被傳遞給了 func,所以 func 的返回值直接存放到了 c 中,不存在任何複製構造或賦值構造。 8、724 號類隨着 func 的運行結束被銷毀,析構函數被調用。 9、控制權交給 cin.get(); 語句。 為了進一步驗證我們的猜想是否正確,我們現在對這個程序進行反彙編。核心的彙編代碼列舉如下。先來看看賦值構造函數情況下。(其中,ebp 是棧的基地址,eax 是 C/C++ 語言中存放返回值或返回值地址的寄存器的默認約定。) 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| // 語句 b = func(a);
010C1808 sub esp,8
010C180B mov ecx,esp
010C180D mov dword ptr [ebp-12Ch],esp
010C1813 lea eax,[ebp-18h]
010C1816 push eax // 以上代碼分配返回值的內存空間,位於 main 函數領空的堆棧空間,並壓入棧內作為隱性參數
010C1817 call TestClass::TestClass (10C120Dh) // 調用複製構造函數獲得實參
010C181C mov dword ptr [ebp-134h],eax
010C1822 lea ecx,[ebp-120h]
010C1828 push ecx // 實參壓棧
010C1829 call func (10C104Bh) // 執行 func 函數
010C182E add esp,0Ch
010C1831 mov dword ptr [ebp-138h],eax
010C1837 mov edx,dword ptr [ebp-138h]
010C183D mov dword ptr [ebp-13Ch],edx
010C1843 mov byte ptr [ebp-4],2
010C1847 mov eax,dword ptr [ebp-13Ch]
010C184D push eax // 將臨時內存空間內的類對象的地址傳給 operator= 構造變量 b
010C184E lea ecx,[ebp-28h]
010C1851 call TestClass::operator= (10C11C7h)
010C1856 mov byte ptr [ebp-4],1
010C185A lea ecx,[ebp-120h]
010C1860 call TestClass::~TestClass (10C112Ch) // 調用析構函數,析構臨時內存空間內的類對象 |
下面是直接初始化的情況。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // 語句 TestClass c = func(b);
010C1895 sub esp,8
010C1898 mov ecx,esp
010C189A mov dword ptr [ebp-110h],esp
010C18A0 lea eax,[ebp-28h]
010C18A3 push eax // 變量 c 的地址
010C18A4 call TestClass::TestClass (10C120Dh) // 複製實參
010C18A9 mov dword ptr [ebp-134h],eax
010C18AF lea ecx,[ebp-38h]
010C18B2 push ecx // 實參入棧
010C18B3 call func (10C104Bh)
010C18B8 add esp,0Ch
010C18BB mov dword ptr [ebp-138h],eax
010C18C1 mov byte ptr [ebp-4],3 |
明顯比前面的代碼少,返回值被直接寫入了調用方指定的棧中,也就是證明了我們之前的想法。 總結如下:調用函數時,不管參數類型如何,調用者(caller)負責從右至左將參數依次壓棧,最後壓入返回地址並跳轉到被調函數入口處執行。被傳遞的參數和返回地址都是位於調用者的stack frame(堆棧空間)中的。如果函數的返回值類型是整型(包括char,short,int,long及它們的無符號型)或指針類型的話(它們的長度小於等於 EAX 寄存器的長度(32位機上是 4 個字節)),那麼就利用EAX寄存器來傳回返回值。否則,利用 EAX 寄存器存放返回值的地址。傳入的返回地址可能是一個臨時的地址,這個地址將會在其他變量賦值後被立即回收,也可能是一個局部變量的地址,將在作用域超過時正常回收。 雷人的標題…但是,這實際上確實是一首歌名,演唱者是謝娜。這首歌用這個歌名只是為了嘩眾取寵,內容還是《采蘑菇的小姑娘》。 今天在友人的提醒下,突然想起了那首《采蘑菇的小姑娘》,順便也想起了《采姑娘的小蘑菇》……。回想起來,我還真的不怎麼記得這首曲子的旋律了。在我的印象里,這首歌聽得並不多,而且聽的年代很也已遠逝不復。類似的歌曲還有《中華民謠》《九月九的酒》之類。 雨中的天文台之夜,別有感覺。在今天這種將下不下的雨天中,我未曾想到還會有人同來。雨聲稀稀拉拉不成氣候,更襯出周圍的安靜。為了避雨,站在悠遠之處的亭子里,四周望去,儘是樹,儘是草,整個校園掩映在霧騰騰的水汽之中,幽靜的小路上空無一人,四周空蕩蕩的座位似乎是在為強化寧靜安逸之美而設。我在想這種氣氛下應該吹什麼。可是我想不出。卑微的我不知道拿什麼去配這樣的一個雨夜,然而音樂的魅力是無窮的。雨水滴在了笛膜上,所以和平時相比,吹奏頗有麻煩之處。然而所得怡然之樂可以讓人忘卻一切,包括時間。 回來之時,寢管早已等候多時。 作為我最喜愛的兩種樂器,鋼琴以其純美、廣闊讓我愛不釋手,笛簫以其婉轉、悠揚使我不能自己。其實,作為中華民樂的精華,笛簫和西洋樂器鋼琴的結合,產生的那種唯美的天籟之聲,更是讓人愛不釋手。可惜一個人不能同時演繹兩種樂器,很希望以後可以和友人一起,演繹出這樣的組合。在這裡我推薦一些鋼琴和笛簫結合的作品,以觴讀者。 綠野仙蹤(Fairy Footsteps In Green Land) 音頻片段:需要 Adobe Flash Player(9 或以上版本)播放音頻片段。 點擊這裡下載最新版本。您需要開啟瀏覽器的 JavaScript 支持。 這首樂曲我很喜歡的一首鋼琴和蕭結合的樂曲,鋼琴營造的寧靜感和蕭的緩緩訴說,彷彿讓人們來到一片夢幻般的綠色原野,這裡能找到傳說中的仙蹤嗎?在緩緩的節奏中,蕭和鋼琴的特色都被非常好地發揮了出來。尤其是那完美的氣顫音,讓人心為之所動。 亂紅(Flowers In a Riot Of Color) 音頻片段:需要 Adobe Flash Player(9 或以上版本)播放音頻片段。 點擊這裡下載最新版本。您需要開啟瀏覽器的 JavaScript 支持。 歌如其名,亂紅這首樂曲給人的感受,用竹笛和鋼琴的結合來演繹實在是太合適不過了。而樂曲的設計——開頭是竹笛旋律,鋼琴伴奏,後面是鋼琴旋律,竹笛增色,使兩種樂器的配合大放異彩。 愛爾蘭晨風(Moming Breeze of Lreland) 音頻片段:需要 Adobe Flash Player(9 或以上版本)播放音頻片段。 點擊這裡下載最新版本。您需要開啟瀏覽器的 JavaScript 支持。 剛剛聽到這首樂曲,我 還以為這首樂曲是使用愛爾蘭風笛演奏的,可是經過證實,這首樂曲就是用普通的竹笛演奏的,只是換用了不同的笛膜和演奏技巧,就把愛爾蘭的特色發揮地如此生動和活潑。 這三個曲目都來自專輯《亂紅》,有興趣的朋友可以聽一下。 | |
近期評論