2013-03-29 39 views
16

Ta deklaracja kompiluje bez ostrzeżenia w g ++ -pedantic -Wall (wersja 4.6.3):Jak wykryć liczbę ujemną przypisaną do size_t?

std::size_t foo = -42; 

Mniej widoczny podrobiony deklaruje funkcję z argumentem size_t i nazywając ją z wartością ujemną. Czy taka funkcja może zabezpieczyć się przed nieumyślnym negatywnym argumentem (który pojawia się jako szesnaście kwintillionu, przestrzegając §4.7/2)?

Niekompletne odpowiedzi:

Tylko zmieniając size_t do (podpisany) długich odrzutów semantyki i inne zalety size_t.

Zmiana na ssize_t jest zwykłym POSIX, a nie standardowym.

Zmiana na ptrdiff_t jest krucha, a czasem zepsuta.

Testowanie ogromnych wartości (zestaw bitów wysokiego rzędu itd.) Jest arbitralne.

+0

Powiązane http://stackoverflow.com/questions/2711522/what-happens-if-i-assign-a-negative-value-to-an-unsigned-variable – halex

+2

+1 dla "mnoga kwintillion". Jk, +1, bo to dobre pytanie. –

+0

Dziękuję, halex. Właśnie tam przytoczyłem standard. –

Odpowiedz

3

Problem z ostrzeżeniem o tym, że nie jest niezdefiniowanym zachowaniem zgodnym ze standardem. Jeśli konwertujesz podpisaną wartość na niepodpisany typ tego samego rozmiaru, możesz później przekonwertować ją z powrotem na wartość podpisaną i uzyskać oryginalną wartość na dowolnym kompilatorze zgodnym ze standardami.

Ponadto, przy użyciu wartości ujemne przekształcane size_t jest dość powszechne dla różnych warunków błędu - wiele wywołań systemowych zwrócić bez znaku (size_t lub off_t) Wartość dla sukcesu lub -1 (w przeliczeniu na unsigned) w przypadku błędu. Dodanie takiego ostrzeżenia do kompilatora spowodowałoby fałszywe ostrzeżenia dla znacznie istniejącego kodu. POSIX próbuje to skodyfikować za pomocą ssize_t, ale zrywa połączenia, które mogą zakończyć się pomyślnie, zwracając wartość większą niż maksymalna wartość podpisu dla ssize_t.

+0

Wykrywanie czasu wykonywania jest tu większym problemem niż ostrzeżenia kompilatora. Ale dałeś mi pomysł, w funkcji, rzucanie wartości z powrotem do podpisu i testowanie dla <0. To działa. Więc zadzwonię to odpowiedział. –

+0

@ CamilleGoudeseune D'oh, konwersja z powrotem do podpisu i testowanie <0 jest niezdefiniowanym zachowaniem. Wybierz maksymalną wartość, na przykład 'LONG_MAX' lub' std :: numeric_limits < long > :: max() 'i porównaj wartość bez znaku do tej wartości, bez opcji typowania. – Potatoswatter

+0

@hvd: Chodzi o to, że specyfikacja umożliwia odlewanie w tę iz powrotem przy użyciu czcionek o tym samym rozmiarze, które są podpisane i niepodpisane, i uzyskuje tę samą wartość, nawet jeśli zakresy są różne. –

1

Poniższy fragment pochodzi z prywatnej biblioteki.

#include <limits.h> 

#if __STDC__ == 1 && __STDC_VERSION__ >= 199901L || \ 
    defined __GNUC__ || defined _MSC_VER 
    /* Has long long. */ 
    #ifdef __GNUC__ 
     #define CORE_1ULL __extension__ 1ULL 
    #else 
     #define CORE_1ULL 1ULL 
    #endif 
    #define CORE_IS_POS(x) ((x) && ((x) & CORE_1ULL << (sizeof (x)*CHAR_BIT - 1)) == 0) 
    #define CORE_IS_NEG(x) (((x) & CORE_1ULL << (sizeof (x)*CHAR_BIT - 1)) != 0) 
#else 
    #define CORE_IS_POS(x) ((x) && ((x) & 1UL << (sizeof (x)*CHAR_BIT - 1)) == 0) 
    #define CORE_IS_NEG(x) (((x) & 1UL << (sizeof (x)*CHAR_BIT - 1)) != 0) 
#endif 

#define CORE_IS_ZPOS(x) (!(x) || CORE_IS_POS(x)) 
#define CORE_IS_ZNEG(x) (!(x) || CORE_IS_NEG(x)) 

Powinno działać z wszystkimi rodzajami bez znaku.

+0

Dobre wykorzystanie funkcji CHAR_BIT, http://stackoverflow.com/a/3200969/2097284. Ale co za niejawny casting sprawia, że ​​to działa? '(x) & ...' rzuca x na ULL? '(x) &&' oznacza '(x! = 0) &&'? A ZPOS oznacza '> = 0'? –

+1

Tak, testy 'IS_POS' /' IS_NEG' dla '> 0' i' <0'; oraz testy 'IS_ZPOS' /' IS_ZNEG' dla '> = 0' i' <= 0'. W grę nie wchodzi żadna obsada; makra sprawdzają jedynie bit najwyższego rzędu (część '(x) & ...}, używając największego bez znaku typu całkowitego dostępnego do skonstruowania wyrażenia maski bitowej (stąd UL i ULL). Część '(x) && ...' oznacza po prostu '(x)! = 0'. – alecov