我发现了这段代码,恰好我的分析器报告它是一个瓶颈:
#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>
。
c = (a > b) ? a - b : b - a;
或者
c = max(a, b) - min(a, b);
无符号数如果小于零将会回绕(效果类似于加上2的sizeof(unsigned long) * 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++
中使用 abs(来自 cmath
)函数
float abs( float arg );
double abs( double arg );
long double abs( long double arg );
s/sizeof(unsigned long)/sizeof(unsigned long) * CHAR_BIT
- The Paramagnetic Croissant我不知道您是否认为这是有意义的,但是应用于无符号值的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;
}
在使用GCC 4.9.1编译器在Mac OS X 10.10.1 Yosemite上将其编译为C或C++时,它会产生以下结果:
u1 = 0x98765432; abs(u1) = 0x6789ABCE
u2 = 0x9876543201234567; abs(u2) = 0x6789ABCDFEDCBA99
abs()
的结果不是传递给函数的值。abs()
返回的值将与传递给它的值不同。
#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); }
^
<stdlib.h>
以及<cstdlib>
,则有一个额外的重载可用于使调用更加模糊。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++头文件。
<stdlib.h>
,在C++中abs
应该是一个重载函数。但是,要么实现没有这样做,要么abs(int)
不会被选择为重载。 - user743382<stdlib.h>
,那就不行。这只为您提供标准C函数。我不知道哪些C++头文件定义了abs()
的重载。而且我不知道这些重载是否包括无符号类型。 - Jonathan Leffler<cstdlib>
被指定为包含C的<stdlib.h>
的声明,除了在命名空间std
中,还包括long abs(long)
和long long abs(long long)
。C++的<stdlib.h>
被指定为包含C++的<cstdlib>
的声明,除了在全局命名空间中。(引用C++11:“每个C头文件,其名称形式为name.h
,都表现得好像由相应的cname
头文件放置在标准库命名空间中的每个名称都被放置在全局命名空间范围内。”)这将包括添加的重载。但是实现存在错误,并且并非所有实现都能正确处理此问题。 - user743382<cstdlib>
而不是<stdlib.h>
,我就不会创建一个双语测试程序。我引用了我使用的环境以便于跟踪错误。今天稍后我可能会尝试一下是否使用<cstdlib>
而不是<stdlib.h>
有所不同;在没有实验的情况下,我不会进行调用。 - Jonathan Leffler我认为当c=a-b为负数时,如果c是无符号数,那么c不是准确的答案。使用abs函数可以保证c为正数。
abs()
函数不应改变其值。 - Ulrich Eckhardt
abs()
调用没有意义。但是,在 C++ 中,abs()
被重载并且也可能是一个宏,所以我不完全清楚它的作用。它甚至可以是一个接受signed long
的函数,然后根据参数的 MSB 做一些事情。为什么要取某些东西的绝对值,而这些东西不能为负数呢?如果没有原因,只需删除abs()
调用即可。 - Ulrich Eckhardta = 10UL;
和b = 30UL;
,正确的答案应该是20UL
,但是如果去掉abs
就会得到错误的答案。 - Ken Y-Nabs()
函数之前,10UL - 30UL
会得到4294967276UL
的结果(假设是32位无符号长整型)。如果abs()
函数按照其建议进行操作(请参考我的有关宏/重载的注释),则结果也是4294967276UL
。如果您想要得到20,您必须使用带符号的算术运算,例如abs(static_cast<long>(a) - static_cast<long>(b))
。但是,您必须确保这不会发生溢出,因为那样会产生“未定义行为”。总之,我建议您使用Mohit Jain的建议,即首先检查哪个更大。 - Ulrich Eckhardt