【C++】巨坑-VC++的localtime_s的三宗罪

(点击上方公众号,可快速关注)

前几天在运行一段代码的时候,发现localtime执行失败,由于代码没有判断返回值,后续对空指针操作导致段错误。所以,需要对该段代码增加保护判断,避免程序崩溃。由于后面的代码需要拷贝返回的struct tm,所以理所当然地采用了localtime_s改写,噩梦由此开始。。。

代码如下,我们会在localtime_s执行失败的情况下返回false

// Windows下的源码
struct tm tmp = { 0 };
time_t t = xxx;
if(!localtime_s(&tmp, &t))
{
    return false;
}

这类带后缀_s统称安全函数,是微软发明创造,并推荐加入C标准库的。讽刺的是,VC++也是微软家的,但竟然跟标准差了那么多。

第一宗罪

VC++的localtime_s参数顺序跟标准不一致。

VC++版的函数签名:

errno_t localtime_s(    struct tm * result,     const time_t * time);

标准版的为:

struct tm *localtime_s(
    const time_t *restrict time, 
    struct tm *restrict result);

顺序正好相反。但好在两个参数的类型不一致,C/C++的类型系统是可以检出这个错误的。

第二宗罪

VC++的localtime_s返回类型跟标准不一致。

VC++版返回errno_t,实际上是int的别名,函数执行成功时,会返回0,否则返回非0,所以判断失败的代码为:

if (localtime_s(...,...))
{
    //失败
}

而,标准版返回struct tm *,成功时为有效的struct tm指针,失败时返回null指针,对应的失败判断语句:

if (!localtime_s(...,...))
{
    //失败
}

两者的判断逻辑正好相反,这是大坑啊,而且编译器也帮不了忙。所以文章一开始的代码在VC++下是有问题的。

实际上,我躲过了第一个坑,栽在了这个坑里头。

第三宗罪

Windows SDK不同版本localtime_s行为也有差异

以下代码在VS 2012里(Windows SDK 8.1)执行没问题,在VS2019(Windows SDK 10,准确点是10.0.19041.0)执行失败。

struct tm tmp = { 0 };
time_t t = INT_MAX;
localtime_s(&tmp, &t);

特别说明:这个行为的差异具体是哪个版本引入的、以及临界值为多少,我没有进一步测试。

教训总结

  • 不要太相信标准,存在”特立独行“的一些例外

    这里仅捉到了localtime_s,其他带_s的函数有没有类似的问题,我也没进一步挖掘,大家遇到这类函数一定要小心。

  • _s后缀函数虽然加入了标准库的扩展,还没得到GCC、Clang等编译器的支持。

    所以能不用就不用了,这样还能降低入坑的可能;可以用localtime代替localtime_s,虽然”不安全“,但基本上所有的编译器都支持。

喜欢我的文章,请关注我的公众号。转载请标明出处。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值