【问题标题】:Does dereferencing an argument create a local copy of it?取消引用参数会创建它的本地副本吗?
【发布时间】:2019-08-24 15:18:42
【问题描述】:

请考虑以下代码:

void bar(Thing* thing) {
    some_buffer[some_index++] = *thing;
}

void foo(void) {
    Thing thing;
    bar(&thing);
}

some_buffer[some_index++] = *thing; 是否创建原始thing副本

就内存而言,这段代码是否保证安全?因为foo 中的thingfoo 退出时显然已经死了。

【问题讨论】:

  • 代码错误,因为没有Thingsome_buffersome_index的定义...

标签: c pointers memory


【解决方案1】:

取消引用参数会创建它的本地副本吗?

下面的代码概念上创建一个本地副本然后复制。发出的代码可以在一个步骤中简单地读取和分配,而无需中间的本地副本 - 或者可能不 - 取决于编译器。

some_buffer[some_index++] = *thing;

这段代码是否保证安全

不,不确定。

Thing 未初始化,因此根据其定义,代码可能有陷阱或 UB。最好发minimal reproducible example

由于some_indexsome_buffer 的数据位置未知,可能会以某种奇怪的方式重叠thing。这要求some_buffer[some_index++] = *thing; 考虑到这一点。研究restrict 来否定这种可能性。更好:避免使用全局数据。

否则,看起来还可以。

【讨论】:

  • Per 6.2.6.1.6 "结构或联合对象的值永远不是陷阱表示,即使结构或联合对象的成员的值可能是陷阱表示。",所以分配一个未初始化的结构类型对象永远不会陷入或有未定义的行为。
  • @ChrisDodd 是的,但Thing 可能不是struct,因此陷阱 仍然存在。不幸的是,OP 没有提供minimal reproducible exampleThing thing = { 0 }; 会搁置这个问题。
  • 但是,根据 6.5.16.1.3,“如果存储在一个对象中的值是从另一个对象读取的,该对象以任何方式与第一个对象的存储重叠,那么重叠应该是准确的并且这两个对象应具有兼容类型的合格或不合格版本;否则,行为未定义。”,因此如果可能重叠,那肯定是一个问题,但不需要编译器提供任何额外的东西。
  • @ChrisDodd 正是 OP 的“保证安全的代码”让我开始担心这些附带问题。在本地查看 some_buffer[some_index++] = *thing; 是一回事,因为看不到 通常 问题。我正在对整个代码进行限定 - 假设剩余的看不见的代码可能会呈现“不,不确定”的“保证”评估。感谢您的引用。
【解决方案2】:

some_buffer[some_index++] = *thing; 是否会创建原始 thing 的副本?

是的。它创建*thing 的副本并将其分配给some_buffer[some_index],然后递增some_index

就内存而言,这段代码是否保证安全?因为foo中的thingfoo退出时显然已经死了。

是的。 thing 在这两个函数的整个持续时间内都在作用域内,foo 不会将 thing 的地址传递给它的调用者(因为它是一个 void 函数)。

您的代码完全有效。

【讨论】:

    猜你喜欢
    • 2011-02-27
    • 2023-03-22
    • 2016-11-21
    • 2015-02-08
    • 2013-02-19
    • 2014-05-25
    • 1970-01-01
    • 2018-09-20
    • 1970-01-01
    相关资源
    最近更新 更多