3) В C/C++ размерности массивов выражаются беззнаковым типом size_t. По этой причине ипользовать для индексирования знаковые типы -- создавать потенциальные дыры.
4) LUT удобнее делать с беззнаковыми типами. Классический пример.
Здравствуйте, night beast, Вы писали:
NB>если не ошибаюсь, сравнение с указателем который указывает не на "элемент массива или элемент после конечного" -- UB.
NB>зачем так мне не известно.
Например
The 80386 memory access model is quite
complicated and there are many ways to implement a "pointer".
One involves a pair (selector,offset) and loading a wrong selector
in any of the selector registers is an error (General Protection Fault
if I remember correctly). Deleting an object could result in freeing
the entry in a selector translation table and subsequent load of the
pointer value could fail.
Здравствуйте, Шахтер, Вы писали:
К>>В знаковой арифметике одно и то же неравенство можно выразить несколькими способами: x<y, x-y<0, 0<y-x, а в
Ш>В знаковой арифметике эти выражения точно так же не эквивалетны. Кроме того, x<0 не эквивалетно -x>0.
Ну не "точно так же". Конечно, в области больших чисел есть риск поймать переполнение.
Но предусловие "оба числа по абсолютному значению не превосходят MAXINT/2" достаточно распространено, и этим можно пользоваться.
Кстати говоря. Это и плюс, и минус одновременно. С одной стороны, упрощаются выражения, а с другой — есть риск забыть проверки и получить феерверк на больших значениях. Беззнаковая же арифметика не прощает халатности с самого начала.
Здравствуйте, Шахтер, Вы писали:
E>>signed -- для чисел E>>unsigned -- для флажков. Ш>По-моему, это та простота, которая хуже воровства.
Ну прости, я ответил на вопрос, что же я таки думаю.
ИМХО выгоды от использования беззанковых чисел всегда какие-то призрачные.
Конечно есть какие-то специальные очень случаи, когда хочется именно беззнакового и именно 32-ного числа.
Но, это очень большая редкость. Я вот припоминаю только один случай.
Обычно беззнаковые числа хочется использовать для каких-то объектов, которые числами не являютчся (скажем над ними не производят арифметических действий, например).
Это или какие-то ID или сборки флагов или ещё какие-нибудь хитрые конструкции. но никак не результат сложения/вычитания/деления/умножения/взятия остатка.
пример о котором я помню -- млпдшее слово в руками сделанном двухсловном числе.
Кроме того беззнаковость числа ничего хорошего ни в CT ни в RT не гарантирует.
Ну а выгоды я хотел бы узнать (кроме, конечно, выгоды в один бит)
Часто выгода в один бит всё равно бесполезна. Ну и потом обычно это всё непереносимо
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Беззнаковые типы не то чтобы не нужны. Однако использовать их для представления ширины или ещё чего-то заведомо положительного нет смысла. Предположим, нам надо сохранить не саму ширину, а разницу между шириной одного и другого окна. Она может быть как положительной, так и отрицательной.
Ш>3) В C/C++ размерности массивов выражаются беззнаковым типом size_t. По этой причине ипользовать для индексирования знаковые типы -- создавать потенциальные дыры.
Можно написать свой вектор, где отрицательный индекс ведёт отсчёт с конца.
Здравствуйте, Erop, Вы писали:
E>>>signed -- для чисел E>>>unsigned -- для флажков. Ш>>По-моему, это та простота, которая хуже воровства.
E>Ну прости, я ответил на вопрос, что же я таки думаю. E>ИМХО выгоды от использования беззанковых чисел всегда какие-то призрачные. E>Конечно есть какие-то специальные очень случаи, когда хочется именно беззнакового и именно 32-ного числа.
у беззнаковых чисел есть одно хорошее свойство -- все арифметические операции делаются по модулю.
но постоянная борьба с варнингами при сравнении знаковых и беззнаковых просто удручает.
АТ>>int a[N];
АТ>>int* p = a + N;
АТ>>while (--p >= a)
АТ>> *p = 0;
АТ>>
АТ>>Типичный 'int'-овый программист зачатсую даже и не видит проблемы в этом коде. Более того, такой код зачастую "работает". В то время как на самом
J>Что то я действительно не вижу проблем в таком коде...
В один прекрасный момент 'p' станет указывать на начало массива. К такому указателю в языках С/С++ запрещается применять операцию '--', т.е. запрещается пытаться получить указатель на элемент "перед первым" элементом массива. После последнего — можно, перед первым — нельзя. Попытка получить такой указатель приведет к неопределенному поведению. Этот код как раз попытается получить такой указатель.
Зачем объяснять?
Ошибка в том, что s имеет беззнаковый тип.
Она же допущена и в std::vector
Ну и что?
1) реально size таким большим не бывает, так как в память плохо помещается
2) Лично я и std::vector не пользуюсь. Пользуюсь массивами из другой библиотеки, там размер массива int
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: А может пусть лучше оптимизатор сокращает? :) (-)
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
АТ>Я могу лишь посторить, что специфика "близости края интервала", присущая беззнаковым типам и за которую их часто "не любят", в языках С и С++ присуща далеко не только беззнаковым типам. Если внимательно поискать, найти ее можно практически везде. И спрятаться от нее вам не удастся, как бы вы этого не хотели.
А может писать как-то попроще. Скажем так:
int a[N];
for( int i = N - 1; i >= 0; i-- ) {
a[i] = 0;
}
Компиляторы у нас сейчас хорошие, оптимизирующие. Код вроде как тоже понятный
Ну а то, что так нельзя с итераторами программировать, так, ИМХО, это проблема дизайна итераторов, а не необходимости пользоваться всюду ьеззнаковыми типами
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, night beast, Вы писали:
NB>у беззнаковых чисел есть одно хорошее свойство -- все арифметические операции делаются по модулю. NB>но постоянная борьба с варнингами при сравнении знаковых и беззнаковых просто удручает.
Поясни что ты имеешь в виду под этим "хорошим" свойством.
и от чего оно таки хорошее?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Хорошо, а как «правильно» решить следующую простейшую задачу?
Круг разбит на n > 0 секторов, пронумерованых подряд числами 0, 1, ..., n – 1. Некто стоит в секторе номер 0, затем идет по кругу, делая s шагов. Куда он попадет?
E>int a[N];
E>for( int i = N - 1; i >= 0; i-- ) {
E> a[i] = 0;
E>}
E>
E>Компиляторы у нас сейчас хорошие, оптимизирующие. Код вроде как тоже понятный
Не надо фиксироваться на частностях. Мои примеры — лишь иллюстрация общего принципа. Приведенный мною ранее пример доступа с помощью указателя (или итератора) производит честный последовательный доступ к элементам контейнера. Ты же в своем примере эмулируешь последовательный доступ при помощи произвольного доступа. В общем случае это, разумеется, невозможно. И никакой оптимизирующий компиялтор тут тебе не поможет.
Здравствуйте, Кодт, Вы писали:
К><> АТ>>Я не знаком ни с какими "потенциальными проблемами" беззнаковых типов. В моем коде, например, практически все целочисленные типы — беззнаковые, за исключением случаев, когда действительно приходится работать со знаковой величиной. К><>
К>Не столько проблема, сколько неудобство (иногда). К>В знаковой арифметике одно и то же неравенство можно выразить несколькими способами: x<y, x-y<0, 0<y-x, а в беззнаковой — единственным x<y. А если слева и справа — по нескольку слагаемых/вычитаемых, то рукодельная нормализация приводит к плохо читаемому коду.
Способ 'x-y' в общем случае так же неприменим к знаковым типам, как непирименим к беззнаковым.
Отдельно, кстати, стоит заметить, что, несмотря на то, что большая часть машинных арифметических операций на популярных арихитектурах не требуют различения знаковых и беззнаковых типов, такие требования языков С/С++, как округление у нулю при целочисленном делении, зачастую таки приводят к генерации менее эффективного кода для знаковых типов. Для беззнакового типа, например, деление на 2 эквивалентно сдвигу на 1 бит вправо, а вот со знаковым типом в дополнительном коде приходится возиться дополнительно.
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Не надо фиксироваться на частностях. Мои примеры — лишь иллюстрация общего принципа. Приведенный мною ранее пример доступа с помощью указателя (или итератора) производит честный последовательный доступ к элементам контейнера. Ты же в своем примере эмулируешь последовательный доступ при помощи произвольного доступа. В общем случае это, разумеется, невозможно. И никакой оптимизирующий компиялтор тут тебе не поможет.
Ты сейчас, ИМХО, находишься в плену идеологических абстракций. На мой взгляд, опять же, вредных, так как они ведут к запутыванию кода.
Тебе надо написать пример похожего на цикл для массива, цикла для списка, который тоже успешно реализует полседовательный досутп?
Много довольно лет жили не тужили во многих языках, где вообще никакого unsigned типа не было и всё было и них хорошо.
Что такого страшного случилось после разработки STL, что коллекции стало трудно итерировать старыми добрыми простыми и надёжными способами?
Чтобы было понятнее о чём это я, вот тебе два примера последовательного доступа:
вот тебе список:
not_std::list<my_struct> lst;
for( my_struct* current = lst.last(); current != 0; current = current->prev() )
{
// тут что-то делают с current
}
А вот и файл:
while( !is_EOF( src ) {
char buffer[bufer_size];
int read_count = fread( src, buffer, buffer_size );
assert( read_count > 0 );
// тут обрабатываем read_count байт данных из buffer
}
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
АТ>>Не надо фиксироваться на частностях. Мои примеры — лишь иллюстрация общего принципа. Приведенный мною ранее пример доступа с помощью указателя (или итератора) производит честный последовательный доступ к элементам контейнера. Ты же в своем примере эмулируешь последовательный доступ при помощи произвольного доступа. В общем случае это, разумеется, невозможно. И никакой оптимизирующий компиялтор тут тебе не поможет.
E>Ты сейчас, ИМХО, находишься в плену идеологических абстракций. На мой взгляд, опять же, вредных, так как они ведут к запутыванию кода.
Абстракции ведут к consistency (единообразию?) — основному (если не единственному) средству НЕзапутывания кода. Непонятно только, почему именно "идеологических"...
E>Тебе надо написать пример похожего на цикл для массива, цикла для списка, который тоже успешно реализует полседовательный досутп?
Не совсем понимаю, причем здесь пример для списка, если речь шла именно о массиве.
E>Много довольно лет жили не тужили во многих языках, где вообще никакого unsigned типа не было и всё было и них хорошо.
Ну так когда-то и языков никаких не было. И тоже, вроде, жили.
E>Что такого страшного случилось после разработки STL, что коллекции стало трудно итерировать старыми добрыми простыми и надёжными способами?
Во-первых, в STL — это необходимость реализации именно абстракций. Поэтому стали пользоваться новыми добрыми простыми и надёжными способами. И они, похоже, даже добрее, проще и недежней. Что делает осмысленным из использование и за пределами STL.
Во-вторых, к STL все это отностся на самом деле мало. Эти "абстакции" зарыты намного глубже.
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Отдельно, кстати, стоит заметить, что, несмотря на то, что большая часть машинных арифметических операций на популярных арихитектурах не требуют различения знаковых и беззнаковых типов, такие требования языков С/С++, как округление у нулю при целочисленном делении, зачастую таки приводят к генерации менее эффективного кода для знаковых типов.
В C++ такого требования в общем случае нет. Оно есть для положительных делимого и делителя; в противном случае поведение implementation-defined (C++03 5.6/4). В C — да, округление к нулю (C99 6.5.5/6).