Categories

Calendar

July 2017
M T W T F S S
« Jun    
 12
3456789
10111213141516
17181920212223
24252627282930
31  

久别重逢的 std::bad_alloc

久别重逢是说,自从在教科书上见过它一面之后,这才是第二次碰面。也就是说,在这些年的编程经历中,从来没有遇到过吧——至少在我印象中是这样的。以至于我都开始怀疑在“平常的”程序中,它是否真正存在了。内存分配,C 里的 malloc (或者配套的函数) ,如果分配失败了会返回地址 0 ,所以,“作为良好的编程习惯,每次申请内存之后,应该检查一下返回值是不是 NULL ”,这样的“良好习惯”也许刚开始写几个程序的时候还能坚持,到后来就完全不管了——因为从来没有遇到过 malloc 返回 0 的情况,申请内存怎么会失败呢?如果连内存都申请失败了,那接下去估计也没有什么好做的了,估计系统已经处于崩溃边缘了,与其每次都费力去检查,还不如让它自生自灭好了——反正之后如果尝试去访问这个 0 地址,肯定会碰到段错误 (segment fault) 而挂掉的,当然,一个不好的地方可能就是这个挂掉的位置和最初申请内存失败的位置已经相差了十万八千里,可能追踪起来会比较麻烦。

至于 C++ 里,就更简单了,new 的时候如果申请不到那么多内存的话,会抛出 std::bad_alloc 异常,如果没把这个异常接住,让它一直跳到最顶层的话,程序会立即挂掉。比 C 更加“人性化”——当场挂掉,而不是在某个未知的其他地方 segment fault 。如此一来,就更加熟视无睹了。

总而言之,渐渐地有了这样一种印象:像内存申请失败之类的情况,大概只有在嵌入式设备等非常极端的资源匮乏的平台上编程的时候才会碰到吧。结果这次却在一个内存很大很大(256G 物理内存)的环境里遇到了。果然是和车祸之类的类似,越是在看上去很太平的路段,越会让驾驶员掉以轻心呀。

巨大的 Matlab 存储数据

好吧,我的标题越来越土了,本来想取个“XX之迷”或者“走进科学”啥的,不过其实是个小问题。总之,最近我在用 matlab 跑一些实验的时候,保存的结果比较大,其实也不算太大,就几百兆,但是由于有很多个这样的文件,所以占用空间还是比较大的,而且几百兆的 .mat 文件每次 load 进来画个图之类的也要等半天,非常不方便。于是我就想能不能有方法处理一下。

一开始我以为是因为我存储的数据里有一些位图,所以才会比较大,于是我写了一个工具来将位图压缩了一下,发现单张图片即使是无损 JPEG 压缩也可以变得比原来小很多,于是赶紧用来处理原来存储的文件,结果发现处理之后基本上没有任何改观。由于很久没有遇到这么神奇的“bug”了,我比较兴奋,把代码相关的地方都检查了一遍,发现其中有一个中间计算结果作为 cache 放在一个 struct 的 field 里,而这个 cache 理论上是可以丢掉的(虽然要再算一遍很耗时,但是基本上不会再重跑这个实验了),于是我果断把它咔嚓掉。再一运行,保存……结果发现居然一丁点改观都没有!

更为诡异地是,我用 whos 看 matlab 里那个 struct 占用的空间,得到的结果其实是很小,但是当我 save 它到文件的时候,它就莫名其妙地占用了大量空间。 -.-bb 我用一个赶紧的 matlab workspace 把这个文件加载进来,逐一删掉其中的变量做测试。

其实一开始我也比较怀疑其中的一个 function_handle 的 field ,我在想不会 matlab 聪明到为了保证 function handle 下次加载进来能够运行,把所有相关的代码和引用到的代码也一应打包了吧?-.-bb 但是 Matlab 的文档说得很清楚就是在其他地方 load function handle 的时候,如果原来的代码文件不存在或者被修改过的话,就会发生 unexpected 结果。但是呢,当我删去 struct 的那个 function handle 的时候,竟然真的就瞬间从几百兆变成几兆了。

虽然是大跌眼镜,但是我突然一下子又想明白了。原来那个 function handle […]

Acrobat meets Embedding

今天遇到一个非常 weird 的问题。实际上,这个问题已经困扰了我好几天了,那就是我发现我系统里的 Acrobat ,打开 PDF 文件的时候有时候需要“打开两次”才能真正打开,就是双击一下没有反应,要双击第二下才会出现 Acrobat 窗口。令我困惑的是并不是总是这样的,而是“偶尔出现”,实在是让人摸不着头脑。

今天又碰到了这个问题,终于忍无可忍,打开任务管理器观察一番,发现第一次双击的时候确实会出现 Acrobat.exe 这个进程,但是窗口并不显示出来,第二次双击的时候就可以了。当然,如果有其他 Acrobat 窗口打开着,总之后台如果有一个 Acrobat.exe 进程在运行着的话,就一切正常。

我很无语,正要去学校的论坛上抱怨一番,琢磨着怎么描述我的问题,想起来这个问题似乎并不是总是可以重现的,但是为什么今天一直都可以确定地重现出来?于是又随便找了桌面上一个 PDF 打开,一切正常。然后我把桌面上这个 PDF 拷贝到刚才我试验的那个目录下面,果然,问题又出来了。我觉得无比诡异,Acrobat 总不会是会栽在路径中有空格或者路径太长这种古老的错误上吧?于是我把那个文件一层一层地往上移动,再尝试,最后发现它放在一个叫做 “Manifold Embedding” 的目录下面,就会出事,我甚至把这个目录移动到其他地方,也都是这样,比如 “C:\Manifold Embedding\” 。我就尴尬了,Acrobat 怎么可以如此? -,-bb

最后再尝试一番,发现罪魁祸首是 Embedding ,嗯,只要是在一个叫做 Embedding 的目录下就不成。Acrobat 可真是奇怪的癖好,实际上,在命令行下面输入

acrobat.exe Embedding

就有这个效果——在后台打开一个 Acrobat 进程,但是如果参数是其他的东西,甚至是小写的 embedding ,就会是另一个效果——窗口显示出来了,不过有一个错误对话框,说“打开本文档是发生错误,无法找到本文件”,这才是正常的行为嘛。甚至是 fooEmbeddingbar 这样的参数也会被识别出来,要说这是一个命令行参数实在是说不过去。找另一台装了 Acrobat 的机器尝试也是这样,搞不明白,这货究竟是个 bug 呢?还是个隐藏 feature 啊?

ps: 我被迫把我的 Manifold Embedding 目录改了个名字…… =.=bb

名字空间的问题

昨天晚上回到寝室,发现室友都没有在玩游戏,而是关灯睡觉了,很是惊奇,后来才发现原来寝室没有交电费给停电了,我倒是觉得挺开心的 -.- ,至少这样可以早睡了。于是也整理睡觉,正要关机的时候收到湖边的短信,让我帮忙调一段很短的汇编代码,其实我汇编已经几乎忘光了,连 mov 的方向在两种汇编下面分别是怎样的都分不清楚了,不过既然是很短的,那还是看看能不能帮上忙吧。于是第二天一大早就起来,然后就邂逅了这个让我不断地以为自己肯定是还没有睡醒的 bug 。代码是嵌入在 C 语言里的,大概是某个题目吧,要求用汇编来实现把小写字母转换为大写,也就是实现注释中那段 C 语言的功能:

CPU 风扇的问题

在上次讲调试的小课堂里我曾经提到过我前一阵子遇到过一个 Matlab 的问题,具体症状是,开启 Matlab 来做计算,过了大约一天以后,再去看,就会发现系统处于完全无响应的状态,完全挂掉了,键盘有一个灯在闪烁。

碰到了好几次之后我相当崩溃,这个 bug 几乎处于不可调试的状态,首先要重现 bug 就很麻烦,虽然几乎都能重新,但是我不可能一直坐在那里盯着屏幕等一天(具体需要几个小时来重现我也不太清除),而且 bug 出现之后系统会挂掉,唯一的办法是强制重启,这个时候所有现场都被销毁了。另一方面,阅读代码的必杀技也没法用,因为我的程序是一段不长的 Matlab 代码,在这样的脚本语言里,内存都是自动管理的,我能想到的危害最大的操作大概就是申请超大内存了,一般会有两个结局:如果 swap 空间还能承受得了的话,会使用 swap 空间,并导致系统奇慢无比,不过并没有挂掉;如果 swap 空间都不够用了,那么 Linux 系统会把这个进程 kill 掉,而不是同归于尽。总的来说,不太可能是这段 Matlab 脚本导致了这么严重的问题,如果真有问题的话,估计是出在 Matlab 了,并且还是非常强悍的 bug ,会让系统彻底挂掉。

MSTC 小课堂之 C 语言调试技术

一方面现在越来越大类培养了,另一方面我们自己的审视标准也在上升吧,总是有一种现在小课堂参与率不够高的一种“错觉”,反正觉得有些劳民伤财。所以,一方面现在我们开始降低小课堂的成本(比如,省略掉横幅这个较昂贵的宣传方式),另一方面尽量让主题更加大众化一些。

比如上次 quark 的关于 Word 的小课堂,就效果不错。说起来当时听说 MSTC 居然要讲 MS 的技术了,一些老朋友还深表诧异! 而这次我来讲的 C 语言调试的课堂,其实主要是想讲调试的,但是还是在前面加了个“C 语言”就是要显得大众一点,C 语言现在是很多专业都要学的课嘛。

Symlink with Directories

Linux 下的软链接 (英文通常称作 symbolic link, soft link 或者是 symlink) 是非常实用的东西,我想任何经常在 Linux 做事情的人都会时不时地会用到这个东西。相比起硬链接来说,它要更加灵活一些,限制也更少,而相对于 Windows 下的那种“快捷方式”来说,它又更加底层一些,处在文件系统层面,很多操作(例如打开文件 open )都会自动 follow 链接到指定的文件,因此它对大部分应用程序可以说是完全透明的。虽然如此,也有一些特殊的地方对于把 symlink 当作“自己本身”来处理还是当作“它所指向的源文件”来处理却并没有一个定论的。

for vs. each

虽然这个问题我是在 Python 里遇到的,但是用 Ruby 解释起来比较容易一些。在 Ruby 里,遍历一个数组可以有很多种方法,最常用的两种无非是 for 和 each:

arr = [’a’, ‘b’, ‘c’]
 
arr.each { |e|
puts e
}
 
for e in arr
puts e
end

通常我比较喜欢后者,似乎因为写起来比较好看,不过从效率上来说前者应该会稍微快一点,因为后者实际上是在遍历的过程中对每个元素都调用一个 lambda 函数来做的,虽然一般情况下并不明显,不过设置上下文并调用函数确实是有开销的,特别是在动态语言里面(不考虑 JIT 内联优化的话)。不过这次的问题并不是性能。然而确实跟“ each 对每个元素都会新建一个 scope 而 for 则不是”有关。

(\d+|\d*\.\d+)

我在之前的 blog 上专门有一个分类叫做 Bug Archive ,用于记录自己平时碰到的应该记录下来的 bug ,现在我觉得这个分类大概还得继续下去吧。因为记录的目的是希望自己以后不要再犯同样的错误,所以通常实际记录下来的其实也都是非常低级的 bug 了。毕竟复杂的 bug 一方面本身也比较难重现,再者遇到了也不是很羞愧的事情。

但是低级的 bug 就不一样了,低级的 bug 并不是指它本身很低级,而是对于你来说不应该会犯的错误——比如,在你最熟悉的地方出现问题。就像今天要说的这个 bug 一样。我想,如果你熟悉正则表达式,大概在打开本文之前就看出来标题里的正则表达式有问题了;如果一开始没有看出来,现在我告诉你它有问题,你再去看一眼,应该也知道问题的所在了。当然,如果你对正则表达式不熟悉,那也只能说这个问题对你来说不是低级的 bug 。

GoDaddy: No Number in Name!

I spent the whole morning struggling with GoDaddy’s payment page. I bought my domain name from GoDaddy. All of my friends suggest it. Maybe because it’s DNS service is stable? Or just all others sucks more? But one thing I have to say is that the website of GoDaddy is really very very bad. It’s […]