看了下时间,此时已是凌晨01:17分,辗转难眠的我,空洞的双眼显得些许木纳,脑海一片空白的自己也不知道在想什么。

其实哪有什么好失眠的啊,不过是日常熬夜成瘾,一旦闲下来早睡也睡不着罢了。

大字躺着望着乌漆麻黑的天花板发呆的我,索性任性不睡了,那就竖起耳朵倾听着时间走向天亮吧。

这个点,阳台外面早已褪去了人群来往的喧嚣,不过偶尔还是会传来车经过的声响。

有的司机像是在着急赶路一样“呼”的一声飞驰而过;有的则像极了新手上路缓缓行走的车轮温柔地抚摸着洒落在街道上的月光,生怕把它的影子碾碎一般。

唯一相同的是,黄亮的车前灯伴着月光穿过围墙,透过窗台依然是那么刺眼,似乎在赤裸裸地挑衅着宣告它已经看光了所有,你不必躲躲藏藏。

我想,此时那条温顺的小狗应该和往常一样吧,会一如既往地乖巧地躺在守夜阿姨的身旁,默默地陪伴着看守着这凄清的街道。

但我并不敢起身去验证这个猜想,因为我害怕啊。

我害怕看到的是忠诚的小狗也有坚持不住每天都守护着主人的那一天,只剩下阿姨落寞的形影单只,孤单地守着本就孤单的夜晚。

其实主要是因为我胆小。

放在床头边乘凉的小风扇机械地在运转着,振动的嘎嘎声像是它发出疲倦呻吟的求救信号一般地荡漾在我耳边。

已远去的夏天,炎热却并没有随着跟走。我哪有那么大的慈悲让它停下来稍微歇息一会,哪怕一小会啊。

运转扇风本就是它活着的使命,谁不在疲惫的活着呢。

忘记了这是第几个凌晨还没睡,也忘记了多久没有十二点前睡着过了。

老实说,每天杂七杂八忙忙碌碌的生活,我都不知道自己在忙了些什么,但又真的没空。大概这就是混日子最后反被日子混了的结果吧。

记得,以前老师说过眼前辛苦点好好学习过后上了大学就轻松了,可我似乎并没有感到过一刻轻松。

我甚至不知道是我上了大学还是大学上了我,除了做不完的作业还是作业,恨不得时光倒流回去只需要一个脑袋栽进学习里就好了的高中生活。

每天由己或者身不由己的熬夜成瘾,一日复一日里成了死循环。想要改变的决心还没有萌生一秒,意识里就已经发出了否的反抗,可笑的是理由还挺理所应当。

不是不害怕过自己会突然毫无征兆的猝死在某一天的深夜里,可是比起死亡我更害怕活不下去。

活下去的压力一个又一个的接踵而来,死亡显得反倒像是一种奢望的解脱,只是有点不负责罢了。

那既然这样,还是接着好好熬夜好好生活下去吧。

也没什么不好的,至少能醒着感知这个世界多一点点。

首先请大家看一个视频: https://www.bilibili.com/bangumi/play/ep314554

这个视频讲的是北京的夜晚,一群男人在湖边钓鱼,不在乎能不能钓到鱼,在乎的是坐在湖边的那一份安静。这些人来自各行各业,不过他们大都有一个特点,就是人到中年,身上扛着各种各样的压力,白天到处奔波,面对各种柴米油盐,晚上终于有一点自己的时间,来湖边安安静静的坐着,释放着一天的疲惫,享受一个人的孤独和平静,天亮了,继续去奋斗。

视频很温馨,看完这个视频我还想起之前看过的一个文章,说有一些人,下班开车回家,到家之后并不马上回屋里,而是在车里安静的坐一会,或者点支烟抽一会,然后才回屋里,这是每天最平静的时候。走出这一块安静的地方,又得去面对各种各样的事情。

这就是人生,你永远不知道世界上这么多人每个人都是过着什么样的生活,有的人看完视频觉得原来有那么多人跟我一样啊,瞬间感觉自己不孤独,心里很温暖。有的人看了视频,感叹原来还有这样的生活,那我自己还觉得压力大,真是无病呻吟,瞬间压力减少了很多。还有的人看完视频,心有感触,哇的一下哭了了出来,哭完心里也好了很多。

其实这哪里是什么终极解法,视频只是给忙碌中的我们一丝温暖,一丝感动,希望我们能放下焦虑的心,世界这么大,我们永远不是一个人,休息过后继续奋斗才是真。

现代社会,焦虑和迷茫总是人们提及的最多的词,几乎所有的人都希望能有一份安稳,然而安稳几乎都是用付出换来的,我们不能一边呆在舒适区渴望着成功,渴望着改变,一边害怕自己的努力被辜负,害怕付出之后得不到结果,等到风雨来袭的时候,又后悔当初的自己没有努力。

有焦虑有迷茫是好事,坏的是感受不到焦虑每天沉浸在娱乐中不能自拔,坏的是感到焦虑却不去努力改变。其实不仅中年人有危机,年轻的时候如果不努力,那时候危机就已经有了,只不过当时是一个人吃饱了全家不饿,并没有切身的感受到。当中年到来的时候,才感受到了种种的危机,开始还以前欠下的债务。人生短短几十载,我们能做的就是把握住当下,想想未来五年,十年之后的我们,不要让那时的我们在因为今天的不努力而继续悔恨。

世界很大,无论我们现在处于什么状态,世界上总会有很多人跟我们一样,虽然我们彼此不认识,但是我们只要知道在前进的道路上我们并不孤单就行了,知道总会有人与我们同行就行了。

一起加油吧,不管我们是否认识,我们都在努力着。

引言

为何redis中大量使用的是SDS,而不是传统的C语言字符串表示法存储字符串?到底什么是SDS?为什么要使用SDS?其相对于传统的C语言字符串有何优点?本文会针对以上几点做一个详细的分析,帮助大家以及自己更好的理解redis中的简单动态字符串

介绍SDS之前先简单介绍一下C语言中的传统字符串表示法

C语言字符串(C字符串)

始终以空字符结尾的字符数组,c语言为其封装了大量的函数库操作API

C字符串特点
  • 字符数组存储
  • 以空字符结尾,除了末尾之外,字符串里面不可以包含空字符
  • 由于采用字符数组,导致所存储的字符必须符合某种编码(如ASCII)
  • 获取C字符串长度必须遍历整个字符串,并对遇到的每个字符计数,直到遇到空字符为止,时间复杂度为O(N)

那么基于以上特点,C字符串是否能够满足我们Redis高效,安全的需求呢?显然是不能的,单是一个获取字符串长度就需要遍历整个字符串数组了,必须重新定义一种新的结构以支持Redis中对于频繁高效操作字符串的要求。


SDS 简单动态字符串

基于以上C字符串的缺陷,Redis自己构建了一种名为简单动态字符串的抽象类型,简称SDS。

SDS结构
struct sdshdr {

    // buf数组已经使用字节数量,即SDS字符串长度
    int len;

    // 记录buf数组中未使用字节的数量
    int free;

    // 字节数组,用于保存二进制数据
    char buf[];
}

可以看出,SDS定义的结构中,增加了一个int类型的len属性用于记录SDS所保存的字符串长度,一个free属性用于记录数组中未使用的字节数量。

SDS特点
  • 常数复杂度获取字符串长度

Redis中利用SDS字符串的len属性可以直接获取到所保存的字符串的长
度,直接将获取字符串长度所需的复杂度从C字符串的O(N)降低到了O(1)。


  • 减少修改字符串时导致的内存重新分配次数

基于前面介绍的C字符串的特性,我们知道对于一个包含了N个字符的C字符串来说,其底层实现总是N+1个字符长的数组(额外一个空字符结尾),那么如果这个时候需要对字符串进行修改,程序就需要提前对这个C字符串数组进行一次内存重分配(可能是扩展或者释放),而内存重分配就意味着是一个耗时的操作。

Redis巧妙的使用了SDS避免了C字符串的缺陷,在SDS中,buf数组的长度不一定就是字符串的字符数量加一,buf数组里面可以包含未使用的字节,而这些未使用的字节由free属性记录。

SDS采用了空间预分配策略避免C字符串每一次修改时都需要进行内存重分配的耗时操作,将内存重分配从原来的每修改N次就分配N次降低到了修改N次最多分配N次。

字符串扩展:

首先检查SDS未使用空间即free属性是否够用,如果够用,则会直接使用未使用空间,而无须进行内存重分配

空间预分配

所谓预分配就是额外多分配一部分空间给SDS,并不是仅仅只分配刚好够字符串扩展修改后的空间。

分配策略:

  1. 若SDS修改后,其长度(len的属性)小于1MB,那么程序会分配和修改后的len属性同样大小的未使用空间
  2. 若修改后,其长度大于1MB,那么程序只会分配固定1MB的未使用空间,不会再多分配了,考虑内存的成本因素

字符串缩短:

针对SDS字符串的缩短场景,SDS并不会立即释放多余出来的内存空间,而是将这部分内存空间记录再其free属性中,当SDS字符串进行扩展时,这部分未使用的内存空间就能直接用,而不需要进行内存重分配,这就是SDS的惰性空间释放


  • 杜绝缓冲区溢出

在C字符串的拼接操作过程中,例如某程序员操作S1拼接S2,由于程序员忘记了给需要拼接的字符串S1分配足够的内存空间(到底需要分配多少内存呢?哈哈,当然需要遍历S2的字符数组才能知道S2的长度是多少,因为C字符串不记录自身的长度),那么拼接的时候就会导致缓冲区溢出的可能性。

针对以上情况,SDS的空间分配策略可以完全杜绝这种情况,因为当SDS 的API对SDS进行修改时,API会首先检查SDS的未使用空间是否足够,不够的化会进行内存重分配以扩展空间至足够修改所需的大小,然后再执行实际的修改操作,这样就可以达到杜绝缓冲区溢出的可能。


  • 让Redis保存更多类型的数据成为可能

由于C字符串中保存的字符必须符合某种编码格式(如ASCII),这就限制了C字符串只能保存文本数据,而不能保存箱视频,音频,压缩文件这种的二进制数据。

另一个限制是C字符串中间不能包含空字符,否则中间的空字符会被认为是整个字符串的结尾,而导致后面的部分字符被忽略掉。

SDS的API被设计成了二进制安全的,以处理二进制的方式来处理SDS中存放再buf数组中的数据,原样存取,这就是为什么在SDS的结构中采用的是字节数组,而不是C字符串中的字符数组

这样的二进制安全的SDS,使得Redis不仅可以保存文本,还可以保存任意格式的二进制数据。


  • 兼容部分C字符串API

由于C字符串本身具有大量操作API,SDS如果可以利用一部分C字符串的API那样就不用重复发明轮子了,所以Redis中的SDS遵循C字符串以空字符结尾的惯例,在SDS的API中,总会将SDS保存的数据末尾设置未空字符,在分配buf数组时也总会多分配一个字节来保存这个空字符,这样SDS就可以重用一部分C字符串库的API。


C字符串与SDS对比

对比点C字符串SDS
获取字符串长度时间复杂度O(N)O(1)
API安全性不安全,可能造成缓冲区溢出安全,不会造成溢出
字符串修改N次需要几次内存重分配N次至多N次
能够保存数据类型只能保存文本文本或二进制
对于C语言中字符串API的使用范围所有一部分

总结

Redis中采用SDS替代C语言中传统字符串表示法,提升了获取字符串长度的效率,扩大了能够保存数据的类型范围,以及降低了每次修改字符串时候的内存重分配次数,甚至规避了在操作C字符串中可能出现的缓冲区内存溢出的可能性,从而为Redis中字符串操作的安全,高效提供了保障。

(1)读取 http body 部分

(2)根据 boundary 分析出分隔符特征(这个串是唯一的,不会与body内其他数据冲突)

(3)根据实际分隔符分段获取 body 内容

(4)遍历分段内容

(5)根据 Content-Disposition 特征获取其中值

(6)根据值中 filename 或 name 区分是否是包含二进制流还是表单数据的 k-v

(7)根据 filename 获取原始文件名

(8)从连续 两个 newline 字符串为起始至当前分段完毕,按照二进制流读取上传文件流信息。

完成后即有——

  • 原始文件名信息
  • 原始文件类型信息
  • 全部文件流信息

然后该干嘛干嘛,比如写文件到磁盘等。

具体怎么处理是服务器做的事儿,HTTP协议本身并没有死规定,以下说的只是解决问题的某一种思路。

有一个基本认识是,每一个请求都是一个流。而每一个用于传输文件的HTTP报文,都会有类似于这样的报头:

Content-Type: multipart/form-data; boundary=巴拉巴拉

如果报头定义了这样的东西,就可以判断客户端采用了multipart格式传递信息,同时我们也拿到了boundray。

再考虑文件如何处理。以问题中提到的报文为例,payload(你题干中的报文格式并不对,我根据题目意思做了相应修改)为:

-----------------------------14579331036932498511351460782
Content-Disposition: form-data; name="userfile1"; filename="备注说明.txt"
Content-Type: text/plain

1.±ê×¢ÒÔiPhone6s ÆÁÄ»³ß´çΪ±ê×¼£»
2.Èç¹ûÐèÒª²»Í¬³ß´çµÄicon£¬ÔÙ¸øÎÒ˵¡£
-----------------------------14579331036932498511351460782
Content-Disposition: form-data; name="hehe"

tewtw
-----------------------------14579331036932498511351460782--

1. 第一次读到定义的边界”—————————–14579331036932498511351460782″

意味着一个字段的开始;

2. 继续读入一行,发现这是个文件;再读入一行,发现定义了Content-Type,也许还会定义charset之类的信息;再读入一行发现是个 CRLF ,意味着后续的内容是文件数据,这时候可以构造一个新的临时文件对象,将后续的数据pipe到这个临时文件对象中。

3. 再一次读到边界”—————————–14579331036932498511351460782″意味着这一个字段结束,这时候可以去关闭刚刚创建的临时文件。

4. 然后开始继续下一字段解析过程。

ps:以上部分只是简单的说了解决思路,并不涉及检查、转换等工作。比如在流的pipe过程中,可能需要根据之前定义的charset进行流的转换,甚至如果发现Content-Type不是自己需要的,就压根不存而是直接pipe到黑洞中去。

编译环境:VS2019 + Win10 + cmake-gui-3.8.0 + cef_binary_3.2623.1401.gb90a3be_windows32

最后一个兼容Windows XP的CEF(2623)的下载地址:https://pan.baidu.com/s/1UoWt8Ffs_YPBCmYlHbipLg

提取码:x7ym

1、解压 cef_binary_3.2623.1401.gb90a3be_windows32 后,目录如下:

2、下载cmake-gui

下载地址:https://pan.baidu.com/s/1KdOaZXWX9gy7yVKVbJdpgA

提取码:ptnu

下载好cmake-gui并安装好之后打开cmake-gui.exe,设置如下:

Where is the source code : cef_binary_3.2623.1401.gb90a3be_windows32解压后的路径

where to build the binaries : cef_binary_3.2623.1401.gb90a3be_windows32解压后的路径

Configure: 选择你电脑上装有的VS的编译器的版本,如果选择了电脑本地并没有的VS编译器版本,会遇到如下情况:

用cmake生成编译工程时候报这样的错误,原因是配置错误导致cmake找不到对应的编译器,于是通过File->Delete cache清理配置,重新通过Configure更换你电脑上装有的VS的编译器的版本即可。

当出现Configuring done的时候点击Generate按钮即可生成对应版本的VS sln解决方案,使用VS打开生成解决方案即可。

VS2015打开cef.sln然后直接编译即可生成libcef_dll_wrapper.lib文件了,如下图项目cefsimple项目和cefclient项目会失败,这个并不影响生成我需要的libcef_dll_wrapper.lib,我就不解决了。

在这里还有一个坑就是这个工具最多只支持到VS2017,由于我的电脑上装了VS2013和VS2019,于是我选择了VS2013的配置并成功编译出了libcef_dll_warpper.lib,但在导入CEF浏览器实际项目调用的时候报了如下错误:error LNK2038: 检测到“_MSC_VER”的不匹配项问题。

_MSC_VER这个相当于做了宏的检测  _MSC_VER 定义编译器的版本。下面是一些编译器版本的_MSC_VER值:
MS VC++ 14.0 _MSC_VER = 1900 vs2015
MS VC++ 12.0 _MSC_VER = 1800 vs2013的编译器他的平台是v120
MS VC++ 11.0 _MSC_VER = 1700 vs2012的编译器他的平台是v110
MS VC++ 10.0 _MSC_VER = 1600 Visual C++ 2010
MS VC++ 9.0 _MSC_VER = 1500 Visual C++ 2008
MS VC++ 8.0 _MSC_VER = 1400 Visual C++ 2005
MS VC++ 7.1 _MSC_VER = 1310
MS VC++ 7.0 _MSC_VER = 1300
MS VC++ 6.0 _MSC_VER = 1200
MS VC++ 5.0 _MSC_VER = 1100

error LNK2038: 检测到“_MSC_VER”的不匹配项: 值“1800”不匹配值“1700”(main.obj 中)
原因:由于你使用了vs2012,工作集选择了更高的1800也就是vs2013的,致使msvc不兼容!
方法:在项目(解决方案资源管理器或者属性管理器里都行)右键属性-配置属性-常规中,平台工具集选用为合适平台即可,比如上面的就是要选择成2012的 v11版本,注意光选了还没有用,还要应用。
注意一个工程里面会有几个解决方案的时候,需要给每个解决方案都更改一遍,最后重新编译即可。

最近在通过OpenHardWareMonitorLib来获得一些CPU和GPU的信息,采用了c++调用c#dll的方法,由于只能传递基本数据类型,所以动态数组考虑到使用String来传递回C++并进行字符串分割,在.net中string是需要用gcnew进行初始化,先来看看gcnew和普通的new的区别:

C++/CLI中使用gcnew关键字表示在托管堆上分配内存,并且为了与以前的指针区分,用^来替换* ,就语义上来说他们的区别大致如下:

1.     gcnew返回的是一个句柄(Handle),而new返回的是实际的内存地址. 
2.     gcnew创建的对象由虚拟机托管,而new创建的对象必须自己来管理和释放.
暂时没有很深入的去理解这些区别。因为需要在c++的控制代码中对c#产生的String^变量进行写出,而默认的文件写出是string类型的,因此需要进行转换。查阅资料发现有人总结了一下较为简单的转换方式:

1:std::string转String^:std::string stdstr=””;
String^ str = marshal_as<String^>(stdstr);

2:String^转std::string:
String^ str= gcnew String();
std::string stdstr = marshal_as(str->ToString());

3:CString转Sting^:
CString cstr=””;
String^ str = marshal_as(cstr.GetBuffer());
cstr.ReleaseBuffer();

4:String^转CString:
String^ str;
CString cstr(str);

1、打开Visual Studio,新建一个C#的Class Library项目(这里选择的是.Net Framework 4),项目名为CSharpDll。

2、由于默认没有引入Forms等UI库,先在reference中添加引用System.Windows.Forms以便可以在测试中使用MessageBox等。

3、最终C#编写的dll的源代码如下图所示,命名空间为CSharpDll,公共类为CSharpClass。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace CSharpDll
{
    public class CSharpClass
    {
        public CSharpClass() { }
        public int add(int a , int b)
        {
            return a + b;
        }

        public void substract( int a , int b , ref int c)
        {
            c = a - b;
        }

        public static void showBox(string str)
        {
            MessageBox.Show("C#:" + str);
        }
    }
}

里面包含一个加法add,一个减法substract(为了测试指针,所以在减法的返回类型是void,而把计算结果通过ref参数c给返回),一个showBox方法(里面采用C#的MessageBox对话框显示用户输入的参数字串)

4、对project进行release build,在release目录下生成了CSharpDll.dll(待会用到)。

5、关闭CSharpDll项目,另外新建一个C++ CLR类型的Class Library项目(选择与C#项目相同的.Net Framework 4),项目名称为CppDll。

一开始我用的VS2019,发现VS2019好像无法新建 C++ CLR类型的Class Library项目了,所以学习微软的技术一定要小心,学习主流的支持很久的技术,尽量不要学习新出的技术,如果必须学新技术,一定要认真考量,一些边缘化的技术一定不要学习,没准哪天微软就不维护了。

6、选择Project->CppDll Properties…,在弹出的属性页面选择“Add New Reference..”,点击“browsing.”后选择CSharpDll项目中release目录下的CSharpDll.dll。

7、选择CSharpDll.dll后,可以看到在项目属性的References中出现了CSharpDll这个Library。

8、在CppDll项目中的CppDll.h中利用_declspec(dllexport)导出对应的3个接口函数add,substract,showBox。需要using namespace System::Reflection,对于这里的showBox,其参数不能采用CSharpDll里面的showBox参数的String类型,而是使用const char* 类型。

主要代码如下所示:

// CppDll.h

#pragma once

using namespace System;
using namespace System::Reflection;

__declspec(dllexport) int add(int a, int b)
{
	CSharpDll::CSharpClass obj;
	return obj.add(a, b);
}

__declspec(dllexport) void substract(int a, int b, int *c)
{
	CSharpDll::CSharpClass obj;
	obj.substract(a, b, *c);
}

__declspec(dllexport) void showBox(const char* content)
{
	CSharpDll::CSharpClass obj;
	String^ str = gcnew String(content);
	obj.showBox(str);
}

namespace CppDll {

	public ref class Class1
	{
		// TODO:  在此处添加此类的方法。
	};
}

9、选择release方式build CppDll项目,在release文件夹中生成了CppDll.dll文件,可以看到同时其也将引用的CSharpDll.dll也给拷贝到release文件夹中了。

10、接下来在Qt中进行调用, 在QtCreator中新建一个TestCSharpDll GUI项目,编译器选的mingw。通过VS自带的命令行工具中的dumpbin工具可以查看CppDll.dll导出的函数接口。

dumpbin -exports (+dll路径)

在TestCSharpDll工程中通过typedef定义函数指针,同时采用QLibrary动态加载并resolve函数。

在这里.dll的路径设为当前目录下“./CppDllMingW.dll”,也就是编译好的程序exe同一目录下的dll,去resolve由普通导出方式的接口即“?add@@YAHHH@Z”。

主要代码如下所示:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QLibrary>
#include<QMessageBox>
typedef int (*x_add)(int a , int b);
typedef void (*x_substract)(int a , int b , int* c);
typedef void (*x_showBox)(const char* content);

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

//add
void MainWindow::on_pushButton_clicked()
{
    int a = ui->lineEdit->text().toInt();
    int b = ui->lineEdit_2->text().toInt();
    QLibrary library("./CppDll.dll");
    if(library.load()){
        x_add add = (x_add)library.resolve("?add@@YAHHH@Z");
        if(add){
            QString str = QString::number(add(a , b));
            QMessageBox::information(this , "call add from dll" , str);
        }
    }
}

//sub
void MainWindow::on_pushButton_2_clicked()
{
    int a = ui->lineEdit_3->text().toInt();
    int b = ui->lineEdit_4->text().toInt();
    int c = 0;
    QLibrary library("./CppDll.dll");
    if(library.load()){
        x_substract sub = (x_substract)library.resolve("?substract@@YAXHHPAH@Z");
        if(sub){
            sub(a , b , &c);
            QString str = QString::number(c);
            QMessageBox::information(this , "call sub from dll" , str);
        }
    }
}

//showBox
void MainWindow::on_pushButton_3_clicked()
{
    QLibrary library("./CppDll.dll");
    if(library.load()){
        x_showBox showBox = (x_showBox)library.resolve("?showBox@@YAXPBD@Z");
        if(showBox){
            showBox("showBox!");
        }
    }
}

编译TestCSharpDll工程,将CppDll.dll和CSharpDll.dll复制到同一目录下,执行TestCSharpDll.exe,可看出点击按钮后,通过QLibrary进行动态resolve,均正常调用。

调用add函数
调用sub函数
调用showBox函数

最好是将相关dll置于同一目录下运行,不然会出现“未能加载文件或程序集”的异常。针对.lib链接方式,理应是置于同一目录下。而针对QLibrary进行resolve方式,可能通常一开始的想法是,CppDll.dll和CSharpDll.dll放在与程序不同目录的地方,程序中利用了QLibrary指定了CppDll.dll的方式进行加载,而CppDll.dll和CSharpDll.dll,因此程序调用CppDll.dll里面的函数时,CppDll.dll会找到与CppDll.dll同一目录下的CSharpDll.dll,然而CppDll.dll在被程序进行加载时,其继承了程序的环境变量,因此会从程序的当前目录下去查找,所以最好还是将CppDll.dll和CSharpDll.dll放置于程序同一目录下,同时QLibrary加载当前目录下的CppDll.dll。当然,部署到另外一台机器上时,目标机器还是需要安装.Net Framework,其版本至少不能低于当前CSharpDll.dll所使用的版本。

C++的进入点是main()吗?

什么代码比main()更早被执行?

在linux下我们可以写一个startup code,通常情况下main()是通过startup code调用的,所以startup code会比main()更早执行,它是C++真正的进入点(entry point)。

下面我们来写一段startup code:

首先创建一个entrypoint.c文件(.cpp也可以),写入我们的startup code。

在这个startup中我们有两个函数,一个blabla()函数,一个main()函数,接下来我们将blabla()函数设置为entry pont(C/C++程序进入点),使用gcc的-e指令进行编译,就可以以告诉链接器使用blabla作为程序进入点,执行结果如下:

我们可以看到,main()函数并没有被startup code调用,所以并没有被执行。

而startup code实际上为一个函数,所以startup code一定是C/C++程序中最早被执行的函数。

而在Windows下的C++程序又分为命令行(console),窗体程序(Win32),以及动态链接库(WinDll),他们的主函数名称各不相同,startup code会争对不同的C++程序调用不同的主函数。

在VC6.0下,默认的Startup code由4个文件组成,分别为crt0.c,wcrt0.c,wincrt0.c,wwincrt0.c,而wcrt0.c,wincrt0.c,wwincrt0.c这三个文件全部include了crt0.c,所以startup code的核心全部在crt0.c中。

我是流浪佣兵。

哪怕随时随地死去,也很正常的人生。

夕阳西下,风吹过衣角的某一天,我的尸体也将如落叶一般,在某个地方飘荡。

也许,那天就是今天。

“全部杀光了?相信你跟随你的那些人全部……?”

“……就这样吗?你所说的理想全部都是假话吗?”

故事要从何说起呢……?

……好吧,就从我第一次听到白魔法师的名字开始说起吧。

我一直都是一个人。

还没懂事前,我就学会了拿武器,为了不饿肚子,哪怕是几分钱,我都必须用命去搏。

我运气好,活得久了点,但很多朋友并没有我这么好运。

当握武器的手上长满厚茧的时候,我终于开始对周围人的死亡无动于衷。

战争和饥饿,掠夺和灾害……世界正在一点点崩塌。

然后那些富人们依然在不断地累积着财富。其中阿里安特的大富豪哈萨尔就是一个。

“呵呵……我说,你不会是哈萨尔派来的手下吧?让你把我安全带回阿里安特。”

“…什么?不是手下,是佣兵?”

“不管是手下还是佣兵,都没差别!总之太好了。你有水吗?我一个人负责这么多行李,完全忘了这回事。”

“我们遭到了怪物的袭击!商团里就只有我活下来了。我静静地装死,然后刚才慌忙打包了一些贵重物品逃出来。”

当时我正在佣兵事务所工作,独自一人接了一单在阿里安特押镖的活,这次要保护的人是巨商哈萨尔的手下。

“怪物的袭击?”我问道。

“是的。是我从未见过的怪物,散发着超级难闻的气味。好像馊掉的味道……直到现在,我想起来还是觉得毛骨悚然。可是,你怎么就一个人?你的同伴们在哪里呢?”

“我独自行动的。太阳落山前赶紧出发吧。”我催促他赶紧动身,我不想浪费太多的时间。

“你说什么,就凭你一个人来保护我?这怎么可能……你不会是说真的吧?”

我没有理会他,准备动身启程。

“不,不会吧…稍等一下!一起走,一起走嘛!”

“你一个人真的能够保护我吗?到处都是危险的怪物啊。”

“出发吧。”

“等等……一起走!”

穿过这条路并不是特别轻松,怪物繁杂多变,是一些从来没有见过的。

我护送哈萨尔的手下一路到了阿里安特的宫殿附近。

“哎哟,真没想到你把那么多的怪物都消灭光了……你,长得挺俊俏的,没想到实力超出我想象啊?真是的,真是个木讷的人啊。不管怎样,我喜欢!虽然不能说是报答,但是,我告诉你一个有价值的消息吧。”哈萨尔的手下凑到我耳边偷偷说。

“哈萨尔最近好像很头疼。不知道从哪儿来的怪物们出现在运输途中,这已经不是一天两天的事情了。”

我也觉得很奇怪,“的确是奇怪的怪物……就好像是死去的家伙又活过来一样。”

“其实这个世界上能够解决这一问题的只有一个人。但是,那个人现在下落不明,所以哈萨尔想要找到他。”

我的好奇心驱使我问了一句:“他是谁?”

“我也不知道,具体的你去问哈萨尔吧。如果你能帮到哈萨尔的话,就能拿到很多钱哦。怎样,对佣兵你来说,是个不错的提案吧?”

我接受了他的委托,见到了巨商哈萨尔。

“原来你就是一个人护卫我的行李马车的佣兵啊。我怎么会一下子就知道了?你身上有味道啊。我对两种味道特别敏感。危险的味道,还有就是钱的味道……你身上有危险的味道。看来你当佣兵已经有不少日子了吧?”哈萨尔开始打量起我来。

“……你是大富豪哈萨尔?你想我从这里得到什么?”我直接开始询问他。

不想说废话浪费时间,直接问。

你想我从这里得到什么?

意外地,哈萨尔好像很满意我无礼的态度,笑了几声,就开始说重点了。

“你帮我找个人。这次的委托不是给佣兵事务所的,而是我交给你个人的。”

我瞪大了眼睛,“找人?”

“你听说过白魔法师吗?”

白魔法师?第一次听说,听听故事吧。

他是一个被称为天才的魔法师。

据说,他年纪轻轻就拥有非凡的魔法造诣,没人能够做他的老师。

努力达到阶梯的尽头时,却发现什么都没有,这种伤心和空虚不可谓不大。

继续听。

想到达更高的地方。

这个世界上已知的魔法,与他的野心相比,实在是微不足道。

几十年间,他带着一根法杖,孤身游荡世界。疯狂地写书,帮助他人,留下了无数的故事。

——白魔法师

拥有纯白头发的他,不知道从何时起,人们开始如此称呼他。

就这样,有一天。

他好像突然领悟了一样,说道。

终极光明只有在终极黑暗中才能找到。

留下这句话后,白魔法师从此失去了踪迹。

他现在在做什么呢?

在喜欢故事的好事者们无尽的猜测中,人们逐渐忘却了他的存在。

“白魔法师曾是冒险岛世界上最优秀的光之魔法师。如果是他,一定能帮我击退妨碍我生意的黑暗怪物们。”

“你觉得那个白魔法师会为你做事吗?“

“哪怕是要花费很多钱,我也要雇佣他。没有人能够顶住金钱的诱惑。如果这次事情成功的话,我会给你足够你挥霍一辈子的钱。怎么样,感兴趣吗?“

“……我只拿该拿的钱。如果找到他,我会和你联系。“

钱不是问题。

关键是我确实产生了兴趣。我开始对叫做白魔法师的人产生了好奇。

他是否也像我一样,感受到了人生的疲惫和空虚?他是否找到了答案呢?

三个月后,艾琳森林。

根据情报,最后一次看到白魔法师是在这里的艾琳森林……在这里一定能找到线索。我去妖精女皇巢穴见了妖精女皇艾菲尼娅,她应该知道一些什么。

“贪婪的人类……真是太可恶了。什么事?我什么都不会告诉无礼的人类。快点给我消失。“我能感受到她见到我的时候的满脸厌恶。

“我想知道关于黑暗怪物的事情。“我不想和她多说什么,直接表明了我来这儿的目的。

“黑暗怪物?那和我们有什么关系!对我们来说,人类比黑暗怪物可怕多了。他们绑架我们族人,偷走我们的宝物!就是为了要钱。“

我并不关心其他人类的所作所为,“那你告诉我一些关于白魔法师的事情吧。“

听到白魔法师的名字,艾菲尼娅的表情变得温和了些。

“白魔法师……?为什么要找白魔法师?他可是我见过的唯一的好人。我喜欢看他专心研究魔法的侧脸……他是理想主义。他的热情超过这世上所有的人。他一定会创造出比现在更好的世界。但是我不会随便告诉别人他的居所的。因为我根本没办法确认你不是坏人。如果你真是善良的人的话,你现在就当场证明给我看。你能去干掉那些挖开妖精们的坟墓,抢走贵重的花的坏家伙吗?都说胳膊肘往里拐,你不会因为同是人类就袒护他们吧?“

没问题,毫不犹豫,也不夹杂着任何感情。为了取得她的信任,我帮她去干掉了那群可恶的盗墓贼。

“你,不是坏人啊。我对你另眼相看了。…这么说来,你的眼神和那家伙的眼神也不是太像。还有比盗墓者更恶毒的家伙们——四处抓捕妖精们的人类。他们只把妖精看作是神奇的商品。替我把那些家伙赶出艾琳森林吧!“

我瞪大了眼睛,“商品?“

“这是人类的本性啊。虽然现在还不敢动到我头上来…但是已经有很多妖精被绑架或者杀害了。那些家伙的最终目标应该是我,抓到我能卖多少钱啊?呵呵呵……这些家伙们真是丑恶至极,没有比它们更可恶的了。“

没问题,在当佣兵的这些日子里,我杀了不少的偷猎者,这对我来说并不是问题。我准备去艾琳森林侵略地去会一会那些邪恶的偷猎者,但在路上我看到了一个拿着枪的小孩。

我朝他大喊:“喂,小孩。“

他好像被我吓了一跳,“喂!干,干什么呀。吓到人了。“

小孩子家家的,居然拿着枪,难道这家伙是偷猎者?

“干嘛盯着我看啊……不想受伤的话,就走你的路吧。我有事情在这里处理。“

我劝诫他:“小小年纪,不要杀生。这可是我的经验之谈,快回家去吧。“

“……家?“

听到小孩失魂落魄的声音的瞬间,我本能地明白了。

……这家伙无家可归。

没什么好说的,这就是生活。

“总之不要把枪口对准无辜的精灵。我就当做没看见你,你快离开艾琳森林吧。“

“你说什么啊?我要抓的不是精灵!我想抓的是……“

我懒得理他,继续去寻找偷猎者,最好不要让我发现他是偷猎者。

我在艾琳森林侵略地教训了一下那些可恶的偷猎者,他们逃出了艾琳森林。

“呵呵……暂时能找到些活着的滋味了。和平真是毫无意义,只有依附于绝对力量的统治,才能救济这种情况。你说过想知道白魔法师的行踪对吧?还记得,他隐居前说的最后的话吗?终极光明只能在终极黑暗中被发现……所以,他去了世界上最黑暗的地方之一——静谧之林。静谧之林是几乎没有白天的黑夜之森林。因此,与其他任何地方相比,那里的光明更为纯粹。听说他在那里秘密研究魔法,这样的情报,应该够了吧?如果你见到他的话,请转达我的问候……另外,告诉他,不要忘了我们的约定,等他完成光之研究后,一定要来到这里,和我一起实现理想。我会一直等他。必须啊,一定要帮我转达。我给你情报,就是为了让你帮我带话给他。“

去艾琳森林的路上,我的好奇进一步扩大。

妖精女皇好像已经完全被白魔法师迷住了。

能够让一族的女王沉迷成这样,他究竟是怎么样的人物?

妖精女皇说,白魔法师将会创造更美好的世界。

……那是真的吗?

现在还不知道。答案在静谧之林。

我在前往静谧之林的路上又看到了那个小孩。

我朝他大声呼喊:“我说,小孩,你怎么还不回去……“

这一次,他没有回答我。

我跑到他跟前,发现他旁边有一只散发着邪恶力量的黑色怪物,那怪物究竟?来不及想那么多了。

“快跑,孩子!“我喊了出来。

小孩一动不动,好像是被吓到了,必须帮帮他才行……!我像农场主杀鸡一样将黑色怪物给打败了。然而,小孩却像丢了魂一样倒在地上。

“喂,你没事吧。“我扶住了他。

“……我没能扣动扳机。妈妈,爸爸……必须……报仇……我怕,怕得没法扣动扳机。“

小孩的名字叫亚林。

那家伙说,刚才看到的怪物就是杀死他家人的怪物。

我干掉的怪物,名字叫做乌曼。

据说,在乌曼出现的地区,没多久就开始陆续出现不死怪物。

也就是说,乌曼才是所有事情的元凶。

“白魔法师……他知道如何解决吗?“小孩问道。

“我不知道。总之只能先找找看。“我摇摇头,叹了口气。

“你不知道的还真多,大人们不是所有事情都知道吗?“

“……你,是佣兵吧?请你教教我打架吧。我要变得和你一样强大。“

“快睡你的觉吧。“

当我正准备离开这儿前往静谧之林的时候,小男孩发话了:“你丢下我这是要去哪里?一起走,佣兵。“

“危险。别跟着我。“

“哼,从我下决心要报仇那刻起,我就知道这是件危险的事情。而且你不觉得,把我一个人放在这里,更加危险吗?大人们真是笨。“

我不理他,继续往前走。

“你在做什么,佣兵!要想彻底消灭乌曼的话,不是要找到那个叫做白魔法师的人吗?我也要亲自见见那个人。“

没办法,我只好带上他一同前行。

找到白魔法师的话,真的能解决这儿所有的事情吗?

这一切都不确定。

答案就在静谧之林。

三个月后,我们找到了静谧之林。

这里是静谧之林……几乎从来没有太阳的森林。也许这里适合研究魔法,但我肯定的是,这里找路可是最不方便的。

“……迷路了啊,佣兵。“小男孩开始不耐烦了起来。

“我也知道,别发牢骚,安静点。“

“你要干什么?在这里迷路的话可怎么办。就这样,能找到白魔法师吗?“

“在这等会儿,我去找路。“

“让我自己等着?……你不会是要丢下我跑掉吧?你敢逃走试试,我一定会追上你,用枪打穿你的身体……我在这里休息。“

我没理会他,径直去了森林的东边找路。

“完了……好像彻底迷路了。这么下去怎么能找到白魔法师呢?……不行。在走得更远之前,还是先回去吧。“我自言自语。

这时候森林中传来了几声枪声,总觉得不安,我快马加鞭地赶往小孩所在的地方,路上却出现了许多长着猪鼻子的蝙蝠阻拦我的去路,我一路杀了过去。

失误了,怎么会把孩子独自留在那里呢……当我回到孩子所在的地方时,我看见了4只乌曼围在亚林的四周。

“!!孩子,小心!“

“乌曼居然这么多……这次真的报仇……!“

那个傻孩子!距离太远了,以我的速度,还是迟了……!

“给我去死!“突如其来的光之魔法将乌曼全部消灭了。

“傻小子,这是做好了死的打算了吗!?“我问道。

“……刚才发生了什么事?有道光……“

“没事吧?已经很久没在这里看到外人了。”黑暗中走出一位白发的翩翩少年。

白色长发。

智慧的眼神,令人信赖的声音,我一眼就看出来了。

他就是我们一直在找的白魔法师。

我将这段时间发生的事情告诉了白魔法师。

他好像听到了什么了不起的大事一样,不时慎重地点点头。不过这好像是长久以后形成的习惯。

“原来如此……艾琳森林里居然有偷猎者和盗墓贼。贪婪的人类居然把手伸到了这里。“白魔法师惊叹不已。

“妖精女皇已经对人类失去了信心。但是好像唯独非常喜欢你。“我轻声说。

“我和妖精女皇有过一个约定,直到现在,我仍然在为遵守这个约定而努力。“

“佣兵!魔法师!请稍等,我们一起走?“小孩儿赶了上来。

“对了,那孩子?“白魔法师突然问道。

“……他的家人被乌曼夺去了生命。“

“看来刚才看到的黑暗怪物的名字叫做‘乌曼’吧。在这里,没有办法及时知道外面的消息。“白魔法师若有所思。

“其实我最想问你的问题是这样的。听说你是天才魔法师?你到底一个人在这森林里做什么?“

“我在研究光。“

“我想听的答案不是这个。我……“

想再说点什么,但又闭上了嘴。

虽然他并没有什么错,但不知道为什么,我很生他的气。

我不过是一介佣兵,可你不是啊?

你拥有能够拯救世界的力量。

不,最起码能让世界变得好一些。

可你却悠哉悠哉地躲在这里,一个人做什么研究?

那样的话在嘴里转了又转。

但是白魔法师,他好像看透了我的心,露出微微的笑容。

“虽然我有很多话想要说,但是首先来纠正一件事。我并不是‘一个人’“。我还没开口,白魔法师先说话了。

“等等!一个家伙这样,两个家伙也这样,为什么走这么快?!“亚林在后面追赶着我们。”呃,呃。腿疼……还要走多远啊?“

“快到了。那里就是我们的家,欧罗拉大神殿。“白魔法师将我们带去了他宏伟壮观的神殿。

“大师,这些人是……?“一位年长的大叔向白魔法师询问起我们来。

“马尔斯,这些都是远道而来的客人。给他们准备住所吧。“

“啊?但是大师……他们不是外来的人吗。“

“不是添麻烦的人。暂时先让他们住下吧。“

马尔斯来给我们做自我介绍了。“我的名字叫马尔斯,是研究光魔法的研究团队欧罗拉的首席魔法师。虽然不知道你是怎么找到这里来的……拜托你,希望你不要妨碍这里的研究。尤其是右边的大师的研究室,没有特别的事情的话,那里是禁止出入的。我们为客人准备好了休息的房间,在左边。和你一起来的孩子好像很疲惫的样子,去休息一下吧。“

他领着我和小孩去到了左边休息的房间,让我们好好休息。

“哎哟,真是好不容易有得休息了。看上去什么都没有的森林里还有这么像回事儿的建筑,很不错嘛?“

我警告他:“你刚才差点儿没命了。以后千万别草率行事。“

“我也知道。可是,只要能杀死一只乌曼,就算我明天死了,我也没有什么好遗憾的。“

“明知道对方的实力远远超过自己,还以命相拼,那是愚蠢之人才会干的事儿。“

“哼,说教……就凭你一个佣兵。你又不教我打架,少跟我说这样那样的。可是,这森林里怎么有这么多乌曼啊?是因为太黑了的缘故吗?“

我也不知道为何在这个森林里有如此多的乌曼,但我心里隐隐有一种不详的预感。

“啊,对了,佣兵!我有东西给你。你到我这来一下。”

“这是我收集的乌曼的残骸。虽然不太确定,但是魔法师们也会用这个做实验的吧?说不定这对研究乌曼的真实身份有所帮助呢。帮我转交给白魔法师,如果是那个人的话,就能让这世上所有的乌曼都消失了吧?对吧?”

“没问题。”我接过他递过来的乌曼的残骸,想不到一个小孩子竟然能这么心细。

我跑去了白魔法师的研究室,轻轻地敲了敲门,并没有得到回应。看来白魔法师并不在这里,看来是出去了。

“什么?你在找大师?”首席魔法师马尔斯发现了正在敲门的我。“我帮不了你。如果没有什么重要的事,不要接近大师的房间!!呃,嗯……以后注意吧,大师在神殿的屋顶。”

这魔法师还挺敏感的。屋顶……要不要上去看看呢?

我在屋顶找到了白魔法师并将乌曼的残骸递给了他。

“乌曼的残骸……那种情况下,这孩子还能想到收集这些,真是个聪明的孩子。请转告他,我会好好用来做实验的。”白魔法师用手帕将残骸小心翼翼的包裹了起来。

“无法知道乌曼是如何出现的。但是请不要担心,终极光明研究结束时,乌曼一定会不见的。”

“那个所谓的研究主要是在屋顶上进行的吧?”我很好奇。

“不是的。这里只是我最喜欢的地方罢了。”

如此说着,他的眼神望向远处的某个地方。

我一下子又好奇起来。

抛弃原本的荣华富贵,他到底要追求些什么?

“你相信神明吗?如果你不相信神明的话,你相信什么?有些人相信金钱,有些人相信权力。但是在所有东西都变成浮云的死亡瞬间,你相信什么呢?”

“我在这里研究的东西……它并不是简单的‘力量’。而是存在于安息地平线那边的无限的知识。能够使我们变得更完美,能够在我们所居住的这个世界,再现神的城市的本源的智慧。”

“那才是我所追求的东西。”

我试着想象了下他所说的本源的智慧。

而且我还想象了一下他所说的神的城市。

如果人类能达到那种境界的话……应该也能够创造出没有伤心,没有苦痛的世界吧。

“……我想要翻过那道墙。”

“我既不是贤者,也不是哲学家。”

“只不过,是好奇光的那头会是什么罢了。”

大致和白魔法师进行了简短的对话。但是和他对话后,有一种进行了长途旅行的感觉。自那以后,我在欧罗拉大神殿待了一段时间,帮助白魔法师进行研究工作。

就那样,不知不觉,就过了三个月。

从未想过以后会发生什么事……

直到有一天,那个叫亚林的小孩子跑过来找我。

“嗯,佣兵。听说你最近在帮助做研究?又是一个白魔法师的崇拜者。刚才一个叫飞鱼丸的人在找你呢?我不相信那个叫白魔法师的人。总是说些人听不懂的话……最近天天关在研究室里基本都不出来了。我也不知道为什么就是不喜欢他。”

我没有理会他,跑去大厅见到了飞鱼丸。

飞鱼丸看到了我,和我问好。“啊,你好啊。今天也来帮助大师做研究吗?现在你应该能记住我的名字了吧?我的名字叫飞鱼丸,欧罗拉的低级魔法师。虽然现在还是低级,但是将来的事谁都说不准,不是吗?哈哈哈。这里总是缺人手。做魔法研究,主要需要的是和光有关的物质,你今天能去狩猎些发光蝙蝠,收集些研究材料来吗?”

“没问题!”我答应了他。

于是我出去寻找发光蝙蝠并按照他的要求狩猎了一些发光蝙蝠并收集好了残骸——黯淡的发光物质回来交给他。

“谢谢。你帮了我们研究员很大的忙。我真希望我能拥有大师的一半的一半的一半的知识。如果可以的话,你能接受马尔斯的委托吗?虽然我知道二位的关系不是太好。哈哈哈。”飞鱼丸挠了挠头笑着说。

“这么说来,最近好像都没看见白魔法师呢。”我问道。

“是的,一个月前开始就日夜不停地做研究。最近白魔法师的研究室除了首席魔法师马尔斯谁都不让进了。那么,你去找找马尔斯吧……试着让彼此变得更亲近些吧。说不定你们两位会成为莫逆之交呢,人生是无法预知的喜剧,不是吗?哈哈哈。”飞鱼丸笑着说。

“没问题。”

我跑过去和马尔斯打了个招呼。

“有什么事吗?今天也来帮助我们研究吗?我完全不知道你心里打着什么算盘。待在这里究竟对你有什么好处呢?”

“……当然啦,虽然从我们的立场来说,没什么不好的。”他补充道。

“在神殿后面的森林,会有些奇妙的石头。敲开那些石头就会发现一些不知何物的光块。你能帮我从那些光块里弄些格拉希姆来吗?对于大师来说,财富和名誉都不重要,他是超脱于浑浊的俗世之人。”

“没问题。”

就这样,我按照他的要求收集了很多格拉希姆交给他,当我回来的时候,他郑重其事的和我说:“其实,你不在的时候,那个叫亚林的小孩来找过我。可是,我有点不放心……那小孩子特别憎恨乌曼,一天来找我好几次,问我何时能让乌曼消失,实在有些心烦,于是我就直截了当地告诉了他……但是这事一直让我有些放不下。对小孩子说话太重了,心里有些后悔。你能替我把这个给他吗?”

说罢马尔斯递给了我一颗星星模样的糖果。

“小孩子喜欢吃甜的,应该能让他心情大好吧?”

“没问题。”我收下了糖果,回到房间想将糖果交给亚林,哄他开心一下。

但是我却没有在房间里看到亚林,仔细以看,在枕头上发现了一张字条。看着上面写的歪歪斜斜的字应该是亚林留下的。

给佣兵

刚才听一个叫做马尔斯的大叔说。

他说像乌曼这样的家伙不是什么大问题。因为欧罗拉要追求更大的真理,所以没办法在乎那种小问题。

我不知道更大的真理究竟是什么东西,我的目标就是消灭乌曼。

直到现在,我一想起爸爸妈妈死去的那天,还无法入睡。

这才不是小问题。

即使只有一个人,我也要对附近展开调查。

从上个月开始,这片森林里出现了相当多的乌曼。

别太担心。

不过也许我就再也回不来了,所以先说一下。

这段时间谢谢你。

——亚林

这个傻孩子……!!

“那……那是什么?我从未见过那么大的乌曼。”

“……!!不会吧?出事了,必须要快点去告诉佣兵……”

亚林遇到了足足有一棵树那么高的乌曼,黑不溜秋,恐怖至极。

快去找孩子吧,现在应该还没有走远。

当我找到亚林的时候,我足足杀了15只乌曼。

“佣兵……快跑。危险。”亚林无力地倒在地上对我说。

“乌曼已经都被我消灭了,放心。”我试图让他冷静下来。

“不是说这个……是至今为止都没见过的……巨大的乌曼……佣兵,快离开‘那个地方’……”亚林失去了意识。

那个地方,什么意思……?

我没有思考那么多,先把他带回了神殿里,让他好好休息一下。亚林的状态不是很好,等他状态好转吧。

他昏迷了足足一整天都还没醒来。

“睡着了吗……究竟要救你几次才行啊,小家伙。”我埋怨道。

亚林呼吸还算正常,但严重发烧,现在只能等他好转了。也许……不,不要想得那么悲观,先看看情况吧。

就这样又过了一天。

“很难醒过来啊……现在几点了?再看看吧……一定会醒过来的。”我这样安慰自己。

时间变得很缓慢。

看来,小孩最后是想告诉我什么。

那是什么呢?

乌曼……还有一个月的时间……

……!!

瞬间,脑子一下子清醒了。

开始整理所有的信息。

这片森林里怎么会有如此多的乌曼?是因为太暗了吗?

从上个月开始,这片深林里的乌曼开始大量出现。

白魔法师从一个月前加快了研究的进度。

至今为止,从未见过的,巨大的乌曼……

佣兵……快点逃跑。

“……怎么会,白魔法师不会这么做的。”

“但是……”

白魔法师是几年前开始在欧罗拉展开魔法研究的。

乌曼也是在几年前开始出现的。

白魔法师锁住研究室的门,开始埋头研究是在一个月之前。

附近的乌曼数量增多也是从一月前开始。

怎么会直到现在才发现呢?

……突然想到白魔法师对我说的话。

“——我是希望能翻过那道墙的人。”

终极光明只有在终极黑暗中才能发现……这是什么意思呢,白魔法师?

全身的血液都冰冷冰冷的。…当然不能乱猜测,但是确实不对劲。

如果我的推测是事实的话,必须马上阻止白魔法师的研究。但是,在那之前我想和本人见面确定一下。白魔法师的研究室的门上了锁,这把锁只有首席魔法师马尔斯才能打开,于是我去大厅找到了马尔斯。

我告诉他我想见一见白魔法师。

“啊?你想见大师?那可不行。大师正在潜心做最后的研究呢。”

“对不起,绝不能妨碍大师的研究。如果现在打扰他的话,我们所付出的努力,可能都会化为乌有。你为什么一定要马上见他呢?”马尔斯讲我拦住了。

我将我大脑中的推测一五一十地告诉了他。

“……!!你说什么?刚才说什么?”看得出来,马尔斯很惊恐。

“白魔法师必须立刻停止研究。乌曼是白魔法师过度研究而形成的影子。只要他能够终止现在的研究,起码事态不会继续恶化。”我说出了我的想法。

“请不要继续猜测了,不会发生这样的事情!”飞鱼丸走了过来。

“如果我错了的话,请你反驳我看看,马尔斯。”

“……我知道事实。”马尔斯神态凝重地望着我。“据我所知,你提到的‘乌曼’……那是大师研究的副作用的产物。看到和你一起来的孩子转交的物质,一下就明白了啊。”

“怎么可能……首席魔法师,那是真的吗?”飞鱼丸睁大了眼睛。

我终于压抑不住内心的怒火了,大声地吼了出来:“白魔法师自己知道这一事实吗?……应该知道已经有人因为乌曼而失去了生命。即便如此,白魔法师还继续研究……?”

但马尔斯仿佛早就知道会有这么一天,他非常镇静地和我们坦白:“这是无可避免的牺牲。光变强的话,影子自然会加深。把现在这混沌的世界作为祭礼,为了能够创造出更加美好的世界……那就是大师的想法。所以才会加快研究进度的。”

“……对不起,我想要继续相信大师。”马尔斯闭上了眼睛。

“无可避免的牺牲……简直可笑。这样的话你能够对着那边屋子里躺着的孩子说吗?快把那扇门打开,否则我就要用武力打开了!”我大声呵斥道。

马尔斯不紧不慢地说:“那我只能拦住你了。”

果然是大师虔诚的追随者,虔诚是什么?虔诚就是无条件的相信自己的信仰。

“不,不要。请停止战斗,两位!”飞鱼丸站在了我和马尔斯的中间,拦住了我们。

就在这时,白魔法师的研究室中传来了奇怪的声音。“外面吵吵什么呢。”

听到门那头传来的声音,起了一身鸡皮疙瘩。

那是我所认识的白魔法师的声音吗?

“我成功了,也失败了。违背禁忌,坚持埋头进行光之研究,终于成功越过了那道墙。但是……没有终极光明。不是因为我没能找到,而是一开始就不存在。因为只要有光,就必然有黑暗。”白魔法师奇怪的声音继续从室内传出。

“……然而终极黑暗是存在的,这就是我的结论。”

仿佛时间凝固了一般,大家都愣住了。

还是马尔斯先缓过神来。“……!?!大师,那是什么?”。

我:“立刻把门打开,马尔斯!”

马尔斯赶紧掏出钥匙打开了那扇门,我们一同冲进了那扇门。

……!!

仿佛扎根在地板上一样,无法动弹。

本能地明白了那里的所有状况。

‘那个’可以说不再是人类。

“……哈哈,大师?!这是什么……?如果你是和我们开玩笑的话,就此打住吧。一点都不好玩。”看来飞鱼丸至今为止都认为这只是白魔法师的一个玩笑。

“……不要,飞鱼丸!都躲到外面去!!!”马尔斯冲我们大声呼喊。

“白魔法师,住手!现在还能回头!”我试图阻止白魔法师。

“太迟了。”白魔法师笑了起来。

……传来雨声。

身体……还能移动。

我晕过去了多久?

当我醒过来的时候,外面电闪雷鸣,神殿已经一片狼藉,像刚进行过一番战斗之后的场景,马尔斯倒在大厅中。我努力支撑着身体,一步一步的走向马尔斯。

马尔斯倒在地上无法动弹,仅剩最后一点意识。

“咳……咳咳……竟然发生了这样的事……”

我问他:“白魔法师究竟发生了什么事?”

“研究终极光明,结果被那力量吞噬了。”马尔斯有气无力地说。

“光的力量深而广,是我们所不能想象的。相比之下我们就像是想要伸手触摸天空的小孩,就像是茫茫大海的被抛弃的一叶小舟般无气力…要是在那里就收手就好了…”

“不知从何时起…大师意识到自己犯了禁忌。可即使知道了他的研究方法有问题,他还是不顾一切地继续研究…而我也选择继续相信他,我很好奇他的研究将带来的崭新未来。光的另一面我们究竟会看到什么呢。”

“现在后悔太晚了……咳咳,咳咳。拜托你,请阻止大师吧,现在也许还能挽回。”

“啊……眼前什么都看不到。谁在那里?”飞鱼丸还没死,他倒在大厅的门口,但好像已经无法睁开自己的双眼。

我缓慢地走了过去:“我是佣兵。”

“原来是你啊。哈哈……我说什么来着?人生就是伸手不见五指的戏剧。光之魔法师,竟然被抢走了光,这就是我最后的结局……咳咳,咳咳。”

我看飞鱼丸情况非常不好,好像很难活下去了。

“凯特……对不起,还有我们还未出生的孩子……”

飞鱼丸的呼吸变淡。

他死了。

我哭着冲出神殿。

当我看到神殿外面全是尸体的时候,我后背一阵发凉。

……全部杀死了?相信你跟随你的所有人……?

……就这样吗?你所说的理想全部都是假话吗?

我往外跑,想找到白魔法师的背影,但一路上我只能看到数不胜数的极光研究员尸体,我不知道他们是怀着什么样的心情死去的。

终于,我看到在森林的深处有什么东西在发光,光是看着,就觉得眼睛得到了净化,是非常纯净的光块。在这个漫无天日的森林里,即使是很微弱的一点光,也会非常刺眼。

这是我至今未曾见过的完美的光之结晶。相比之下,现存的所有光芒之中都好像蕴含着黑暗似的……这光……是白魔法师的痕迹吗?连留给自己的最后一束光都要带走吗?

光之结晶好像马上就会消失一样,微弱地发着光。我仿佛看到了曾经纯净的白魔法师最后的模样。

……这前面究竟是什么?

好可怕。真想现在就逃离这里。

但是不能那样。

如果真的能阻止他的暴走的话,那么现在是唯一的机会了。

“白魔法师!你在哪里!快出来!”我在森林里大声呼喊白魔法师的名字。

就在此刻,一团黑影出现在我的面前。

无法继续挪动脚步,最终冻住了。

其实一开始就知道。

无法对抗他的力量。

但即便知道会死,我还是去追他了。

因为我想亲眼看到结局。

白魔法师。

……不,看到你的样子,就不能再这样称呼你了。

……黑魔法师。

好,来吧,我不会逃走,就算是牺牲我的生命,我也要给你留下永远忘不掉的伤口。

“喂,孩子,你打算跟着我到什么时候?”

“佣兵,我们做彼此的家人好吗?”

“……什么?

“你不觉得寂寞吗?就算你从这个世界上消失了,也不会有人记得你。所以,让我们成为彼此的家人吧,我们互相记着对方,怎么样?”

“……”

“切……开玩笑啦,佣兵。我才不相信这个呢。”

呼……我发生了什么事吗?身体无法移动……我死了吗?

眼前变得昏暗……这里。

我是流浪佣兵。

哪怕随时随地死去,也很正常的人生。

夕阳西下,风吹过衣角的某一天,我的尸体也将如同落叶一般,在某个地方飘荡。

但是如果说生命的尽头,有什么迫切的愿望的话……

虽然我即将死亡,但如果说在人生尽头,有什么迫切愿望的话……

白魔法师……

不,黑魔法师。

我的后代一定要阻止那个恶魔。

“喂,佣兵!醒醒!不要死啊!”

……哈哈……太好了,小孩……你还活着啊。

“不是说让我珍惜生命嘛!不是说即便对方很强也不要随意放弃生命嘛!为什么傻瓜一样地追过去!”

啊……

不行,说不出话来。

小孩的声音也慢慢远去了。

……看来,雨停了。

再次提起冒险,我已经不知道用什么心情去描述了。只是依稀记得,昏黄的天空渲染着周边,绿荫之上一群飞鸟悲鸣着。

双击游戏图标,拖着疲倦的双腿行走在荒无人际的市场上,寂静而悲凉。这让我想起了那荒废的青春,支离破碎的生活,破灭的梦想。霎时间,空白的思维慢慢地压住了神经。

曾经,冒险的路途如此漫长,闭上双眼竭尽全力的努力奔跑。而如今,我只是一个失利者,不错,失利者的我已经是一无所有,连我拥有的青春也消散了。

满目萧然,记忆里承载的大片大片悲伤纠缠不清,数不清的哀愁依托给黑暗,同黑暗分享斟酌。

对于冒险,我只想说:不知道是你抛弃了我,还是我遗弃了你。对你,早已没有当年的那份激情,同样你也只是客串在我的生活里。也许是我不仁,在我恐慌的日子逃避了你,也许是你不义,把最深的刺痛带给了我,一切的一切无从得知。

蓦然发觉,无法泯灭的记忆被岁月的灰尘覆盖了。岁月的流逝,或多或少我们都会丢失掉一部分的记忆,某些人、某些事,即使让我们多么刻苦铭心,多么难以忘怀,最终,它们都会被岁月的巨大洪流冲洗殆尽,不复存在。

夜里,一个人跑地图,去遍了自己喜欢的地方。我想追回往事,可无法追回,也没地方去追寻。原来,到头来我们都会败给时间。我厌倦那种升级打怪一成不变的无味生活,每天我都慵懒得不像话,坐在市场聊天,听着人家讲故事,偶尔逛逛市场……

我不知道自己是怎么了,突然的想哭却哭不出来,想笑也笑不出来,总会莫名地感到悲伤、空虚,对什么事情都提不起劲。

对着电脑久了就想睡觉,躺在床上就又开始无止境的遐想,一到夜晚就失眠,难以入睡。

我在拼命寻找一个能给自己温暖的地方,让疲惫的心有个归宿。彼时,试图找寻一个能理解包容我的人,让阴郁的天空变明朗。

一直一直听着带有淡淡忧伤的音乐,直到耳朵生疼才停止。冒险岛似乎真的远去了,那些花开的声音、绽放的瞬间却永远历历在目。

Maplestory,不要再问我曾经的纯真与梦想,那些都是我无力保护的东西。呐喊、哭泣、软弱,全部随着夕阳的晕染跌落在了遥远的地平线,消失不见。

Maplestory,你是否也早已明白,那些盛开在你年华里遥远的黑夜,是你无论如何坚守,也回不到的过去。

在一段段的章节里,填满了干涩的甚至略微僵硬的记忆。浅薄的流年中,我们学不会——己所不欲,勿施于人。

我们跌跌撞撞迷失在流年里,我的记录、我的沉默、我的思念、我的信仰、我的微笑、我的眼泪,全部在这一刻凝聚。

听,Maplestory,花开的声音!