— Меня вот всегда удивляло, почему в качестве счетчика цикла, который будет использоваться в качестве индексации масива / контейнера, зачастую используется знаковая целочисленная переменная?
Нет, ну я понимаю, что size_t многим просто не нужен — пишу себе под 32 бита и буду писать, думают они.
А вот почему бы не написать unsigned int, к примеру — ума не приложу. Лень написать лишние буквы?
— Кстати, как часто, когда вы имеете дело с контейнерами, вы используете итераторы? Оператор индексации / метод at настолько же часто?
— Допустим, нам надо выделить два одинаковых по размеру куска памяти одного и того же типа.
Здравствуйте, Аноним, Вы писали:
А>- Допустим, нам надо выделить два одинаковых по размеру куска памяти одного и того же типа.
А>Использовали бы стандартный вариант
А>
А>int *a = new int [n];
А>int *b = new int [n];
А>
А>или
А>
А>int *a = new int [n * 2];
А>int *b = a + n;
А>
А>вот такой?
ни тот ни другой, стандартный вариант с динамикой ну ни как не связан:
Здравствуйте, Аноним, Вы писали:
А>Приветствую.
А>- Меня вот всегда удивляло, почему в качестве счетчика цикла, который будет использоваться в качестве индексации масива / контейнера, зачастую используется знаковая целочисленная переменная?
А>Нет, ну я понимаю, что size_t многим просто не нужен — пишу себе под 32 бита и буду писать, думают они.
А>А вот почему бы не написать unsigned int, к примеру — ума не приложу. Лень написать лишние буквы?
Здесь есть несколько факторов, которые влияют на это решение.
Во-первых, исторически так сложилось, что использовали тип int для счетчиков циклов, так как в С еще не существовало типа unsigned int. Этот тип появился позднее.
Во-вторых, есть некоторое противоречие. Дело в том, что разность между указателямми и итераторами, которую определяют, как имеющую тип difference_type, выражают через знаковый целочисленный тип, например, используют стандартный тип std::ptrdiff_t. В чем заключается противоречие? С одной стороны, обычно для всех контейнеров задают тип size_type как беззнаковый целочисленный тип. С другой стороны, разность между двумя указателями на элементы контейнера, которая как раз и определяет индекс элемента, задается как difference_type, то есть знаковый тип. То есть имеет место быть, что если n — это size_type, то есть беззанковый, то разность между элементами контейнера с прямым доступом, для которого память выделяется одним экстентом, &a[n] — &a[0] в общем случае не равна n. То есть в первом выражении мы получаем знаковое значение, а во втором беззнаковое, а знаковое не может представить в себе все значения беззнакового. Имеет место явное противоречие.
Это противоречие наглядно проявляется в объявление стандартных алгоритмов. Например, посмотрите объявление алгоритма count
template<class InputIterator, class T>
typename iterator_traits<InputIterator>::difference_type
count(InputIterator first, InputIterator last, const T& value);
С одной стороны вы его можете вызвать для массива, указав диапазон, как, [a, a + N), где N — это беззнаковое целое, как, например, size_t. А на выходе из вызова алгоритма вы получите уж знаковое значение, как, например, int.
Вот, чтобы избежать такого противоречия, программисты часто и используют знаковый тип в циклах.
А>- Кстати, как часто, когда вы имеете дело с контейнерами, вы используете итераторы? Оператор индексации / метод at настолько же часто?
А>- Допустим, нам надо выделить два одинаковых по размеру куска памяти одного и того же типа.
А>Использовали бы стандартный вариант
А>
А>int *a = new int [n];
А>int *b = new int [n];
А>
А>или
А>
А>int *a = new int [n * 2];
А>int *b = a + n;
А>
А>вот такой?
Это плохой способ выделения памяти, так как кто-то может попытаться удалить указатель b, который не указывает на начало выделенной памяти, а потому это может привести к аварийному завершению программы. Так можно делать лишь в том случае, если вы самостоятельно разррабатываете системы выделения памяти для своей программы и, соответственно перегружаете операторы delete и delete[].
On 02/21/2012 10:59 PM, Аноним 226 wrote:
> — Меня вот всегда удивляло, почему в качестве счетчика цикла, который будет > использоваться в качестве индексации масива / контейнера, зачастую используется > знаковая целочисленная переменная?
Меня тоже. Поэтому использую всегда беззнаковые.
> — Кстати, как часто, когда вы имеете дело с контейнерами, вы используете > итераторы? Оператор индексации / метод at настолько же часто?
Всегда почти при итерации. Всегда при работе алгоритмов.
индексацию можно применять только если это -- контейнес с произвольным доступом.
> — Допустим, нам надо выделить два одинаковых по размеру куска памяти одного и > того же типа. > > Использовали бы стандартный вариант > > int *a =new int [n]; > int *b =new int [n];
Да, потому что не факт, что у них будет одинаковое время жизни (т.е. что их
удалять в одно и то же время).
Если будет, то надо делать из них структуру или массив.
Здравствуйте, Аноним, Вы писали:
А>А вот почему бы не написать unsigned int, к примеру — ума не приложу. Лень написать лишние буквы?
А зачем? Если разницы никакой — то да, лень.
А задачи, где разница есть, встречаются настолько редко, что далеко не каждому программисту хоть раз в жизни попадется.
Здравствуйте, Аноним, Вы писали:
А>- Кстати, как часто, когда вы имеете дело с контейнерами, вы используете итераторы? Оператор индексации / метод at настолько же часто?
Если можно индексом, то всегда перебираю индексом. Так короче писать.
Здравствуйте, B0FEE664, Вы писали:
BFE>Здравствуйте, Аноним, Вы писали:
А>>А вот почему бы не написать unsigned int, к примеру — ума не приложу. Лень написать лишние буквы?
BFE>Попробуйте написать цикл с использованием unsigned int, который перебирает элементы массива в обратном порядке — от хвоста к голове.
Это так сложно?!
const size_t N;
int a[N];
size_t n = N;
while ( n-- ) a[n] = n;
Здравствуйте, Аноним, Вы писали:
А>- Меня вот всегда удивляло, почему в качестве счетчика цикла, который будет использоваться в качестве индексации масива / контейнера, зачастую используется знаковая целочисленная переменная?
Если говорить о встроенной операции индексирования вообще, то согласно стандарту она сводится к сложению указателя с целым числом и последующим разыменованием. И точно так же, как и операция сложения указателя с целым числом, операция индексирования обладает свойством коммутативности. Так, для любых массива array и целочисленного индекса index следующие выражения являются эквивалентными:
И точно так же, как и операция сложения указателя с целым числом, операция индексирования не накладывает никаких ограничений на знак целочисленного индекса. Т.о. само по себе использование индексирования вовсе не означает, что индекс обязан быть положительным и беззнаковым. Во многих случаях в качестве индексов действительно лучше использовать беззнаковые величины, но, во-первых, не абсолютно всегда, а, во-вторых, по иным соображениям, не связанным с использованием операции индексирования как таковой.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, B0FEE664, Вы писали:
BFE>Попробуйте написать цикл с использованием unsigned int, который перебирает элементы массива в обратном порядке — от хвоста к голове.
Стандартная идиома:
for (unsigned i = n; i --> 0 ;)
std::cout << i << std::endl;
Здравствуйте, Centaur, Вы писали:
BFE>>Попробуйте написать цикл с использованием unsigned int, который перебирает элементы массива в обратном порядке — от хвоста к голове. C>Стандартная идиома:
C>
C>for (unsigned i = n; i --> 0 ;)
C> std::cout << i << std::endl;
C>
BFE>Так можно. Но это не цикл for, который был бы естественен здесь.
Это что-то на вас сегодня погода плохо действует. На самом деле имеет место законченное предложение for. Я, кстати сказать, когда написал while, подумал, а почему я не использовал for? Да исправить не знал как.
Но, на самом деле, гемор начинается чуть позже, когда тебе надо поитерировать массив с конца и с шагом step.
Все проблемы лечатся, конечно, кодом вроде такого:
for( unsigned int i = N - 1; i < N; i -= STEP )
но я бы не назвал его интуитивно понятным...
Второй минус такого подхода -- он на итераторы не обобщается, но в итераторах и не надо ходить вниз, с другой стороны.
Но я вообще противник использования беззнаковых целых, без крайней на то нужды. Слишком легко допустить переполнение...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, B0FEE664, Вы писали:
BFE>>>Попробуйте написать цикл с использованием unsigned int, который перебирает элементы массива в обратном порядке — от хвоста к голове. C>>Стандартная идиома:
C>>for (unsigned i = n; i --> 0 ;)
C>> std::cout << i << std::endl;
BFE>Стандартная? А где массив?
Вот честное слово, не вижу разницы для иллюстративных целей — индексировать массив числами от 9 до 0 или выводить в стандартный вывод числа от 9 до 0.
Это "неправильный for" только в вашей голове. Любой компилятор на это for не жалуется, так как конструкция написана в соответствии со стандартом, в котором предложение for определяется следующим образом
for ( for-init-statement conditionopt; expressionopt) statement
Обратите внимание, что у элемента expression справа приписано opt, что означает optional, то есть необязательный.
Можно вообще писать предложение for в виде
for ( ;; ) statement;
E>Но, на самом деле, гемор начинается чуть позже, когда тебе надо поитерировать массив с конца и с шагом step.
E>Все проблемы лечатся, конечно, кодом вроде такого:
for( unsigned int i = N - 1; i < N; i -= STEP )
но я бы не назвал его интуитивно понятным... E>Второй минус такого подхода -- он на итераторы не обобщается, но в итераторах и не надо ходить вниз, с другой стороны.
Кто вам сказал, что в итераторах не надо "ходить вниз"?! А как быть с алогритмом copy_backward, reverse и со всеми реверсивными итераторами?!
E>Но я вообще противник использования беззнаковых целых, без крайней на то нужды. Слишком легко допустить переполнение...
Без крайней нужды не следует использовать те типы, область значений которых не соответствует области используемых значений. То есть в циклах с индексом как раз без крайней нужды не слледует использовать знаковые типы, так как предполагается, что индекс не может иметь отрицательное значение.
С>Это "неправильный for" только в вашей голове. Любой компилятор на это for не жалуется, так как конструкция написана в соответствии со стандартом, в котором предложение for определяется следующим образом
"Неправильный", в данном случае, обозначает "нечитабельный"...
С>Кто вам сказал, что в итераторах не надо "ходить вниз"?! А как быть с алогритмом copy_backward, reverse и со всеми реверсивными итераторами?!
Тот, кто рассказал об обратных иетраторах...
С>Без крайней нужды не следует использовать те типы, область значений которых не соответствует области используемых значений. То есть в циклах с индексом как раз без крайней нужды не слледует использовать знаковые типы, так как предполагается, что индекс не может иметь отрицательное значение.
Ну вот я считаю такой подход ошибочным. Аргументация -- слишком велик шанс допустить переполнение и не заметить этого. А какой смысл в твоём подходе? Что он даёт? ограничения же не проверяются?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
On 02/22/2012 01:22 PM, Erop wrote:
> > "Неправильный", в данном случае, обозначает "нечитабельный"... > > С>Кто вам сказал, что в итераторах не надо "ходить вниз"?! А как быть с
Здравствуйте, Centaur, Вы писали:
BFE>>>>Попробуйте написать цикл с использованием unsigned int, который перебирает элементы массива в обратном порядке — от хвоста к голове. C>>>Стандартная идиома: C>
C>>>for (unsigned i = n; i --> 0 ;)
C>>> std::cout << i << std::endl;
C>
BFE>>Стандартная? А где массив? C>Вот честное слово, не вижу разницы для иллюстративных целей — индексировать массив числами от 9 до 0 или выводить в стандартный вывод числа от 9 до 0.
Я спрсоня не смог правильно прочитать это цикл. Извините.
Спасибо за конструкцию. Столько лет программирую, а не знал