C 交换两个变量内容的另一种方式?

C 交换两个变量内容的另一种方式?,c,pointers,swap,C,Pointers,Swap,昨天,我看到了以下函数 static void swap (int *p, int *q) { *p -= *q; *q += *p; *p = *q - *p; } 在何种情况下,此功能不起作用?我认为可能存在溢出问题,而且效率也可能很低。还有其他问题吗?溢出问题不是一个可能的问题,它确实存在:当你用一个大的正数交换一个大的负数时,你会得到一个溢出,根据标准,这是一个未定义的行为 但最重要的是,出于可读性的原因,使用这个或xor技巧来代替通常通过temp变量进行的交换

昨天,我看到了以下函数

static void swap (int *p, int *q) {
    *p -= *q;
    *q += *p;
    *p = *q - *p;
}

在何种情况下,此功能不起作用?我认为可能存在溢出问题,而且效率也可能很低。还有其他问题吗?

溢出问题不是一个可能的问题,它确实存在:当你用一个大的正数交换一个大的负数时,你会得到一个溢出,根据标准,这是一个未定义的行为


但最重要的是,出于可读性的原因,使用这个或xor技巧来代替通常通过temp变量进行的交换是毫无意义的。

溢出问题不是一个可能,它确实存在:当你用一个大的正值交换一个大的负值时,你会得到一个溢出,根据标准,这是未定义的行为


但最重要的是,出于可读性的原因,使用这个或xor技巧来代替通常通过temp变量进行的交换是毫无意义的。

实际上上述答案是错误的。它为p获取0xFFFFFE,因为它使用0x7FFFFFFFE初始化了q,而0x7FFFFFFFE不适合,并且除了未定义的行为之外,实际上被截断为0xFFFFFE

因此,答案中的代码实际上以p=0xFFFFFF和q=0xFFFFFE开始,并成功地交换了它们

编辑:错了,现在已经更正了

这个例程实际上运行得很好,除了最终未定义的行为,我不确定是否存在,我对标准的细节有点模糊

忽略最终未定义的行为,如果基础算法是common 2的补码模运算,则例程可以工作,并且没有溢出/下溢问题。这是一个模运算

唯一真正的问题是别名。如果两个指针相同,则指向的整数将变为0

因此,例程的先决条件是两个指针有效且没有别名。如果您希望您的程序需要空指针和相同指针,那么可以添加空指针和相同指针的检查


为什么使用这种机制而不是xor技巧而不是临时变量交换,这个问题仍然没有解决。但与OP问题无关:例程工作,溢出由处理器上最常见的2的补码模运算处理,这不是一个问题编辑:它之所以成为溢出,只是因为溢出在标准中是未定义的行为,但它在任何使用2的补码模运算的处理器上都工作。至于效率,这完全取决于优化器和处理器。

事实上,上述答案是错误的。它为p获取0xFFFFFE,因为它使用0x7FFFFFFFE初始化了q,而0x7FFFFFFFE不适合,并且除了未定义的行为之外,实际上被截断为0xFFFFFE

因此,答案中的代码实际上以p=0xFFFFFF和q=0xFFFFFE开始,并成功地交换了它们

编辑:错了,现在已经更正了

这个例程实际上运行得很好,除了最终未定义的行为,我不确定是否存在,我对标准的细节有点模糊

忽略最终未定义的行为,如果基础算法是common 2的补码模运算,则例程可以工作,并且没有溢出/下溢问题。这是一个模运算

唯一真正的问题是别名。如果两个指针相同,则指向的整数将变为0

因此,例程的先决条件是两个指针有效且没有别名。如果您希望您的程序需要空指针和相同指针,那么可以添加空指针和相同指针的检查


为什么使用这种机制而不是xor技巧而不是临时变量交换,这个问题仍然没有解决。但与OP问题无关:例程工作,溢出由处理器上最常见的2的补码模运算处理,这不是一个问题编辑:它之所以成为溢出,只是因为溢出在标准中是未定义的行为,但它在任何使用2的补码模运算的处理器上都工作。至于效率,这完全取决于优化器和处理器。

XOR技巧是否可能?注意:p和q可以指向同一个对象。是的,这些正是问题,溢出和别名。谢谢,我没有考虑别名的情况。这只是一个个人问题,我读了几个解决方案来交换两个变量,我以前从未见过这种方法。所以我问为什么。这不是家庭作业@EdHeal:此外,编译器通常能够通过一个临时变量检测交换,并在一条适当的汇编指令(如果可用)中转换它。XOR技巧可能吗?注意:p和q可能指向同一个对象。是的,这些正是问题,溢出和别名。谢谢,我没有考虑别名的情况。这只是一个个人问题,我读了几个解决方案来交换两个变量,和

我以前从未见过这种方法。所以我问为什么。这不是家庭作业@EdHeal:此外,编译器通常能够使用临时变量检测交换,并在单个适当的汇编指令中转换它(如果可用)。0x7FFFFFFFE需要35位,初始化时已经有溢出。如果用不需要大于int宽度的位数初始化p和q,则在溢出时有未定义的行为,但如果该行为恰好是环绕的,则有效。@Danielf如果你说得对,我应该更仔细地计算我的位数。非常感谢您的更正!您应该已经编译了所有警告。如果你的编译器很好,它会警告你初始化时溢出@DanielFischer我使用了ideone,它隐藏了编译器上的警告,但没有错误:啊,自己动手的另一个原因。0x7FFFFFE需要35位,初始化时已经溢出。如果用不需要大于int宽度的位数初始化p和q,则在溢出时有未定义的行为,但如果该行为恰好是环绕的,则有效。@Danielf如果你说得对,我应该更仔细地计算我的位数。非常感谢您的更正!您应该已经编译了所有警告。如果你的编译器很好,它会警告你初始化时溢出@DanielFischer我使用ideone实现了这一点,它隐藏了编译器上的警告,但没有错误:啊,这是自己动手的另一个原因。有符号整数溢出是一种未定义的行为,它在大多数系统上都有效,这使得它更加隐蔽,因为人们可能越来越期望它能工作。这个错误早就被纠正了,所以你的答案在这方面已经过时了。正是出于这个原因,我写了一篇忽略最终未定义行为的文章。你是对的,人们可能越来越期待它。未定义的行为意味着任何事情都可能发生,包括按预期工作,但关键是处理器算法使该算法有效。在C语言中,溢出是一种未定义的行为,尽管所有机器都使用模运算,这一事实使它成为问题。至于这个错误已经纠正的事实。。。那不是我开始写作的时候。对不起,我打字很慢。溢出的评论是因为你写的,我不确定是否有,只是为了澄清,不是批评。至于打字慢的问题,我很同情,我也很慢。尽管事实上所有的机器都使用模运算,但问题是,并不是所有的机器都使用模运算,不幸的是,仍然存在1的补码机器。使用1的补码进行有符号整数运算的机器仍然使用模运算进行无符号整数运算吗?如果是这样的话,该算法可以用无符号整数指针编写,事实上,即使指针指向的不是有符号整数,只要大小相等,也可以工作。使它工作的是模运算,而不是2的补码和1的补码的问题在于它不是模运算。我承认现在我只是好奇地问。是的,对于无符号整数,标准要求算术模2^WIDTH,所以对于无符号*,唯一的问题是别名,当然,指针必须是有效的。然而,使用unsigned*交换其他类型的变量同样是未定义的行为,但是,就我所知,它失败的唯一途径是编译器在假设严格别名的基础上进行优化。有符号整数的溢出是未定义的行为,它在大多数系统上都能工作,这让它变得更加阴险,因为人们可能越来越期待它能工作。这个错误早就被纠正了,所以你的答案在这方面已经过时了。正是出于这个原因,我写了一篇忽略最终未定义行为的文章。你是对的,人们可能越来越期待它。未定义的行为意味着任何事情都可能发生,包括按预期工作,但关键是处理器算法使该算法有效。在C语言中,溢出是一种未定义的行为,尽管所有机器都使用模运算,这一事实使它成为问题。至于这个错误已经纠正的事实。。。那不是我开始写作的时候。对不起,我打字很慢。溢出的评论是因为你写的,我不确定是否有,只是为了澄清,不是批评。至于打字慢的问题,我很同情,我也很慢。尽管事实上所有的机器都使用模运算,但问题是,并不是所有的机器都使用模运算,不幸的是,仍然存在1的补码机器。使用1的补码进行有符号整数运算的机器仍然使用模运算进行无符号整数运算吗?如果是这样的话,该算法可以用无符号整数指针编写,事实上,即使指针指向的不是有符号整数,只要大小相等,也可以工作。使它工作的是模运算,而不是
2的补码与1的补码的问题在于它不是模运算。我承认现在我只是好奇地问。是的,对于无符号整数,标准要求算术模2^WIDTH,所以对于无符号*,唯一的问题是别名,当然,指针必须是有效的。然而,使用unsigned*交换其他类型的变量同样是未定义的行为,但是,就我所知,它失败的唯一方法是编译器在假设严格别名的基础上进行优化。