我遇到过这段代码,偶然我的个人资料报告是一个瓶颈:
#include <stdlib.h>
unsigned long a, b;
// Calculate values for a and b
unsigned long c;
c = abs(a - b);
该行是否更有趣c = a - b;
?任何一个选项都会调用未定义或实现定义的行为,还有其他潜在的陷阱吗?请注意,我们会包含C <stdlib.h>
,而不是<cstdlib>
。
答案 0 :(得分:19)
不是没有意义。
如果您想要区别,请使用
c = (a > b) ? a - b : b - a;
或
c = max(a, b) - min(a, b);
无符号,如果低于零会回绕(效果类似于添加2 sizeof(无符号长)* CHAR_BIT )
如果您要查找两个数字之间的差异,可以编写一个小模板,如下所示
namespace MyUtils {
template<typename T>
T diff(const T&a, const T&b) {
return (a > b) ? (a - b) : (b - a);
}
}
查看从C
继承的abs声明(因为您包含了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++
cmath
(来自float abs( float arg );
double abs( double arg );
long double abs( long double arg );
)
signed
如果您注意到,每个函数的参数和返回类型都是unsigned T1 -> signed T2 -> unsigned T1
。因此,如果将无符号类型传递给其中一个函数,则会发生隐式转换T1
(T2
和T1
可能相同,long
为{{1}在你的情况下)。将无符号整数转换为有符号整数时,如果无法以有符号类型表示,则该行为是实现依赖性。
来自 4.7积分转换[conv.integral]
- 如果目标类型是无符号的,则结果值最小 无符号整数与源整数一致(模2 n ,其中n为 用于表示无符号类型的位数)。 [注意:在 两个补码表示,这种转换是概念性的 位模式没有变化(如果没有截断)。 - 尾注]
- 如果目标类型已签名,则值可以保持不变 以目标类型(和位字段宽度)表示;除此以外, 该值是实现定义的。
醇>
答案 1 :(得分: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;
}
当编译为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 ++标头时它不会编译。如果您添加<stdlib.h>
以及<cstdlib>
,则可以使用额外的重载来使调用更加模糊。
如果您向abs()
的调用添加(in)适当的强制转换,您可以编译代码,并且签名数量的绝对值可能与原始签名数量不同,这不是什么令人惊讶的消息:
#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 ++标题。
答案 2 :(得分:-1)
我认为当c = a-b为负数时,如果c为无符号数,则c不是准确答案。使用abs来保证c是一个正数。