【问题标题】:Does abs(unsigned long) make any sense?abs(unsigned long) 有什么意义吗?
【发布时间】:2015-01-08 05:03:43
【问题描述】:

我遇到过这段代码,顺便说一下我的分析器报告为瓶颈:

#include <stdlib.h>

unsigned long a, b;
// Calculate values for a and b
unsigned long c;

c = abs(a - b);

那条线有没有比c = a - b; 更有趣的事情?任何一个选项都调用未定义或实现定义的行为,还有其他潜在的陷阱吗?请注意,包括 C &lt;stdlib.h&gt;,而不是 &lt;cstdlib&gt;

【问题讨论】:

  • 由于这些值是无符号的,它们不能为负数,所以abs() 调用没有意义。然而,abs() 在 C++ 中被重载并且也可能是一个宏,所以它的作用对我来说并不是 100% 清楚。它甚至可以是一个采用signed long 的函数,然后它会根据参数的MSB 执行某些操作。取不能为负数的绝对值的原因是什么?如果没有,只需删除abs() 调用。
  • unsigned 到无法表示值的 int 的潜在转换是未定义的行为。这种转换也是唯一一种腹肌会产生影响的情况。
  • @UlrichEckhardt 如果a = 10UL;b = 30UL;,正确答案应该是20UL,但删除abs 会给我错误的答案。
  • 在调用 abs() 之前,10UL - 30UL 产生 4294967276UL(假设为 32 位无符号长整数)。如果abs() 按照它的建议去做(见我关于宏/重载的评论),结果也是4294967276UL。如果你想要 20,你必须使用有符号算术,例如abs(static_cast&lt;long&gt;(a) - static_cast&lt;long&gt;(b))。不过,您必须确保它不会环绕,因为这会产生“未定义的行为”。总之,我建议您使用 Mohit Jain 的建议,即首先检查哪个更大。
  • 对于 C++ 情况,这可能是become ill-formed in the future

标签: c++ c


【解决方案1】:

不,这没有意义。

如果你想要不同,请使用

c = (a > b) ? a - b : b - a;

c = max(a, b) - min(a, b);

如果低于零,无符号将回滚(效果类似于添加 2sizeof (unsigned long) * CHAR_BIT

如果你正在寻找两个数字之间的差异,你可以写一个如下的小模板

namespace MyUtils {
  template<typename T>
  T diff(const T&a, const T&b) {
    return (a > b) ? (a - b) : (b - a);
  }
}

abs的声明继承自C(因为你包含了stdlib.h

int       abs( int n );
long      abs( long n );
long long abs( long long n ); //    (since C++11)
//Defined in header <cinttypes>
std::intmax_t abs( std::intmax_t n ); //    (since C++11)

C++ 中的abs(来自cmath

float       abs( float arg );
double      abs( double arg );
long double abs( long double arg );

如果您注意到,每个函数的参数和返回类型都是signed。因此,如果您将无符号类型传递给这些函数之一,则会发生隐式转换 unsigned T1 -&gt; signed T2 -&gt; unsigned T1 (在您的情况下,T1T2 可能相同,T1long)。将无符号整数转换为有符号整数时,如果不能以有符号类型表示,则行为取决于实现。

来自 4.7 积分转换 [conv.integral]

  1. 如果目标类型是无符号的,则结果值是最小的 与源整数一致的无符号整数(模 2n 其中 n 是 用于表示无符号类型的位数)。 [注:在一个 二进制补码表示,这种转换是概念性的,并且 位模式没有变化(如果没有截断)。 — 尾注]
  2. 如果目标类型是有符号的,如果可以的话,值不变 以目标类型(和位域宽度)表示;除此以外, 该值是实现定义的。

【讨论】:

  • 请注意,虽然它不适用于 unsigned longunsigned int,但它适用于 unsigned charunsigned short(除了没人使用的病理系统)。
  • 我不明白 sizeof(short)==sizeof(int) 的系统比 sizeof(int)==sizeof(long) 的系统更病态(只是寻找一个取笑“男人”的机会)
  • s/sizeof(unsigned long)/sizeof(unsigned long) * CHAR_BIT
  • @TheParamagneticCroissant 感谢 :) 的更正
【解决方案2】:

我不知道你是否认为它是有道理的,但abs() 应用于一个无符号值肯定可以返回一个不同于传入的值。那是因为abs() 需要一个int 参数并返回一个int 值。

例如:

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    unsigned u1 = 0x98765432;
    printf("u1 = 0x%.8X; abs(u1) = 0x%.8X\n", u1, abs(u1));
    unsigned long u2 = 0x9876543201234567UL;
    printf("u2 = 0x%.16lX; abs(u2) = 0x%.16lX\n", u2, labs(u2));
    return 0;
}

当编译为 C 或 C++(在 Mac OS X 10.10.1 Yosemite 上使用 GCC 4.9.1)时,它会产生:

u1 = 0x98765432; abs(u1) = 0x6789ABCE
u2 = 0x9876543201234567; abs(u2) = 0x6789ABCDFEDCBA99

如果设置了无符号值的高位,则abs() 的结果不是传递给函数的值。

减法只是分散注意力;如果结果设置了最高有效位,则从abs() 返回的值将与传递给它的值不同。


当您使用 C++ 标头而不是问题中显示的 C 标头编译此代码时,它会因不明确的调用错误而无法编译:

#include <cstdlib>
#include <iostream>
using namespace std;

int main(void)
{
    unsigned u1 = 0x98765432;
    cout << "u1 = 0x" << hex << u1 << "; abs(u1) = 0x" << hex << abs(u1) << "\n";
    unsigned long u2 = 0x9876543201234567UL;
    cout << "u2 = 0x" << hex << u2 << "; abs(u2) = 0x" << hex << abs(u2) << "\n";
    return 0;
}

编译错误:

absuns2.cpp: In function ‘int main()’:
absuns2.cpp:8:72: error: call of overloaded ‘abs(unsigned int&)’ is ambiguous
     cout << "u1 = 0x" << hex << u1 << "; abs(u1) = 0x" << hex << abs(u1) << "\n";
                                                                        ^
absuns2.cpp:8:72: note: candidates are:
In file included from /usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:72:0,
                 from absuns2.cpp:1:
/usr/include/stdlib.h:129:6: note: int abs(int)
 int  abs(int) __pure2;
      ^
In file included from absuns2.cpp:1:0:
/usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:174:3: note: long long int std::abs(long long int)
   abs(long long __x) { return __builtin_llabs (__x); }
   ^
/usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:166:3: note: long int std::abs(long int)
   abs(long __i) { return __builtin_labs(__i); }
   ^
absuns2.cpp:10:72: error: call of overloaded ‘abs(long unsigned int&)’ is ambiguous
     cout << "u2 = 0x" << hex << u2 << "; abs(u2) = 0x" << hex << abs(u2) << "\n";
                                                                        ^
absuns2.cpp:10:72: note: candidates are:
In file included from /usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:72:0,
                 from absuns2.cpp:1:
/usr/include/stdlib.h:129:6: note: int abs(int)
 int  abs(int) __pure2;
      ^
In file included from absuns2.cpp:1:0:
/usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:174:3: note: long long int std::abs(long long int)
   abs(long long __x) { return __builtin_llabs (__x); }
   ^
/usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:166:3: note: long int std::abs(long int)
   abs(long __i) { return __builtin_labs(__i); }
   ^

因此,问题中的代码仅在仅使用 C 样式标头时才能编译;使用 C++ 头文件时它不会编译。如果您添加 &lt;stdlib.h&gt;&lt;cstdlib&gt;,则会有一个额外的重载使调用更加模糊。

如果在对abs() 的调用中添加(不)适当的强制转换,您可以使代码编译,并且有符号数量的绝对值可能与原始有符号数量不同,这不足为奇的消息:

#include <cstdlib>
#include <iostream>
using namespace std;

int main(void)
{
    unsigned u1 = 0x98765432;
    cout << "u1 = 0x" << hex << u1 << "; abs(u1) = 0x" << hex << abs(static_cast<int>(u1)) << "\n";
    unsigned long u2 = 0x9876543201234567UL;
    cout << "u2 = 0x" << hex << u2 << "; abs(u2) = 0x" << hex << abs(static_cast<long>(u2)) << "\n";
    return 0;
}

输出:

u1 = 0x98765432; abs(u1) = 0x6789abce
u2 = 0x9876543201234567; abs(u2) = 0x6789abcdfedcba99

道德:不要使用 C++ 代码中存在 C++ 等效项的 C 头文件;改用 C++ 头文件。

【讨论】:

  • abs 应该是 C++ 中的重载函数,即使包含 &lt;stdlib.h&gt;。要么实现没有这样做,要么abs(int) 不会是被选中的重载。
  • 如果你只包含&lt;stdlib.h&gt;,我认为不会。这只为您提供标准的 C 函数。顺便说一句,我不知道哪个 C++ 标头为abs() 定义了重载。而且我不知道重载是否包括无符号类型。
  • &lt;cstdlib&gt; 被指定为包含 C 的 &lt;stdlib.h&gt; 的声明,除了命名空间 std,加上 long abs(long)long long abs(long long)。 C++ 的 &lt;stdlib.h&gt; 被指定为包含 C++ 的 &lt;cstdlib&gt; 的声明,但在全局命名空间中除外。 (引用 C++11:“每个 C 标头,每个标头都有一个 name.h 形式的名称,其行为就像每个名称由相应的 cname 标头放置在标准库命名空间中一样放置在全局命名空间中范围。”)这将包括添加的重载。但是实现有缺陷,并不是所有的都能做到这一点。
  • 如果问题中的代码包含&lt;cstdlib&gt; 而不是&lt;stdlib.h&gt;,我就不会创建双语测试程序。我引用了我用来允许跟踪错误的环境。我可能会在今天晚些时候玩一下,看看使用&lt;cstdlib&gt; 代替&lt;stdlib.h&gt; 是否会有所不同;我不会在没有实验的情况下调用它。
【解决方案3】:

我认为当 c=a-b 为负数时,如果 c 是无符号数,则 c 不是准确的答案。用abs保证c是正数。

【讨论】:

  • 如果您的意思是(学校数学)对自然数进行计算,则可能会发生“不是准确的答案”。如果您在 C++ 中超出其范围,无符号值将环绕,因此它始终为正数。在其上使用abs() 不应更改其值。
猜你喜欢
  • 2021-05-07
  • 2011-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-20
  • 2013-06-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多