【问题标题】:Using memset() on struct which contains a floating point number在包含浮点数的结构上使用 memset()
【发布时间】:2018-01-10 21:32:56
【问题描述】:

在一个 C/C++ 混合项目中,我发现了一些可以简化为的代码

#include <mem.h>

struct StructContainingDouble
{
    double d;
    /// other elements omitted
};

void clear(StructContainingDouble* p)
{
    memset(p, 0, sizeof *p);
}

不停止 Cppcheck 以引发可移植性警告

在包含浮点数的结构上使用 memset()。

消息是正确的,但是由于浮点数被声明为双精度,它似乎是误报,因为双精度中的(正)零值是按照 IEEE 754 标准编码的:[*]

0 00000000000 0000000000000000000000000000000000000000000000000000

所以我倾向于简单地压制警告并忘记它

void clear(ContainingDouble* p)
{
    // cppcheck-suppress memsetClassFloat
    memset(p, 0, sizeof *p);
}

但也许这里真的存在可移植性问题?

附录:

实际代码基于Win32平台。该结构用于管理对shared memory 的访问,这就是构造函数无用的原因。不仅是该结构中的一个对象必须归零,而且它的数组也嵌入到另一个结构中,如下所示:

#include <mem.h>

struct Slot
{
    double d;
    // more members...
};

struct SharedMem
{
    Slot slots[2048];
    // more members...
};

void clear(SharedMem* p)
{
    memset(p, 0, sizeof *p);
}

[*] 来自:Double-precision floating-point format - Wikipedia

【问题讨论】:

  • C 语言不保证 IEEE 754 浮点数。
  • @interjay 很有趣,但是 C++ 呢?是否有一些包含它的 C 标准?就我而言,它是一个 C++ 项目,其中包含一些旧的(样式)C 代码。
  • C++ 也不保证 IEEE 754。
  • 1) 只有一种 C 和一种 C++ 标准。 2)没有“C(C ++”语言。它们是不同的语言,对于相同的语法/名称,您不能假设相同的行为。在C中没有可移植的方法将除整数类型之外的对象归零。使用“零-结构”和memcpy.
  • 这可能是因为人们可能会根据您的实际操作提出替代方案。你没有列出其他成员。您知道nullptr 的位模式也不必为零吗?但我再次不敢相信 Windows 机器会出现这种情况。归零只能(完全可移植)初始化整数类型。

标签: c++ c double portability cppcheck


【解决方案1】:

实际代码用于在Windows(Win32)下初始化共享内存。这意味着代码很可能会被移植(如果有的话)到具有适当 IEEE 754 支持的某个平台。

这就是为什么我建议将不可移植性结合起来

  1. 使用WinBase.h 中定义的ZeroMemory macro(通过Windows.h)明确声明您没有尝试将一些花哨的位模式存储到浮点数据中。

  2. 在调用之前禁用 Cppcheck 的可移植性警告。

换句话说:

#include <windows.h>

// ...

void clear(SharedMem* p)
{
    // cppcheck-suppress memsetClassFloat
    ZeroMemory(p, sizeof *p);
}

【讨论】:

    【解决方案2】:

    我认为在这种特殊情况下对可移植性的考虑太过分了。我一生中(我从事编程工作 30 多年)没有见过任何硬件或 C 实现,其中零意味着零以外的东西。当然可以创建零不是零的任何数字格式(例如 BDC 用 ASCII 数字代码表示的数字 - 但这里不是这种情况),但我们讨论的是真实的现有硬件和软件。

    因此,即使标准说浮点数是实现定义的,IMO memset 为零也是安全且可移植的,因为我不知道任何现有的实现,其中零意味着零以外的东西,而这种实现将非常不切实际。

    【讨论】:

    • 当我检查出来时,使用ZeroMemory 会是更好的选择。但可悲的是,这也引发了 Cppcheck 中相同的可移植性问题。 (没有零值的memsetting会有问题)
    • 会的。由于另一个答案和 cmets 中所述的原因,这是正确的。设置浮动占用的内存绝对是一个UB,但在现实世界中再次为零是IMO不是。但是必须发出警告。
    【解决方案3】:

    有问题吗?是的。不,不是。可能是一个非常遥远的地方。

    正如 interjay C 在 cmets 中指出的那样,不强制浮点值的格式。所以理论上“零”可能是无效值或不代表零。

    如果有 FPU,所有现代平台都会实现 IEEE 754。任何不会在其库中实现 IEEE 754 的(可能是嵌入式)系统(除非它是退化的实现并且根本没有浮点......)。

    因此,虽然不能保证您在这里遇到问题的可能性很小。 在某些平台上存在关于它们如何精确地符合 IEEE 754 字母的细节问题,但之前曾问过但从未得到任何答案,我会很高兴看到 memset() 没有带零的双精度的平台将其设置为零值。

    也就是说,如果您开始切分,请不要忘记字节顺序,但零也不是问题。

    请记住,C 已经接近 50 年了,而现在几乎通用的东西(例如一个字节是 8 位)还没有确定下来,我认为为了提供最大的支持(不要忘记嵌入)它是正确的从未致力于这些标准。

    在偏执模式下,你可以选择:

    memset(&obj,0,sizeof obj);
    #ifndef __STDC_IEC_559__
        obj.dbl_val=0.0;
    #endif
    

    其中dbl_valobjdouble 成员。然而,未定义的唯一可能原因不是因为块归零不为零,而是标准的其他一些细节没有完全实现。

    【讨论】:

    • 有趣:我正在考虑对 IEEE 支持进行编译时检查。而且实际单元的便携性要差得多,因为与 Win32 共享内存处理相结合。
    • 不是 IEEE 的 Win32 平台?我不知道它在任何地方都有定义,但那将是一种非常罕见的野兽。
    【解决方案4】:

    已经提到 C 不保证任何特定的浮点实现。

    嗯,C++ 也不行。不需要浮点值的具体实现。

    [basic.fundamental]

    共有三种浮点类型:float、double 和 long double。 [...] 浮点类型的值表示是 实现定义。

    所以,memset-ing 浮点值在 C 或 C++ 中都是不可移植的。

    【讨论】:

    • 我认为这取决于目标硬件平台对 fp 数的支持。
    • @Serge No. 一些硬件不支持的平台,在软件中实现 IEEE 754 算法。
    • @Serge 这正是可移植性的意义
    • 看来我不得不接受这个事实。至少对于“桌面应用程序”来说,是否可以合理地假设使用memset 是可以的?在我的实际情况下,它在 Windows 下初始化共享内存(便携性要差得多) - 所以我可能会更好地保持代码原样。
    • x86 原生支持 ieee754,因此可以安全地假设它可以跨 x86 平台移植。不知道其他人。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-16
    • 2010-11-05
    • 1970-01-01
    • 2015-09-29
    • 2019-12-04
    • 2023-01-14
    • 1970-01-01
    相关资源
    最近更新 更多