多进程下载歌词尝试失败
回归多进程下载模型

一个类型提升引发的bug

Tiger Soldier posted @ 2010年1月08日 02:05 in 程序设计 with tags osd-lyrics c , 3598 阅读

今天OSD Lyrics收到了一个issue,说是在歌手不存在的情况下xmms2搜索歌词成功,下载歌词失败。之前在Exaile下也有类似的bug,不过这个bug的问题不同(应该说Exaile下也还是会有这个问题)。

OSD Lyrics对歌词文件名的查找是基于模板的,模板中有一些占位符,例如%t代表歌名,%p代表歌手。有一个函数ol_path_get_lrc_pathname,负责给定路径模板和文件名模板以及歌曲信息,输出根据模板转换后的完整文件路径。成功时返回输出的路径长度,失败则返回-1。

但是在程序日志中发现,当歌手为空时,文件名模板“%p-%t”应该是失败的,然而却依然被当作成功来使用。这样一来,匹配的结果就成了只有目录,缺少文件名的路径,把歌词下载的目标地址设成本机的一个目录,那肯定是会失败的嘛。

把ol_path_get_lrc_pathname相关的函数里外查了一遍,没有任何问题,出错时也的确返回了-1,于是目光落到调用这个函数的地方:

if ((ol_path_get_lrc_pathname (
     path_list[i], name_list[j], info, 
     file_name, MAX_PATH_LEN)) >= 0)
{
  /* do something */
}

发现把>=0改成!=-1就正常了。

为什么会这样?原因就在于ol_path_get_lrc_pathname的返回类型是size_t,而size_t在我的系统里的定义是unsigned int。任意一个unsigned类型都不会小于0。

那么为什么改成!=-1后,反而又可以了呢?任意一个unsigned类似应该都不会等于-1呀。

这就涉及到C语言的类型提升问题了。在C语言中,那个操作数的类型不同时,会把操作数都转化成两个操作数中能表示的范围最大的类型。就表示的类型来说,shot<=int<=long<float<double,这个是没有任何问题的。问题在于,当出现了unsigned后,怎样计算。具体来说,两个操作数是unsigned int 和 int时,如何转化?

如果是unsigned short int和short int就好办了,直接转成int就行。然而很可惜,在32位机子里,int = long,把unsigned int和int转成long不能解决任何问题。

怎么办呢?很简单,两个都转成unsigned int。

在这个例子中,两个规则是一样的,因为返回的-1变成了size_t后,变成了最大的unsigned long,所以-1也跟着被转成了unsigned long,变成了0xFFFFFFFF,两个刚好又相等了:)

作为一个例子,请看下面一段代码:

#include <stdio.h>

int main()
{
        unsigned long a = 0;
        long b = -1;
        printf ("%s\n", a > b ? "Greater" : "Less");
        return 0;
}

会输出什么呢?gcc 4.4.1给出的答案是Less,因为-1被转型了:(

 

 

 


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter