2015-01-31 21 views
5

Ich habe gerade so etwas C++ Code gesehen. Es wurde eine Bedingung verwendet, um zu entscheiden, ob man vorwärts oder rückwärts durch eine std::vector gehen soll. Der Compiler beschwert sich nicht, aber ich dachte size_t war nicht signiert. Ist das gefährlich?Ist es sicher, negative ganze Zahlen mit size_t zu verwenden?

vector<int> v { 1,2,3,4,5 };  
bool rev = true; 

size_t start, end, di; 
if (rev) { 
    start = v.size()-1; 
    end = -1; 
    di = -1; 
} 
else { 
    start = 0; 
    end = v.size(); 
    di = 1; 
} 

for (auto i=start; i!=end; i+=di) { 
    cout << v[i] << endl; 
} 
+1

Der Standard definiert 'std :: string :: npos' als' static const size_type npos = -1; 'was im Grunde ein ähnlicher Trick ist. Um pedantisch zu sein, könnten Sie bevorzugen: 'std :: vector :: size_type start, end, di;'. – Galik

+0

@Galic: Ich verstehe nicht, was die Pedanterie (wie du es nennst) erreicht, welchen Vorteil sie hat. Wenn ein Betreuer seinen Weg verlässt, um Dinge zu brechen, indem er 'std :: vector' mit einem Zuweiser instanziiert eine alberne Größe, alles, was die Pedanterie tut, erhöht die Chance, dass es ihr gelingt, Dinge zu verderben. Wie ich es sehe. –

+0

@ Cheersandthth.-Alf Nun, du hast wahrscheinlich recht. Soweit ich sagen kann, obwohl der Standard besagt, dass 'std :: vector :: size_type' muss unsigned sein, aber ich denke, es kann eine andere Größe sein als' std :: size_t'. Obwohl ich mir vorstelle, dass die meisten Implementierungen "size_type" genauso machen wie "size_t". – Galik

Antwort

6

Es ist gut ganze Zahlen ohne Vorzeichen definiert zu verwenden (und size_t ist unsigned) auf diese Weise, mit Wraparound: das Verhalten von der Norm gewährleistet ist, im Gegensatz zu mit signierten ganzen Zahlen, wo es ist nicht durch den Standard garantiert.

Es ist jedoch unnötig clever.

Um Probleme aufgrund von impliziten Wrapping-Aktionen zu vermeiden, verwenden Sie vorzeichenlose Ganzzahlen für Bitebenen, verwenden Sie vorzeichenbehaftete Ganzzahlen für Zahlen. Wo Sie eine vorzeichenbehaftete Ganzzahl benötigen, die size_t entspricht, gibt es ptrdiff_t für Sie. Definieren Sie eine n_items-Funktion mit einem vorzeichenbehafteten Ergebnis, z.

using Size = ptrdiff_t; 

template< class Container > 
auto n_items(Container const& c) 
    -> Size 
{ return end(c) - begin(c); } 

und Sie sind bereit zu gehen, keine dummen Warnungen vom Compiler.


Statt des zu klug gegebenen Code

vector<int> v { 1,2,3,4,5 };  
bool rev = true; 

size_t start, end, di; 
if (rev) { 
    start = v.size()-1; 
    end = -1; 
    di = -1; 
} 
else { 
    start = 0; 
    end = v.size(); 
    di = 1; 
} 

for (auto i=start; i!=end; i+=di) { 
    cout << v[i] << endl; 

tun z.B.

const vector<int> v { 1,2,3,4,5 };  
const bool reverse = true; // whatever 

for(int i = 0; i < n_items(v); ++i) 
{ 
    const int j = (reverse? n_items(v) - i - 1 : i); 
    cout << v[j] << endl; 
} 
0

Ist es sicher, mit size_t negativen ganzen Zahlen zu benutzen?

Nein, es ist gefährlich. Überlauf.

size_t a = -1; 
std::cout << a << "\n"; 

Ausgang:

4294967295 // depends on the system, largest value possible here 
+1

Dies ist kein gefährlicher Überlauf, aber wohldefinierte Wrap-around. Das Ausgeben negativer Zahlen in size_t ist eine übliche Methode, um Fehler von Funktionen zurückzugeben, die normalerweise eine Größe zurückgeben. Die C-Standardbibliothek selbst tut dies zum Beispiel in Funktionen wie 'mbrtoc32()'. – dpi

1

Ich kann nicht zu sprechen, wie sicher, dass Code ist, aber ich denke, es ist ein ziemlich schlechter Stil. Ein besserer Weg wäre die Verwendung von Iteratoren, die eine Vorwärts- oder Rückwärtsiteration unterstützen.

Zum Beispiel:

std::vector<int> v = { 1, 2, 3, 4, 5 }; 
bool rev = true; 

if (rev) 
{ 
    for (auto itr = v.rbegin(); itr != v.rend(); ++itr) 
    { 
     std::cout << *itr << "\n"; 
    } 
} 
else 
{ 
    for (auto itr = v.begin(); itr != v.end(); ++itr) 
    { 
     std::cout << *itr << "\n"; 
    } 
} 
+0

Irgendwie kompiliert dieser Code nicht mit g ++ 4.8.2. Kannst du irgendeinen Grund denken? –

+1

Ich bin mir nicht sicher (es könnte damit zu tun haben, dass * rend() * und * begin() * nicht zum selben Typ ausgewertet werden). Es war eine konzeptionelle Antwort; Ich habe es nicht getestet. Ich werde mit etwas aktualisieren, das tatsächlich kompiliert. –

0

Jedes Mal, wenn ich brauche mit signierten Typen umgehen, verwende ich immer:

typedef std::make_signed<std::size_t>::type ssize_t; // Since C++11 

... als signierte Alternative zu std :: size_t.

Ich schätze diese Frage ist ein paar Jahre alt, aber ich hoffe, dass andere helfen werden. Kredit an moodycamel::ConcurrentQueue.