2010年十月月 发布的文章

由bool类型引发的一个问题

C99 原生支持布尔类型,类型名字为_Bool。对C程序员来说,这个名字有些“不伦不类”,还好一般C标准库 实现的头文件中都用宏bool来替代_Bool。C99虽说是C语言当前的最新标准,但是它也有10年历史之久了。据说C1x标准 正在讨论制定中,有兴趣的朋友可以到标准C工作组 官方站点上去瞧瞧。

有些跑题了^_^!其实这篇文章想说的不是C1x标准,而是一个与布尔类型有关的问题的分析解决过程。

上周为项目的复用库增加了一个小功能,对外表现形式就是一组函数。使用lcut 对这组函数进行了详尽的单元测试 ,所有用例都顺利通过。今天和一位同事交流后,觉得应该对这个功能作些改动,针对一些异常情况作些完善。修改方案很简单,就是在一个外部可见的结构体里增加一个表示当前状态的布尔类型的字段,然后在各个函数接口中设置该字段的值并根据该字段的值做相应的处理。

按照既定的思路修改后,原先的用例依旧可以全部pass。继续修改单元测试代码,增加针对此次改动的用例。编译并运行测试,这次则没有那么幸运-有几个用例失败了。查看失败原因,确有一两个是因为逻辑上的问题导致。

修正后,继续运行测试,依旧有两个用例无法通过。 仔细查看了一遍库代码以及单元测试代码,没有发现明显的错误。将LCUT_TRUE断言换成LCUT_INT_EQUAL断言,重新运行测试,发现期望值为true的断言,实际值却是一个-3146789这样的大数。看到这种情况我的第一反应是:是不是内存被污染了?比如代码里有内存覆盖或Buffer溢出的情况。又仔细浏览了一遍代码,依旧没有发现蛛丝马迹。采用gdb 单步执行测试程序,无奈lcut采用了许多回调函数,导致在gdb中无法追踪到我期望的符号。未果后,我尝试换成最原始的增加打印日志输出的调试方式,终于发现了问题端倪。

具体是这样的:在库代码和测试用例代码中,我都输出了bool类型的size,但结果却大相径庭。库代码中输出的sizeof(bool)等于1,而在测试程序中输出的值却为4,这个长度差异直接导致了前面的-3146789的出现。

这里我要补充一下,C99的布尔类型(bool)在stdbool.h中定义:#define bool _Bool(注: _Bool是原生的),这只有在C99下才生效。考虑到有些编译器不支持C99或默认语言标准不是C99,为了兼容,自定义了一份bool的定义,并通过预编译宏与标准定义隔开:

#if __STDC_VERSION__ >= 199901L
#include
#else
#undef  bool
#undef  true
#undef  false
typedef enum {
        false,
        true
} bool;
#endif /* __STDC_VERSION__ >= 199901L */

难道原因是库中代码采用了标准bool类型,而测试代码中采用的是自定义的bool类型?似乎没道理啊。突然间看到了屏幕上编译测试代码的gcc命令行输出:
gcc std=c99 -c -o testall.o testall.c … (后面还有较长的链接库的参数,这里省略)
gcc -o testall testall.c …

怎么对testall.c编译了两次呢?测试代码目录下的Makefile并没有包含第一个gcc命令啊,又翻了翻顶层目录下的Makefile,我找到了答案:原来顶层目录下的Makefile中采用了如下脚本:
OBJ = $(SRC:.c=.o)
${OBJ}: ${SRC}
    ${CC} ${CFLAGS} -c ${SRC}
脚本根据.c文件替换获得.o文件名,并在同名.o和.c间建立依赖,这样所有.c文件都会被先编译为.o文件。

不过第一次编译的结果显然做了无用功,因为第二行命令执行后会覆盖第一行命令生成的.o文件。但恰恰第二行gcc命令中没有加入-std=c99的编译选项,导致testall.c这个编译单元中的bool使用了自定义的bool,导致了其长度为4个字节。

真相终于大白,就是因为testall.c所在目录下的Makefile编写时忘记添加-std=c99选项才导致了上述的问题。又检查了一下其他的存放单元测试代码的目录,发现所有Makefile都存在此问题。以前在没有使用bool类型时这样的Makefile是不会有问题的,关键就是这次我们用了bool类型,问题才暴露出来。

使用C语言,你就不得不常常与指针内存问题、编译器或链接器 问题做斗争,其中的痛苦你最清楚,但处理这些事的过程中所蕴含的快乐也只有你自己最能体会到。继续痛并快乐着吧!

你好,TeX

由于某种原因,上周末开始学习使用TeX进行文档排版。哦,当然不是直接使用Donald Knuth他老人家设计的最原始的TeX命令。经过这么多年的发展,TeX领域早已出现了各种各样基于TeX开发的层次更高、易用性更好、更加让作者关注内容的好工具。在Ubuntu下,我选择了"TeX Live"。

周末的时间比较零碎(有了果果后,除了晚上外白天很难拿出一长段的时间钻研些东西了),TeX Live安装和体验的过程也是一波三折。TeX Live支持多种安装方式,起初我选择了网络安装,但TeX Live的网络安装程序需要下载2000多个小文件,我的1M宽带实在是太慢,遂放弃。替代的是下载TeX Live的iso文件,这个iso居然有1.9G,着实让我吃惊不小。虽然只有一个.iso文件,但下载过程耗时估计与网络安装也没差多少。不知等待了多久,TeX Live 2010 iso终于下载完毕。安装步骤参考了网上的一些资料,大致是:
1、安装perl-tk,TeX Live的图形化安装需要这个包的支持
sudo apt-get install perl-tk
2、挂载iso,执行安装程序
sudo mount -o loop texlive2010.iso /cdrom
sudo /cdrom/install-tl -gui

Tex Live包确很庞大,完全安装后要2.5个多G空间,无奈本子磁盘太小,需要对其做些裁减,在安装窗口中,点击"Language Collections"按钮,在打开的对话框中取消除中文和英文以外的所有语言包,另外相关文档也只要中英文的,其他语种的一概取消安装。这样才能节省几百M的空间。TeX Live的默认安装目录是"/usr/local/texlive",我的根目录所剩可用空间已经不多了,不想将TeX Live安装到默认目录下,遂找了另外一个FAT32分区,建了一个texlive目录,修改了Tex Live的安装路径,点击“安装”。让人失望的是这次安装失败了,安装日志提示在操作某目标文件似乎没有某种权限,很是疑惑,命令前面已经sudo了,问题无法解决。将安装目录改回默认目录,再次尝试安装,这次倒是很顺利,安装后我的根目录下只剩下3G的空间了:(。

哦,似乎还忘记了一点:
在安装前务必将"创建指向系统目录的符号链接"这个选项置为"是/Yes",这样安装程序会自动为你设置好各种TeX
Live的环境变量,省去之后的很多麻烦。

3、测试安装结果是否正确
TeX Live的官方指南(通过texdoc
texlive-zh-cn打开)中给出了测试安装是否成功的步骤,这里就不赘述了。测试通过后,你将得到一份用Tex排版后的文档:sample2e.pdf,你可以同时打开sample2e.tex文件与sample2e.pdf做一下直观对比,了解一下各种语法宏的作用。

如果你只是做纯英文文档的排版,那你大可到此为止,无须继续向下看了,因为后面要说的是如何让TeX Live支持中文。

TeX Live支持中文排版的方式有多种,初学起来很容易被一堆概念和工具包名搞得晕头转向(直到目前,我也只是了解些皮毛)。这里固定选择一种方式:使用xelatex命令+xeCJK宏包组合。xelatex命令用来编译用LaTeX格式写成的tex文件,并支持Unicode编码以及直接访问系统字体。TeX Live 2010包中包含xelatex和xeCJK宏包,这样无须我们单独下载安装了。

开始测试TeX的中文支持,下面是一个包含中文字符的tex源文件:
%HelloTeX.tex
\documentclass{article}
\usepackage{xeCJK}
\setmainfont{SimSun}
\begin{document}
你好,TeX!
\end{document}

使用xelatex命令编译该tex文件 – xelatex HelloTeX.tex,执行结果如下:
……
Output written on HelloTeX.pdf (1 page).
Transcript written on HelloTeX.log.

xelatex将tex直接转换为pdf格式输出,并将转换过程的log输出到HelloTeX.log中了。用文档查看器打开HelloTeX.pdf,发现中文字符显示为乱码,看来这次转换并未成功。打开HelloTeX.log尝试分析一下转换日志,发现有这样的错误信息:
Invalid UTF-8 byte or sequence at line 5 replaced by U+FFFD.
Invalid UTF-8 byte or sequence at line 5 replaced by U+FFFD.
Missing character: There is no ? in font SimSun/ICU!
Missing character: There is no ? in font SimSun/ICU!

xelatex居然在HelloTeX.tex中发现了非法UTF-8字节!突然恍然大悟,我的VIM配置文件中将文件内码设置为GBK了,这样HelloTeX.tex的数据存储内码是GBK,而不是UTF-8,而xelatex似乎默认采用UTF-8分析.tex文件,不出错误才怪。

临时修改.vimrc,重新编辑HelloTeX.tex(或用iconv将HelloTeX.tex从GBK编码转换为UTF-8编码),重新执行xelatex HelloTeX.tex,这回成功了,"你好,TeX!"被正确输出到pdf文件中了。

xelatex应该也支持解析GBK内码文件才对,翻了翻网上资料,果不其然,通过增加一行\XeTeXinputencoding
"GBK"即可告知xelatex这个tex文件用的是GBK编码:
%HelloTeX.tex
\documentclass{article}
\usepackage{xeCJK}
\XeTeXinputencoding "GBK"
\setmainfont{SimSun}
\begin{document}
你好,TeX!
\end{document}

在体验TeX Live期间还遇到了xelatex找不到系统字体(如SimSun)的情况,后发现我的系统的确没有安装这些字体,Ubuntu 10.04安装字体似乎很方便,将simsun.ttc从Windows系统的"系统盘\Windows\Fonts"目录中copy出一份放置到你的~/.fonts下面即可,然后你通过"fc-list :lang=zh-cn"命令查看系统已安装的字体列表,字体Copy前列表中没有SimSun,Copy结束后我们就发现SimSun的踪影了:"宋体,SimSun:style=Regular"。

想用好TeX Live,不看Manual是不行的,如果你和我一样采用xelatex和xeCJK的组合,那么XeTex(texdoc xetex)和xeCJK(texdoc xeCJK)的Manual是必许要通读的。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats