两种不同的交换功能有什么区别?

2023-11-29

我想知道两种代码在性能上的区别。有什么优点和缺点?

Code 1:

temp  = a;
a  = b;
b  = temp;

Code 2:

a = a + b;
b = a - b;
a = a - b;

第一种技术的优点是它是一个通用的习语,明显且正确。它适用于任何地方、任何类型的变量。它很可能被优化编译器识别并被实际的“交换”指令(如果可用)替换。因此,除了更清晰、更正确之外,第一种技术也可能更有效。

第二种技术的优点是它避免了使用临时变量,并且它是一种令人愉快的晦涩技巧,深受那些不断收集晦涩技巧并提出涉及晦涩技巧的误导性“陷阱”面试问题的人的喜爱,并且(据我所知),他们通过用一些晦涩难懂的技巧来使自己的程序变得更难维护、更难移植、更不可靠。

第一种技术的缺点是:None.
(理论上,有人可能会说它使用临时变量是有缺点的,但实际上,这根本没有缺点,因为临时变量是免费的。我认为地球上没有人仍在为处理器编写代码,所以内存和寄存器有限,以这种方式“保存”临时变量实际上是值得担心的。)

第二种技术的缺点是它更难编写,更难让读者理解,并且可能效率较低(也许效率非常低)。它仅“适用于”算术类型,不适用于结构或其他类型。如果它被用来尝试与自身交换数据,它将无法工作(它会悄悄地损坏数据)。 (稍后会详细介绍这种可能性。)如果这些还不够糟糕,那么即使在“普通”情况下,它也可能从根本上存在错误,因为它可能会溢出,并且对于浮点类型,它可能会改变一个或两个值稍微由于舍入误差而导致,并且对于指针类型,如果被交换的指针不指向同一对象内,则它是未定义的。

您具体询问了性能问题,所以让我们多说几句。 (免责声明:我不是微优化方面的专家;我倾向于用相当抽象、随意的术语来思考指令级性能。)

第一种技术使用三个分配。第二种技术使用一次加法和两次减法。在许多机器上,算术运算与简单的赋值所花费的周期数相同,因此在许多情况下,两种技术的性能是相同的。但很难想象第二种技术如何变得更加高效,而很容易想象第一种技术如何变得更加高效。特别是,正如我已经提到的,如果目标处理器有第一种技术,编译器更容易识别并转换为更高效的 SWP 指令。


现在,有些题外话。这里介绍的第二种技术是传统的、令人惊奇的晦涩技巧的一种不太美味的变体,用于在不使用临时变量的情况下交换两个变量。在不使用临时变量的情况下交换两个变量的传统的、令人难以理解的技巧是:

a ^= b;
b ^= a;
a ^= b;

曾几何时,在某些圈子里,以一种更加美妙而晦涩的方式呈现这些技术是一种时尚:

a ^= b ^= a ^= b;       /* WRONG */
a += b -= a -= b;       /* WRONG */

但是这些演绎(同时,是的,绝对是精致地如果你喜欢这类东西,那就太晦涩难懂了)还有它们所代表的额外的崩溃缺点未定义的行为,因为他们尝试修改a在同一个表达式中多次出现,且没有插入序列点。 (另请参阅关于该主题的规范问题.)


公平地说,我必须提到,在一种实际情况下,第一种技术使用临时变量可能是一个重大缺点,而第二种技术缺乏临时变量可能因此成为一个实际优势。一种情况是,如果您试图编写一个通用的“交换”宏,类似于

#define Swap(a, b) (a = a + b, b = a - b, a = a - b)

这个想法是,您可以在任何地方、任何类型的变量上使用这个宏,并且(因为它是一个宏,因此很神奇)您甚至不必使用&关于您调用它的参数,就像它是一个函数一样。但在传统的 C 语言中,至少,如果你想写一个Swap像这样的宏,使用技术 1 基本上是不可能做到的,因为没有办法声明必要的临时变量。

你不是在问这个子问题,但既然我提出了它,我不得不说解决方案(尽管它对那些喜欢美味默默无闻的人来说永远是令人沮丧的)就是首先不要尝试编写“通用”宏来交换两个值。你不能在 C 中做到这一点。(事实上,你可以在 C++ 中做到这一点,使用新的定义auto,现在我猜 C 也有一些编写通用宏的新方法。)

当您尝试以这种方式编写“交换”宏时,实际上还有一个额外的崩溃问题,那就是它会not工作 — 如果调用者尝试与自身交换值,它将把一个或两个变量设置为 0,而不是交换值。你可能会说这不是问题,因为也许没有人会写Swap(x, x),但在不太完美的排序例程中,他们可能很容易编写Swap(a[i], a[j])有时在哪里i恰好等于j, or Swap(*p, *q)有时指针p恰好等于q.

另请参阅C 常见问题列表, 问题3.3b, 10.3 and 20.15c.

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

两种不同的交换功能有什么区别? 的相关文章

  • WCF RIA 服务 - 加载多个实体

    我正在寻找一种模式来解决以下问题 我认为这很常见 我正在使用 WCF RIA 服务在初始加载时将多个实体返回给客户端 我希望两个实体异步加载 以免锁定 UI 并且我想利用 RIA 服务来执行此操作 我的解决方案如下 似乎有效 这种方法会遇到
  • GLKit的GLKMatrix“列专业”如何?

    前提A 当谈论线性存储器中的 列主 矩阵时 列被一个接一个地指定 使得存储器中的前 4 个条目对应于矩阵中的第一列 另一方面 行主 矩阵被理解为依次指定行 以便内存中的前 4 个条目指定矩阵的第一行 A GLKMatrix4看起来像这样 u
  • 动态加载程序集的应用程序配置

    我正在尝试将模块动态加载到我的应用程序中 但我想为每个模块指定单独的 app config 文件 假设我的主应用程序有以下 app config 设置
  • 类模板参数推导 - clang 和 gcc 不同

    下面的代码使用 gcc 编译 但不使用 clang 编译 https godbolt org z ttqGuL template
  • Clang 3.1 + libc++ 编译错误

    我已经构建并安装了 在前缀下 alt LLVM Clang trunk 2012 年 4 月 23 日 在 Ubuntu 12 04 上成功使用 GCC 4 6 然后使用此 Clang 构建的 libc 当我想使用它时我必须同时提供 lc
  • 使用 Bearer Token 访问 IdentityServer4 上受保护的 API

    我试图寻找此问题的解决方案 但尚未找到正确的搜索文本 我的问题是 如何配置我的 IdentityServer 以便它也可以接受 授权带有 BearerTokens 的 Api 请求 我已经配置并运行了 IdentityServer4 我还在
  • 如何设计以 char* 指针作为类成员变量的类?

    首先我想介绍一下我的情况 我写了一些类 将 char 指针作为私有类成员 而且这个项目有 GUI 所以当单击按钮时 某些函数可能会执行多次 这些类是设计的单班在项目中 但是其中的某些函数可以执行多次 然后我发现我的项目存在内存泄漏 所以我想
  • SolrNet连接说明

    为什么 SolrNet 连接的容器保持静态 这是一个非常大的错误 因为当我们在应用程序中向应用程序发送异步请求时 SolrNet 会表现异常 在 SolrNet 中如何避免这个问题 class P static void M string
  • 如何在 C 中调用采用匿名结构的函数?

    如何在 C 中调用采用匿名结构的函数 比如这个函数 void func struct int x p printf i n p x 当提供原型的函数声明在范围内时 调用该函数的参数必须具有与原型中声明的类型兼容的类型 其中 兼容 具有标准定
  • 这些作业之间是否存在顺序点?

    以下代码中的两个赋值之间是否存在序列点 f f x 1 1 x 2 不 没有 在这种情况下 标准确实是含糊不清的 如果你想确认这一点 gcc 有这个非常酷的选项 Wsequence point在这种情况下 它会警告您该操作可能未定义
  • 使用 x509 证书签署 json 文档或字符串

    如何使用 x509 证书签署 json 文档或字符串 public static void fund string filePath C Users VIKAS Desktop Data xml Read the file XmlDocum
  • 对现有视频添加水印

    我正在寻找一种用 C 在视频上加水印的方法 就像在上面写文字一样 图片或文字标签 我该怎么做 谢谢 您可以使用 Nreco 视频转换器 代码看起来像 NReco VideoConverter FFMpegConverter wrap new
  • 通过指向其基址的指针删除 POD 对象是否安全?

    事实上 我正在考虑那些微不足道的可破坏物体 而不仅仅是POD http en wikipedia org wiki Plain old data structure 我不确定 POD 是否可以有基类 当我读到这个解释时is triviall
  • cmake 将标头包含到每个源文件中

    其实我有一个简单的问题 但找不到答案 也许你可以给我指一个副本 所以 问题是 是否可以告诉 cmake 指示编译器在每个源文件的开头自动包含一些头文件 这样就不需要放置 include foo h 了 谢谢 CMake 没有针对此特定用例的
  • 将控制台重定向到 .NET 程序中的字符串

    如何重定向写入控制台的任何内容以写入字符串 对于您自己的流程 Console SetOut http msdn microsoft com en us library system console setout aspx并将其重定向到构建在
  • C# 成员变量继承

    我对 C 有点陌生 但我在编程方面有相当广泛的背景 我想做的事情 为游戏定义不同的 MapTiles 我已经像这样定义了 MapTile 基类 public class MapTile public Texture2D texture pu
  • 测试用例执行完成后,无论是否通过,如何将测试用例结果保存在变量中?

    我正在使用 NUNIT 在 Visual Studio 中使用 Selenium WebDriver 测试用例的代码是 我想在执行测试用例后立即在变量中记录测试用例通过或失败的情况 我怎样才能实现这一点 NUnit 假设您使用 NUnit
  • 是否可以在 .NET Core 中将 gRPC 与 HTTP/1.1 结合使用?

    我有两个网络服务 gRPC 客户端和 gRPC 服务器 服务器是用 NET Core编写的 然而 客户端是托管在 IIS 8 5 上的 NET Framework 4 7 2 Web 应用程序 所以它只支持HTTP 1 1 https le
  • Windows 和 Linux 上的线程

    我在互联网上看到过在 Windows 上使用 C 制作多线程应用程序的教程 以及在 Linux 上执行相同操作的其他教程 但不能同时用于两者 是否存在即使在 Linux 或 Windows 上编译也能工作的函数 您需要使用一个包含两者的实现
  • 使用.NET技术录制屏幕视频[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 有没有一种方法可以使用 NET 技术来录制屏幕 无论是桌面还是窗口 我的目标是免费的 我喜欢小型 低

随机推荐

  • Snakemake 无法处理很长的命令行?

    这是一个很奇怪的问题 当我的 input 中指定的rule部分是 input 有超过 500 个文件 snakemake 刚刚退出并显示消息 one of the commands exited with non zero exit cod
  • 如何创建一个 Mailto Share 按钮,该按钮打开一个窗口,可以在其中输入要发送到的电子邮件地址

    我一直在互联网上搜索以了解如何创建mailto共享按钮将打开一个新窗口 用户可以在其中输入他选择发送到的电子邮件地址 我附上了一组连续的图像作为操作示例 这其中有什么技巧呢 为什么我找不到任何有关如何编写此类代码的信息 堆栈溢出上也绝对找不
  • 如何阻止Savon向soap.body添加前缀

    这就是我创建客户端的方式 client Savon Client new do wsdl document my document wsdl endpoint my endpoint end 这就是我得到回复的方式 response cli
  • 将 PHP 数组传递给函数?

    我有以下代码 params array api user gt user api key gt pass to gt email protected subject gt testing from curl html gt testing
  • 如何更改 JAX-RS 应用程序中的 Jackson 版本 (WebSphere Liberty)

    我正在将 JAX RS 应用程序从 WebSphere 8 0 迁移到 WebSphere Liberty 8 5 5 在WebSphere 8 0 中 Jackson 由WebSphere 提供 我可以找到jackson core asl
  • 从 read 调用中得到负一

    我使用 SQL Developer 连接到具有只读访问权限的数据库 这是 TNS 连接 我使用 tnsnames ora 转发端口脚本和 SQL Developer 过去 有时在连接时会收到错误消息 从 read 调用中得到负一 供应商代码
  • 当通过启动器中的图标按下启动时,应用程序完全重新启动

    我正在尝试制作我的第一个 Android 应用程序的发布版本 以发送给一些测试人员 然而 我遇到了一个问题 当您退出应用程序 然后通过其图标启动它重新进入它时 它会重新启动整个应用程序 而不是返回到之前的位置 即使您退出后立即重新进入 也会
  • 如果未使用 CloseHandle 正确关闭,则重新打开串行端口会失败

    我正在 Windows 上使用 USB 设备 该设备被视为虚拟串行端口 我可以使用 CreateFile 和 ReadFile 函数与设备进行通信 但在某些情况下 我的应用程序不会调用 CloseHandle 当我的应用程序在开发中崩溃时
  • 混合模式程序集是针对版本“v1.1.4322”构建的

    我在 c net 4 0 应用程序中包含了一个 directX 播放器 该应用程序包含在此处 答案2 问题是 当我尝试初始化对象 即 Player mPlayer new Player 时 会发生此错误 混合模式程序集是针对运行时版本 v1
  • 画布被跨源数据污染

    我正在从我可以信任的第三方网站加载动态 jpeg 我试图getImageData 但浏览器 Chrome 23 0 抱怨 Unable to get image data from canvas because the canvas has
  • 快速找到以2为底的对数的整数部分

    计算浮点数以 2 为底的对数的整数部分的有效方法是什么 就像是 N ceil log2 f or N floor log2 f 对于浮点数 f 我想这可以以某种方式非常有效地实现 因为人们可能只需要访问浮点指数 EDIT2 我主要不感兴趣精
  • 参与者数量为奇数的每周小组分配算法

    问题有一个循环解决方案我之前问过 它对于偶数的人来说效果很好 但是一旦你实现了算法并尝试了它们 这些建议似乎都不起作用 我已经尝试了很多变化并且 将最后一个与一大堆其他人分组 第二组最后一组 不同的组合 2和4到底行的最后一个 我认为这会给
  • 检索 Matplotlib 热图颜色

    我正在尝试检索 matplotlib 热图上每个单元格的颜色 该热图由imshow 功能 例如由magic function below import matplotlib pyplot as plt import numpy as np
  • 如何使用 javascript d3 打开 json 文件?

    我正在尝试使用 javascript 从 JSON 文件中提取元素 但是收到一条错误消息 指出它无法加载 JSON 文件 这就是我的代码的样子
  • 异步nodejs执行顺序

    processItem什么时候开始执行 是否在某些项目被推入队列后立即开始 或者 for 循环必须在队列中的第一项开始执行之前完成 var processItem function item callback console log ite
  • 将列插入 pandas 数据框

    设想 我有一段代码 可以将 Excel 工作表中的数据读取到数据框中 合并到一个数据框中 并执行一些清理过程 Issue 我试图使用 pd insert 将具有给定值的列添加到数据帧的开头 但每次运行此行时 数据帧都会从变量资源管理器中消失
  • 从 Facebook API 将数据插入 Meteor

    我按照给出的例子here从 FB Graph 中提取数据 到目前为止 我已经设法从 FB 中提取数据 但我不知道如何将其插入到 MongoDB 中 目前 Facebook 的数据呈现如下 data picture https photo j
  • 将 Ajax 与 jQuery DataTables 结合使用时,如何确定如何处理返回的数据?

    像许多其他人一样 我查看类似问题的各种答案 在网上搜索示例等 但除非我碰巧找到我遇到的几乎相同的情况 否则我无法弄清楚如何让 DataTable 填充阿贾克斯呼叫 我认为如果有人能够解释所发生的步骤以及如何使用 DataTables 的 A
  • 按字典顺序查找排列列表中给定排列的索引[重复]

    这个问题在这里已经有答案了 可能的重复 给定一个字符串和该字符串的排列 在字符串排列的排序列表中查找该排列字符串的索引 这是一道面试题 假设有一个按字典顺序排列的排列列表 例如 123 132 213 231 312 321 给定一个排列
  • 两种不同的交换功能有什么区别?

    我想知道两种代码在性能上的区别 有什么优点和缺点 Code 1 temp a a b b temp Code 2 a a b b a b a a b 第一种技术的优点是它是一个通用的习语 明显且正确 它适用于任何地方 任何类型的变量 它很可