复杂表达式中的后缀增量运算符究竟何时计算?

2024-01-08

说我有这样的表情

short v = ( ( p[ i++ ] & 0xFF ) << 4 | ( p[ i ] & 0xF0000000 ) >> 28;

with p是一个指向动态分配的 32 位整数数组的指针。

When exactly will i会增加吗?我注意到上面的代码提供了不同的值v比下面的代码:

short v = ( p[ i++ ] & 0xFF) <<   4;
v |= ( p[ i ] & 0xF0000000 ) >>  28;

我对这种行为的最佳猜测是i在上面的右侧之前不递增|被评估。

任何见解将不胜感激!

提前致谢,

\Bjoern


问题是评估顺序:
C++ 标准没有定义子表达式的求值顺序。这样做是为了让编译器能够尽可能积极地进行优化。

让我们分解一下:

           a1                        a2
v = ( ( p[ i++ ] & 0xFF ) << 4 | ( p[ i ] & 0xF0000000 ) >> 28;

-----
(1) a1 = p[i]
(2) i  = i + 1 (i++)       after (1)      
(3) a2 = p[i]
(4) t3 = a1 & 0xFF         after (1)
(5) t4 = a2 & 0xF0000000   after (3)
(6) t5 = t3 << 4           after (4)
(7) t6 = t4 >> 28          after (5)
(8) t7 = t5 | t6           after (6) and (7)
(9) v  = t7                after (8)

现在,只要不违反上述“after”子句,编译器就可以自由地重新排列子表达式。因此,一种快速简单的优化是将 3 向上移动一个槽,然后进行公共表达式删除 (1) 和 (3)(现在彼此相邻)是相同的,因此我们可以消除 (3)

但编译器不必进行优化(并且可能比我更好,并且还有其他技巧)。但是您可以看到 (a1) 的值始终是您期望的值,但 (a2) 的值将取决于编译器决定执行其他子表达式的顺序。

唯一保证编译器不能将子表达式移过序列点。最常见的序列点是“;” (声明结束)。还有其他的,但我会避免使用这些知识,因为大多数人不太了解编译器的工作原理。如果您编写使用序列点技巧的代码,那么有人可能会重构代码以使其看起来更具可读性,而现在您的技巧已经变成了未定义的行为。

short v = ( p[ i++ ] & 0xFF) <<   4;
v |= ( p[ i ] & 0xF0000000 ) >>  28;

-----
(1) a1 = p[i]
(2) i  = i + 1 (i++)       after (1)      
(4) t3 = a1 & 0xFF         after (1)
(6) t5 = t3 << 4           after (4)
(A) v = t5                 after (6)
------ Sequence Point
(3) a2 = p[i]
(5) t4 = a2 & 0xF0000000   after (3)
(7) t6 = t4 >> 28          after (5)
(8) t7 = v | t6            after (7)
(9) v  = t7                after (8)

这里一切都被明确定义,因为对 i 的写入是就地起诉的,而不是在同一表达式中重新读取。

简单的规则。不要在较大的表达式中使用 ++ 或 -- 运算符。 您的代码看起来就像这样可读:

++i; // prefer pre-increment (it makes no difference here, but is a useful habit)
v = ( ( p[ i ] & 0xFF ) << 4 | ( p[ i ] & 0xF0000000 ) >> 28;

评估顺序的详细解释请参见这篇文章:
C++ 程序员应该了解哪些常见的未定义行为? https://stackoverflow.com/questions/367633/what-are-all-the-common-undefined-behaviour-that-c-programmer-should-know-about/367690#367690

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

复杂表达式中的后缀增量运算符究竟何时计算? 的相关文章

  • C 编程 - 文件 - fwrite

    我有一个关于编程和文件的问题 while current NULL if current gt Id Doctor 0 current current gt next id doc current gt Id Doctor if curre
  • 通过 CMIS (dotCMIS) 连接到 SP2010:异常未经授权

    我正在使用 dotCMIS 并且想要简单连接到我的 SP2010 服务器 我尝试用 C 来做到这一点 如下所示http chemistry apache org dotnet getting started with dotcmis htm
  • 为什么两个不同的 Base64 字符串的转换会返回相等的字节数组?

    我想知道为什么从 base64 字符串转换会为不同的字符串返回相同的字节数组 const string s1 dg const string s2 dq byte a1 Convert FromBase64String s1 byte a2
  • 动态加载程序集的应用程序配置

    我正在尝试将模块动态加载到我的应用程序中 但我想为每个模块指定单独的 app config 文件 假设我的主应用程序有以下 app config 设置
  • 嵌套接口:将 IDictionary> 转换为 IDictionary>?

    我认为投射一个相当简单IDictionary
  • HTTPWebResponse 响应字符串被截断

    应用程序正在与 REST 服务通信 Fiddler 显示作为 Apps 响应传入的完整良好 XML 响应 该应用程序位于法属波利尼西亚 在新西兰也有一个相同的副本 因此主要嫌疑人似乎在编码 但我们已经检查过 但空手而归 查看流读取器的输出字
  • 如何从 appsettings.json 文件中的对象数组读取值

    我的 appsettings json 文件 StudentBirthdays Anne 01 11 2000 Peter 29 07 2001 Jane 15 10 2001 John Not Mentioned 我有一个单独的配置类 p
  • 重载<<的返回值

    include
  • SolrNet连接说明

    为什么 SolrNet 连接的容器保持静态 这是一个非常大的错误 因为当我们在应用程序中向应用程序发送异步请求时 SolrNet 会表现异常 在 SolrNet 中如何避免这个问题 class P static void M string
  • 覆盖子类中的字段或属性

    我有一个抽象基类 我想声明一个字段或属性 该字段或属性在从该父类继承的每个类中具有不同的值 我想在基类中定义它 以便我可以在基类方法中引用它 例如覆盖 ToString 来表示 此对象的类型为 property field 我有三种方法可以
  • 链接器错误:已定义

    我尝试在 Microsoft Visual Studio 2012 中编译我的 Visual C 项目 使用 MFC 但出现以下错误 error LNK2005 void cdecl operator new unsigned int 2
  • 如何使用 C# / .Net 将文件列表从 AWS S3 下载到我的设备?

    我希望下载存储在 S3 中的多个图像 但目前如果我只能下载一个就足够了 我有对象路径的信息 当我运行以下代码时 出现此错误 遇到错误 消息 读取对象时 访问被拒绝 我首先做一个亚马逊S3客户端基于我的密钥和访问配置的对象连接到服务器 然后创
  • 对现有视频添加水印

    我正在寻找一种用 C 在视频上加水印的方法 就像在上面写文字一样 图片或文字标签 我该怎么做 谢谢 您可以使用 Nreco 视频转换器 代码看起来像 NReco VideoConverter FFMpegConverter wrap new
  • cmake 将标头包含到每个源文件中

    其实我有一个简单的问题 但找不到答案 也许你可以给我指一个副本 所以 问题是 是否可以告诉 cmake 指示编译器在每个源文件的开头自动包含一些头文件 这样就不需要放置 include foo h 了 谢谢 CMake 没有针对此特定用例的
  • 如何在Xamarin中删除ViewTreeObserver?

    假设我需要获取并设置视图的高度 在 Android 中 众所周知 只有在绘制视图之后才能获取视图高度 如果您使用 Java 有很多答案 最著名的方法之一如下 取自这个答案 https stackoverflow com a 24035591
  • 混合 ExecutionContext.SuppressFlow 和任务时 AsyncLocal.Value 出现意外值

    在应用程序中 由于 AsyncLocal 的错误 意外值 我遇到了奇怪的行为 尽管我抑制了执行上下文的流程 但 AsyncLocal Value 属性有时不会在新生成的任务的执行范围内重置 下面我创建了一个最小的可重现示例来演示该问题 pr
  • Windows 和 Linux 上的线程

    我在互联网上看到过在 Windows 上使用 C 制作多线程应用程序的教程 以及在 Linux 上执行相同操作的其他教程 但不能同时用于两者 是否存在即使在 Linux 或 Windows 上编译也能工作的函数 您需要使用一个包含两者的实现
  • 如何在文本框中插入图像

    有没有办法在文本框中插入图像 我正在开发一个聊天应用程序 我想用图标图像更改值 等 但我找不到如何在文本框中插入图像 Thanks 如果您使用 RichTextBox 进行聊天 请查看Paste http msdn microsoft co
  • C++ 中类级 new 删除运算符的线程安全

    我在我的一门课程中重新实现了新 删除运算符 现在我正在使我的代码成为多线程 并想了解这些运算符是否也需要线程安全 我在某处读到 Visual Studio 中默认的 new delete 运算符是线程安全的 但这对于我的类的自定义 new
  • 如何防止用户控件表单在 C# 中处理键盘输入(箭头键)

    我的用户控件包含其他可以选择的控件 我想实现使用箭头键导航子控件的方法 问题是家长控制拦截箭头键并使用它来滚动其视图什么是我想避免的事情 我想自己解决控制内容的导航问题 我如何控制由箭头键引起的标准行为 提前致谢 MTH 这通常是通过重写

随机推荐