欢迎辞

欢迎来到“笃志以砺,决起而飞”!
如果您是第一次来到本站,建议访问本站导读以便更快地了解本站。
如果您喜欢本站,欢迎订阅

 

2012 年五月
« 四  
 123456
78910111213
14151617181920
21222324252627
28293031 

《仙剑奇侠传五》音乐资源提取器

这款小工具软件是仙剑5的音乐提取软件,可以将《仙剑奇侠传五》中的全部背景配乐和系统的特殊音效从已经安装的正版游戏的 pkg 包中提取并导出到您所指定的位置。支持简体中文和繁体中文两种界面语言。

请注意:这款软件严禁用于任何商业目的,所提取出的音乐仅限于在您购买正版《仙剑奇侠传五》所在的计算机上聆听,禁止上传至互联网或非法复制给其他单位或个人。因此产生的版权纠纷,作者不负任何责任。

注意:如果您使用的是 Windows 7、Windows Vista ,直接下载后运行即可。如果您是 Windows XP 系统并且下载后无法正常运行,请先下载 .NET Framework 2.0。

本软件的界面截图如下。

简体版截图

简体版截图

繁体版截图

繁体版截图

下载地址

单击此处下载。

写篇文章送给亲爱的你

当你还在震惊的时候,我就要告诉你,没错,这篇文章就是写给你看的。茫茫人海之中,我们相遇了。我们相遇在这月黑风高的夜晚。虽然此刻,你那迷茫的眼神似乎并没有意识到我想表达些什么,是的,虽然此刻,你热的和我一样难受,但就在此刻,我要写篇文章送给你,因为我已经爱上了你。

没错。虽然你那水汪汪的眼睛望着我手中的棍棒,甚至有一丝杀气。虽然你现在放松着身体,如一个民工一样躺在地上,享受着武汉夏季一阵阵的热浪袭来,既没有注意自己的形象,也似乎没有什么出众之处,而我们虽然有过几面之缘,甚至连话都还没有说过。但是现在我要为你写些什么了。

第一次见到你,是在一个平凡的夜晚,我走在校园的小路上,在图书馆旁的草丛与树林之间,我遇到了你。你一下子就吸引了我的目光。你长得不高,但身材还挺好,一双大耳朵似乎拥有无限的潜能。你长的不白,是个标准的黄种人,但也不黑。我和你相视一笑。

从此我就经常到这里和你约会。记得有一次天降大雨,我一个人跑到天文台去,看你会不会过来。果然这里一个人影都没有,我感到一丝遗憾。记得《南屏晚钟》中曾有这样几句话:

青青草木,子現南屏。
殷其雷,何斯違斯。
君子既去,其心也失。
美其子,寤寐思服。

是啊,这和现在的景象是何等的相似!只有我和寂寞的音乐声,却少了那个熟悉的身影。昏黄的灯光下,没有了你的倩影。或许失去了才会知道珍惜。我一时有些担心。这么大的雨,你是否找到了躲雨的地方?我开始后悔我没有去问你是不是无家可归。

是的,我开始自责。虽然我屡次与你相约在月黑风高的昏黄灯光照射的树林之中,可是我都没能问你是不是无家可归。一个大家闺秀怎么可能在这种情况下随便出门呢?如果你只是一个浪子,那我们是不是注定要分离呢?浪子,是四海为家的。

你虽然不是沉鱼落雁,闭月羞花,然而不瘦不胖也不黑,如果你肯干活,定是有人家会收养你的。如果运气好,也许可以有一个很帅的小伙子看上你。生命对你应该还是很美好的。

可是你没有来。“君子既去,其心也失”。

就这样,我们相思相守,我却不了解你,我们也只是最熟悉的陌生人。

我想起在几年之前,虽然那时的我远离江城,也曾经有缘碰到过几个如你一般的伙伴。其中一个和你最像的,也是一个无家可归的家伙。我把她养在家里,她开始时什么都不会,我买奶给她喝,教她去厕所,教她下楼梯……逐渐地她把我当成了自己的家人。虽然她是如此的弱小,然而她却屡次想在陌生人之前保护我。我爱她。

恍惚间又到了这个月黑风高的夜晚,我拿着那根陪我很久的棍棒又来找你。今天天气很好,你躺在老位置等我。我轻抚你的狗头,说:“想死我了!”然后对着你,吹出我最爱的姑苏行。

神奇的 C++ 传值——从期末 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
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 支持。

剛剛聽到這首樂曲,我 還以為這首樂曲是使用愛爾蘭風笛演奏的,可是經過證實,這首樂曲就是用普通的竹笛演奏的,只是換用了不同的笛膜和演奏技巧,就把愛爾蘭的特色發揮地如此生動和活潑。

這三個曲目都來自專輯《亂紅》,有興趣的朋友可以聽一下。

第 3 页,共 66 页12345678910111213...203040...最旧 »