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

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

本文关键字:memset 结构上 包含 浮点数      更新时间:2023-10-16

在 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 平台。该结构用于管理对共享内存的访问,这就是构造函数无用的原因。不仅该结构的一个对象必须归零,而且嵌入在另一个结构中的数组,如下所示:

#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);
}

[*]来自: 双精度浮点格式 - 维基百科

有问题吗?是的。不,不是真的。也许非常遥远。

正如interjay C在评论中指出的那样,C并不强制要求浮点值的格式。因此,从理论上讲,"零"可能是无效值或不表示零。

所有现代平台都有 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 内存设置为零也是安全且可移植的,因为我不知道任何现有的实现,其中零意味着零以外的其他东西,并且这样的实现将极其不切实际。

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

好吧,C++也没有。不需要浮点值的特定实现。

[基础.基础]

有三种浮点类型:浮点型、双精度型和长型双精度型。 [...]浮点类型的值表示形式为 实现定义。

因此,memset浮点值在 C 或 C++ 中都是不可移植的。

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

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

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

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

换句话说:

#include <windows.h>
// ...
void clear(SharedMem* p)
{
// cppcheck-suppress memsetClassFloat
ZeroMemory(p, sizeof *p);
}