3

在这个链接中:

http://blogs.msdn.com/b/oldnewthing/archive/2011/04/06/10150261.aspx

最近有人向我指出以下行:

Widget *pwidOld = reinterpret_cast<Widget*>
                 (InterlockedCompareExchangePointerRelease(
                  &reinterpret_cast<PVOID&>(g_pwidCached),
                  pwid, NULL));

有一个良性问题和一个严重问题。

良性的是可以对返回类型进行 static_cast。

严重的似乎是:

&reinterpret_cast<PVOID&>(g_pwidCached)

有人告诉我,通过严格的别名,当您将 &(void*&)g_pwidCached 传递给函数时,编译器可以假设 g_pwidCached 的值没有变化,因为该变化将通过指针类型发生不是对象的类型,也不是 char*(因为 g_pwidCached 不是 void*,它是 Widget*)。3.10/10 似乎是相关的。

这只是特定编译器实现的一个函数,它只是 Visual C++ 保证该行将正常工作吗?

4

2 回答 2

2

代码当然依赖于特定于实现的属性。甚至不能保证它Widget*的大小与 相同void*,不要介意某些被调用的函数InterlockedCompareExchangePointerRelease在传递void**指向 a 的 a时会正常工作Widget*

我可能忽略了一些东西,但我认为实际问题是,“优化器中的引用转义代码会看到reinterpret_cast<PVOID&>,并假设没有引用gpwidCached转义函数吗?” 如果答案是“是”,那么我们就有问题了,因为编译器会假定没有修改,而实际上确实发生了修改。但答案是“否”,前提是它将它视为InterlockedCompareExchangePointerRelease一个黑盒,因为它知道函数在访问它之前会将点转换回正确的类型,在这种情况下,编译器没有自由假设没有发生修改。

[编辑:实际上,答案比我最初意识到的还要“不”。大概g_pwidCached是一个全局的,所以编译器永远不能假设它没有被它调用的任何未知代码修改,无论参数如何。该代码可能会使用 name 对其进行修改g_pwidCached,这当然会具有正确的类型,以免出现别名。]

如果被内联和/或实现为编译器内在函数,答案也是“否” InterlockedCompareExchangePointerRelease,因为实现将(如果正确)做任何必要的事情来确保不会出错。请注意,该函数需要 a void *volatile*,因此无论实现它,它都必须执行特定于实现的事情以确保没有别名问题,因为传递类型双关指针是预期的用例。

有人告诉我,通过严格的别名,当您将 &(void*&)g_pwidCached 传递给函数时,允许编译器假设 g_pwidCached 的值没有变化,因为该变化将通过指针类型发生不是对象的类型,也不是 char*

这不太正确。如果函数确实通过不正确的类型访问该值,则行为未定义。毫无疑问,Windows 实现确实如此。但是在没有看到函数的定义的情况下,编译器不知道它是否通过它传递的类型来访问它,或者以某种方式找出正确的类型来将它转换回以便在不违反严格别名的情况下进行访问. 这就是为什么(正如我上面所说的)编译器不能围绕对未知函数的调用进行任何依赖于严格别名的优化的原因。

于 2013-10-01T15:00:44.957 回答
0

类型g_pwidCached不对,应该是

void* g_pwidCached;

因为唯一使用过它的东西,把它当作void*.

delete g_pwidCached;无论如何可以说是错误的,应该通过InterlockedExchangePointer(nullptr, &g_pwidCached)将值放入本地并使用本地删除)

但是所有这些,包括更好的代码,都在该博客帖子的评论中得到了解决。

于 2013-10-01T15:21:37.583 回答