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

Using memset() on struct which contains a floating point number

在一个 C/C++ 混合项目中,我发现了一些可以减少到

的代码
#include <mem.h>

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

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

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

Using memset() on struct which contains a floating point number.

消息是正确的,但是由于浮点数被声明为双精度,所以它似乎是误报,因为在双精度中,(正)零值是按照 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 不保证任何特定的浮点实现。

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

[basic.fundamental]

There are three floating point types: float, double, and long double. [...] The value representation of floating-point types is implementation-defined.

因此,memset-ing 浮点值在 C 或 C++ 中不可移植。

有问题吗?是的。不,不是真的。一个很遥远的也许。

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

所有具有 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 成员。然而,未定义的唯一可能原因不是因为块归零不是零,而是标准的其他一些优点没有完全实现。

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

因此,IMO memset to zeroes 是安全且可移植的,即使标准说浮点数是实现定义的,因为我不知道任何现有的实现,其中零表示除零之外的其他东西,而且这样的实现是极其不切实际的.

实际代码用于在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);
}