abs(unsigned long)有意义吗?

Ken*_*Y-N 13 c c++

我遇到过这个代码,顺便说一下,我的探查器报告是一个瓶颈:

#include <stdlib.h>

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

c = abs(a - b);
Run Code Online (Sandbox Code Playgroud)

那条线路做了c = a - b;哪些更有趣的事情吗?任何一个选项都会调用未定义或实现定义的行为,还有其他潜在的陷阱吗?请注意,C <stdlib.h>不包括在内<cstdlib>.

Moh*_*ain 19

不是没有意义.

如果你想要差异,请使用

c = (a > b) ? a - b : b - a;
Run Code Online (Sandbox Code Playgroud)

要么

c = max(a, b) - min(a, b);
Run Code Online (Sandbox Code Playgroud)

如果低于零则无符号将回绕(效果类似于添加2 sizeof(无符号长整数)*CHAR_BIT)

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

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

看看继承自的绝对宣言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)
Run Code Online (Sandbox Code Playgroud)

ABSC++(从cmath)

float       abs( float arg );
double      abs( double arg );
long double abs( long double arg );
Run Code Online (Sandbox Code Playgroud)

如果你注意到,每个函数的参数和返回类型都是signed.所以,如果你传递一个无符号的类型,这些功能之一,隐式转换unsigned T1 -> signed T2 -> unsigned T1会发生(其中T1,并T2可能是相同的,T1就是long你的情况).将无符号整数转换为有符号整数时,如果无法以有符号类型表示,则该行为是实现依赖项.

4.7积分转换[conv.integral]

  1. 如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模2 n,其中n是用于表示无符号类型的位数).[注意:在二进制补码表示中,此转换是概念性的,并且位模式没有变化(如果没有截断). - 结束说明]
  2. 如果目标类型已签名,则如果可以在目标类型(和位字段宽度)中表示该值,则该值不会更改; 否则,该值是实现定义的.


Jon*_*ler 7

我不知道你是否认为它有意义,但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;
}
Run Code Online (Sandbox Code Playgroud)

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

u1 = 0x98765432; abs(u1) = 0x6789ABCE
u2 = 0x9876543201234567; abs(u2) = 0x6789ABCDFEDCBA99
Run Code Online (Sandbox Code Playgroud)

如果设置了无符号值的高位,则结果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;
}
Run Code Online (Sandbox Code Playgroud)

编译错误:

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); }
   ^
Run Code Online (Sandbox Code Playgroud)

因此,问题中的代码仅在仅使用C样式标题时编译; 使用C++标头时它不会编译.如果添加<stdlib.h>以及<cstdlib>,有可用,使通话更加暧昧额外的过载.

如果向调用中添加(in)适当的强制转换,则可以编译代码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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

u1 = 0x98765432; abs(u1) = 0x6789abce
u2 = 0x9876543201234567; abs(u2) = 0x6789abcdfedcba99
Run Code Online (Sandbox Code Playgroud)

道德:不要使用C++代码中存在C++等价物的C头; 请改用C++标头.