Значения Signed и unsigned для подсчета в цикле

таким образом, у меня есть в программе обычный цикл for через вектор объектов (объектов, которые имеют определенный тип I, Если это актуально):

for(int k = 0; k < objects.size(); k++){ ... }

...и когда я компилирую, я получаю это предупреждение:

warning: comparison between signed and unsigned integer expressions 

это имеет смысл, так как я думаю size() для вектора возвращает size_t. Но какое это имеет значение? Разве определенное количество элементов (или даже блоков памяти) не является целым числом, которое вы можете подсчитать? Что еще более важно, так как моя программа имеет несколько таких циклов и случается с segfault много, может ли это быть частью этого?

4 ответов


проблема возникает, когда object.size() возвращает значение больше, чем максимальное представимое значение k. С k подписано, оно имеет только половину максимального значения по сравнению с size_t1.

теперь это может не произойти в вашем конкретном приложении (в типичной 32-битной системе, которая будет более двух миллиардов объектов в вашей коллекции), но всегда полезно использовать правильные типы.

1. Упреждающее опровержение: Да, это верно только для машин, использующих типичную арифметику дополнения двух, и для машин, где int и size_t представлены с использованием того же количества битов.


хорошо ответил, Уже, но я добавлю свой S / 0.02: "правильный" способ сделать это:

for (typename std::vector<MyObject>::size_type i = 0; i < object.size(); ++i) { ... }

только начинающие юристы языка напишут это, и даже они, вероятно, перестанут читать, прежде чем они доберутся до хорошего материала.

С C++11 вы можете воспользоваться decltype:

for (decltype(object.size()) i = 0; i < object.size(); ++i) { ... }

или вы можете воспользоваться auto:

for (auto i = object.size() - object.size(); i < object.size(); ++i) { ... }

или вы можете просто использовать size_t, но у вас все еще могут быть сомнения в переполнении, так как vector<MyObject>size_type может быть больше, чем size_t. (Это не так, но нет никаких гарантий):

for (size_t i = 0; i < object.size(); ++i) { ... }

так что же делать честному программисту?

абсолютно простым решением является то, которое STL продвигает с самого начала. Кроме того, в начале было также больно писать:

for (typename std::vector<MyObject>::iterator_type it = object.begin(); it != object.end(); ++it) { ... }

теперь C++11 действительно помогает вам. У вас есть очень хорошие альтернативы, начиная с простого:

for (auto it = object.begin(); it != object.end(); ++it) { ... }

но он получает еще лучше (барабанная дробь, пожалуйста)...:

for (auto& val : object) { ... }

и это тот, который я бы использовал.


редактировать, чтобы добавить:

Кори Нельсон в комментарии указывает, что также можно кэшировать результат объекта.конец() с:

for (auto it = object.begin(), end = object.end(); it != end; ++it) { ... }

оказывается, что код, сгенерированный for (var : object) синтаксис очень похож на предложенный Кори Нельсон. (Поэтому я бы посоветовал ему и вам просто использовать последнее.)

однако, это имеет тонко различная семантика от других, включая итерацию, которая была предметом исходного сообщения. Если вы изменяете контейнер во время итерации таким образом, чтобы изменить его размер, вам нужно очень тщательно все продумать. Катастрофа весьма вероятна.

единственный способ итерации вектора, который может быть изменен во время итерации, - использовать целочисленные индексы, как в исходном сообщении. Другие контейнеры более снисходительны. Вы можете выполнить итерацию карты STL с помощью цикл, который вызывает объект.end () на каждой итерации, и (насколько я знаю) он будет работать даже перед вставками и удалениями, но не пытайтесь это сделать с unordered_map или вектором. Он работает с deque, если вы всегда нажимаете в конце и хлопаете спереди, что удобно, если вы используете deque в качестве очереди в широте первой прогулки; я не уверен, что вы можете уйти с хлопком deque сзади.

там действительно должно быть простое резюме где-то влияние типа контейнера модификаций контейнера на итераторы и указатели элементов (которые не всегда совпадают с итераторами), так как все это указано стандартом, но я никогда не сталкивался с ним нигде. Если найдете, дайте мне знать.


в большинстве случаев это не имеет значения, пока вы не нажмете точку, что ваш вектор содержит больше элементов, чем может представлять подписанный int.


важные предупреждения могут быть потеряны в потоке предупреждений о подписанных и неподписанных сравнение переменных цикла. И даже некоторые подписанные/неподписанные предупреждения сравнения важны! Итак, избавьтесь от неважных предупреждений, определив функцию размера, например:

#include <stddef.h>    // ptrdiff_t
#include <utility>     // std::begin, std::end

typedef ptrdiff_t Size;
typedef Size Index;

template< class Type >
Size nElements( Type const& c )
{
    using std::begin;  using std::end;
    return end( c ) - begin( c );
}

затем вы можете просто написать, например,

for( int i = 0;  i < nElements( v );  ++i ) { ... }

кроме того, использовать итераторы, например,

for( auto it = begin( v );  it != end( v );  ++it ) { ... }

и/или использовать с++11 на основе for петля,

for( auto const& elem : v ) { ... }

в любом случае, получение чистой компиляции на самом высоком практическом уровне предупреждения важно для того, чтобы избавиться от этих segfaults и других ошибок.

еще одна область, на которую вы должны посмотреть, это C style casts: избавьтесь от них! ;-)