【问题标题】:Is this memset-memcmp on a struct variable valid C?结构变量上的这个 memset-memcmp 是有效的 C 吗?
【发布时间】:2018-06-08 04:36:49
【问题描述】:

memset 结构体设置为某个值是否合法,然后将其与memcmp 进行比较?

struct S {
    // struct definition not relevant, but it has bitfields
};

struct S invalid_S;
memset(&invalid_S, 0xFF, sizeof invalid_S);

struct S value;
memset(&value, 0, sizeof value); // actual data read would be here

if (memcmp(&invalid_S, &value, sizeof(struct S) != 0) {
    /// operate on fields of value
}

struct S value2;
value2 = invalid_S;

if (memcmp(&invalid_S, &value2, sizeof(struct S) != 0) {
    /// operate on fields of value, which doesn't happen now
}

上述代码行为是否已明确定义、未定义或实现指定?以上代码的有效性是否取决于struct S

使用 0xFF 填充结构,然后将其与 memcmp 进行比较的原因是:我有一个函数,它返回一个与我从硬件设备读取的内容相匹配的位域结构,没有浪费位或字节,并且我想要一种有效的方法来报告错误(设备永远不会返回所有 0xFF 字节)。我有固定的平台和工具链,代码现在可以工作,但是如果我提高优化级别,我可以相信它不会中断吗?


结论:虽然如果我确保没有填充位、浮点字段等可能有问题,则可以使此代码工作,但我决定改为将一个特定的结构字段设置为特定的“不可能”值以指示错误。

【问题讨论】:

  • 你不能相信跨平台的是位域的确切布局:""关于位域的几乎所有内容都取决于实现" K&R 第 150 页。
  • 您的问题太笼统了,无法得到有用的答案。您应该解释您使用的实际编译器和平台,以及您的 struct S 到底是什么。
  • @BasileStarynkevitch 是的,但我决定采用替代方法,希望是稳健的方法。

标签: c undefined-behavior unspecified-behavior


【解决方案1】:

将一个结构memset到某个值,然后与memcmp进行比较是否合法?

是的,因为所有memsetmemcmpmemcpy 都被记录在任意内存区域上工作(假设传递给它们的指针和大小指向一个有效的内存区域)。

但是,在某些情况下,这可能没有意义。例如,如果您 memcpy 来自一些 未初始化 内存,您将在目的地获得垃圾,而使用该垃圾的可能是 undefined behavior

您正在使用 memset0xff。理论上,您可能有一些实现,其中 char 大于 8 位(但实际上不会发生,所以您不在乎)。

理论上,您可能有一些具有整数值陷阱表示的实现。实际上,这不会发生。

如果您在具有padding 的结构上使用memcmp,则可能无法按预期工作。您可能想深入了解您平台的 ABI 规范以了解它们是如何实现的(并且位域可能未在您的 ABI 中指定并且是特定于编译器的)。

我相信,在实践中,您需要使用特定编译器很好地了解特定 struct 的确切布局。看起来您的代码可能是特定于硬件的,那么您就不太关心可移植性。所以在实践中你的struct S非常相关的(也许避免其中的位域会“更安全”)。

【讨论】:

  • 请注意,结构中的填充字节(或位)具有不确定的值,虽然 memset() 调用不是问题,但 memcmp() 调用因此不可靠。
  • @JonathanLeffler:使用其类型修改结构的操作可能会任意更改不代表结构成员的字节的值。另一方面,如果代码使用 memcpy 制作结构的两个副本而在此期间没有对其进行任何其他操作,我认为应该保证副本比较相等,除非或直到其中一个(或两个)被修改。
【解决方案2】:

你说函数返回一个“位域结构”。如果你真的返回一个结构,即按值返回一个结构,那么不,不能保证行为是你想要的。复制结构时,实现只需要复制其成员中的值,而不是其表示中的实际字节。

这同样适用于您的value2 = invalid_S; 行。

【讨论】:

  • 你的意思是,例如,如果有填充位,它们的值可能会改变?还是其他/更多?
  • @hyde:填充位和字节将是实现可能不会复制的最常见的东西。但是类型可以具有以多种方式表示的值。例如,理论上,数字 .3 可以用浮点表示法表示,相当于 3*10**-1 或 30*10**-2。也就是说,两个不同的字节模式可能都代表 0.3。当 C 实现复制一个值时,只需要生成相同的值,而不是相同的字节。因此,复制内部为 30*10**-2 的 .3 可能会产生 3*10**-1。
  • 对。由于这是已知的平台并且只有整数(代码需要一些工作才能移植),所以这里只对填充位感兴趣。唉,我认为对真正模糊的未来错误的担心已经足够了,我会拒绝这种方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-22
  • 1970-01-01
相关资源
最近更新 更多