2015-01-08 12 views
9

ich auf diesen Code gekommen, die übrigens meine Profiler Berichte als Engpass:Macht abs (unsigned long) Sinn?

#include <stdlib.h> 

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

c = abs(a - b); 

diese Linie etwas mehr interessant, dass c = a - b; tun tut? Rufen beide Optionen nicht definiertes oder implementierungsdefiniertes Verhalten auf, und gibt es andere potenzielle Fehler? Beachten Sie, dass die C <stdlib.h> enthalten ist, nicht <cstdlib>.

+0

Da die Werte nicht vorzeichenbehaftet sind, können sie nicht negativ sein, daher ist der Aufruf 'abs() 'nicht sinnvoll. "Abs()" ist jedoch in C++ überladen und könnte auch ein Makro sein, also ist es für mich nicht 100% klar. Es könnte sogar eine Funktion sein, die 'signed long' nimmt, was dann etwas abhängig vom MSB des Arguments macht. Was ist der Grund, den absoluten Wert von etwas zu nehmen, das nicht negativ sein kann? Wenn es keine gibt, entfernen Sie einfach den 'abs()' Anruf. –

+2

Die mögliche Umwandlung von unsigned in int, die den Wert nicht darstellen kann, ist ein nicht definiertes Verhalten. Diese Umwandlung ist auch der einzige Fall, in dem diese Bauchmuskeln einen Unterschied machen würden. –

+0

@UlrichEckhardt Wenn 'a = 10UL;' und 'b = 30UL;', sollte die richtige Antwort '20UL' sein, aber die 'abs' zu fallen würde mir die falsche Antwort geben. –

Antwort

13

Nicht das ergibt keinen Sinn.

Wenn Sie den Unterschied möchten, verwenden Sie

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

oder

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

Unsigned wenn unter Null gehen würde wickeln zurück (Effekt ähnelt Zugabe von 2 sizeof (unsigned long) * CHAR_BIT)

Wenn Sie nach dem Unterschied zwischen zwei Zahlen suchen, können Sie eine kleine Vorlage wie unten schreiben

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

bei der Deklaration von abs von C geerbt Suchen (Weil Sie stdlib.h enthalten)

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) 

Und abs in C++ (von cmath)

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

Wenn Sie bemerken, , sowohl das Argument Art und Rückgabetyp jeder Funktion sind signed. Wenn Sie also einen vorzeichenlosen Typ an eine dieser Funktionen übergeben, würde die implizite Konvertierung unsigned T1 -> signed T2 -> unsigned T1 stattfinden (wobei T1 und T2 gleich sein können und T1 in Ihrem Fall long ist). Wenn Sie ein vorzeichenloses Integral in ein vorzeichenbehaftetes Integral konvertieren, ist das Verhalten implementierungsabhängig, wenn es nicht in einem signierten Typ dargestellt werden kann.

Von 4.7 Integral Konvertierungen [conv.integral]

  1. Wenn der Zieltyp nicht signiert ist, ist der sich ergebende Wert der am wenigsten unsigned integer kongruent zu der Quelle Ganzzahl (Modulo 2 n wobei n ist die Anzahl der Bits, die für den unsignierten Typ verwendet werden). [Hinweis: In einer Zweierkomplement-Darstellung ist diese Konvertierung konzeptuell und es gibt keine Änderung im Bitmuster (wenn keine Kürzung erfolgt). - Endnote]
  2. Wenn der Zieltyp signiert ist, ist der Wert unverändert, wenn er im Zieltyp (und Bitfeldbreite) dargestellt werden kann; andernfalls ist der Wert implementierungsdefiniert.
+0

Beachten Sie, dass dies zwar nicht für 'unsigned long' oder 'unsigned int' funktioniert, aber für 'unsigned char' und 'unsigned short' funktioniert (außer auf pathologischen Systemen, die niemand benutzt). –

+0

Ich sehe nicht, wie ein System, wo sizeof (short) == sizeof (int) wäre viel pathologischer als ein System, wo sizeof (int) == sizeof (lang) (nur auf der Suche nach einer Chance, sich lustig zu machen ' der Mann ') – dwn

+0

's/sizeof (unsigned lang)/sizeof (unsigned lang) * CHAR_BIT' –

0

denke ich, wenn c a-b ist negativ =, wenn c unsigned Zahl ist, c nicht die genaue Antwort. Die Verwendung von abs zur Garantie von c ist eine positive Zahl.

+2

"nicht die genaue Antwort" kann passieren, wenn Sie (Schulmathematik) Berechnungen über natürliche Zahlen meinen. Nicht vorzeichenbehaftete Werte werden umgebrochen, wenn Sie ihren Bereich stattdessen in C++ überschreiten, sodass sie immer positiv sind. Mit 'abs()' sollte der Wert dann nicht geändert werden. –

7

Ich weiß nicht, ob Sie es als einen Sinn betrachten würden, aber abs() auf einen Wert ohne Vorzeichen angewandt kann sicherlich einen anderen Wert als die übergebenen eines zurückzukehren. Das ist, weil abs() ein int Argument verwendet und einen int Wert .

Zum Beispiel:

#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 = 0x98765432UL; 
    printf("u2 = 0x%.16lX; abs(u2) = 0x%.16lX\n", u2, labs(u2)); 
    return 0; 
} 

Wenn wie C oder C++ kompiliert (mit GCC 4.9.1 auf Mac OS X 10.10.1 Yosemite), es produziert:

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

Wenn das hohe Bit Wenn der vorzeichenlose Wert gesetzt ist, ist das Ergebnis von abs() nicht der Wert, der an die Funktion übergeben wurde.

Die Subtraktion ist lediglich eine Ablenkung; Wenn für das Ergebnis das höchstwertige Bit gesetzt ist, unterscheidet sich der von abs() zurückgegebene Wert von dem an ihn übergebenen Wert.


Wenn Sie diesen Code mit C++ Header kompilieren, anstelle der C-Header in der Frage gezeigt, dann scheitert es mit mehrdeutigen Aufruf Fehler zu kompilieren:

#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 = 0x98765432UL; 
    cout << "u2 = 0x" << hex << u2 << "; abs(u2) = 0x" << hex << abs(u2) << "\n"; 
    return 0; 
} 

Compilation Fehler:

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); } 
^

Also, der Code in der Frage kompiliert nur, wenn nur die C-style-Header verwendet werden; Es kompiliert nicht, wenn die C++ - Header verwendet werden. Wenn Sie <stdlib.h> sowie <cstdlib> hinzufügen, gibt es eine zusätzliche Überladung verfügbar, um die Aufrufe mehrdeutig zu machen.

Sie können den Code kompiliert werden, wenn Sie den Aufforderungen abs(), und der absolute Wert einer signierten Menge (in) geeignete Abgüsse hinzufügen können vom Original unterzeichnet Menge unterschiedlich sein, die kaum überraschende Nachricht ist:

#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 = 0x98765432UL; 
    cout << "u2 = 0x" << hex << u2 << "; abs(u2) = 0x" << hex << abs(static_cast<long>(u2)) << "\n"; 
    return 0; 
} 

Output:

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

Moralische: verwenden Sie die C-Headern nicht für die es C++ Äquivalente in C++ Code; Verwenden Sie stattdessen die C++ - Header.

+0

'abs' soll eine überladene Funktion in C++ sein, auch wenn' 'enthalten ist. Entweder tut die Implementierung das nicht, oder "abs (int)" wird nicht die Überladung sein, die ausgewählt wird. – hvd

+0

Nicht, wenn Sie nur '' einschließen, denke ich. Das bietet Ihnen nur die Standard-C-Funktionen. Nebenbei, ich weiß nicht, welche C++ Header Überladungen für 'abs()' definieren. Und ich weiß nicht, ob die Überladungen vorzeichenlose Typen enthalten. –

+0

'' wird angegeben, dass es die '''s Deklarationen von C enthält, außer im Namespace' std', plus 'long abs (long)' und 'long long abs (long long)'. C++ 's' 'wird angegeben, dass es C++ 's' ' s Deklarationen enthält, außer im globalen Namespace. (Um C++ 11 zu zitieren: "Jeder C-Header, von denen jeder einen Namen der Form' name.h' hat, verhält sich so, als ob jeder Name, der durch den entsprechenden 'cname'-Header in den Standard-Bibliotheksnamensraum gesetzt wird, innerhalb der globaler Namespace-Bereich. ") Dies würde die zusätzlichen Überlastungen beinhalten. Aber Implementierungen haben Fehler, und nicht alle bekommen das richtig. – hvd

Verwandte Themen