最近有个bug实在让人抓狂,仅仅是由于一个简单的类型强转导致的,这里给大家讲讲。
这是pwrite系统调用的接口说明,我们关注最后一个参数:
ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
我们有个接口实现是:
int storage_write_meta_info(int fd, void *data, int unit)
{
...
off_t offset;
int length;
ssize_t n;
lenght = sizeof(struct xxx);
offset = unit * length;
n = pwrite(fd, data, length, offset);
...
}
运行环境是CentOS x86_64,off_t的实际类型是long,8字节。程序在跑了很长一段时间后,遇到pwrite返回了-1,errno为(22: Invalid argument)。有经验的同学可能已经看出了问题:
当两个int类型相乘(即代码中的unit * length)发生溢出时,最后的结果已经产生,并截断为int类型保存在某处。这样这个被截断的错误值就赋给了offset,两者的相乘行为完全无视左值offset的类型。怎么处理好呢?只需要对其中任何一个相乘的变量做一个强转,即:
offset =
(off_t)unit * length 或者 offset = unit *
(off_t)length 或者 offset =
(off_t)unit *
(off_t)length
不过之前的代码pwrite就没有报错(但不一定就是正确):
offset = uint * sizeof(struct xxx);
因为sizeof的返回值是size_t,它的真面目是unsigned int,这里相当于换成了int * unsigned int,运算的结果被看做是一个unsigned int类型,碰巧没有越界罢了。
所以这里告诫大家,在程序里面涉及很多不同类型的参数之间转换的时候,一点要谨慎,小心。最好的建议就是别整那么多类型!