Я решил попробовать писать "правильный" код. Для ширины например — unsigned, т.е. она не бывает отрицательной. И тд.
Но как меня всякие мелочи задолбали, извините. Чуть чуть перепутал, и всё, 4 миллиарда, вся логика валится.
Если int-ы хоть можно проверить на -1, то unsigned-ы как проверишь? Непонятно.
Собственно вопрос, оправдан ли такой геморрой? Или сделать :%s/unsigned/int/g и жить счастливой жизнью?
С одной стороны вроде бы unsigned идеологически лучше. Но с другой, не получается у меня нормально с ними писать. Каждый раз, когда фунцию с аргументом типа unsigned вызываешь, надо проверять, не передастся ли туда отрицательное число, а если бы она была int, то проверку можно было бы сделать внутри функции (ну не писать же
void f(unsigned x)
{
if ((int)x < 0) // something bad
}
)
Что вы думаете по этому поводу?
18.04.07 14:44: Перенесено модератором из 'C/C++' — Кодт
Здравствуйте, Аноним, Вы писали:
А>Я решил попробовать писать "правильный" код. Для ширины например — unsigned, т.е. она не бывает
А я наоборот, полагаю, что unsigned, по возможности, надо объявлять только в редких случаях и только для неарифметических по смыслу переменных. То есть таких, над которыми никогда не проводятся арифметические операции. И даже для них, если нет нужды не надо unsigned, так я почти никогда не объявляю unsigned даже индексные переменные. Иначе чревато трудно обнаружимыми ошибками в промежуточных вычислениях.
Здравствуйте, Аноним, Вы писали:
А>Что вы думаете по этому поводу?
signed -- для чисел
unsigned -- для флажков.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Аноним, Вы писали:
А>Я решил попробовать писать "правильный" код. Для ширины например — unsigned, т.е. она не бывает отрицательной. И тд. А>Но как меня всякие мелочи задолбали, извините. Чуть чуть перепутал, и всё, 4 миллиарда, вся логика валится.
Нужно обращать внимание на warning’и компилятора. И иметь компилятор, который их сыплет по всякому поводу.
Re[2]: Нужен ли unsigned
От:
Аноним
Дата:
12.11.06 14:39
Оценка:
Здравствуйте, Centaur, Вы писали:
C>Здравствуйте, Аноним, Вы писали:
А>>Я решил попробовать писать "правильный" код. Для ширины например — unsigned, т.е. она не бывает отрицательной. И тд. А>>Но как меня всякие мелочи задолбали, извините. Чуть чуть перепутал, и всё, 4 миллиарда, вся логика валится.
C>Нужно обращать внимание на warning’и компилятора. И иметь компилятор, который их сыплет по всякому поводу.
gcc, -Wall -Wextra
варнингов нет. Да и откуда ему знать, что получится при m_width — m_pageWidth: 10 или 0 или -10
Здравствуйте, Аноним, Вы писали:
А>gcc, -Wall -Wextra А>варнингов нет. Да и откуда ему знать, что получится при m_width — m_pageWidth: 10 или 0 или -10
Здравствуйте, Аноним, Вы писали:
А>Но как меня всякие мелочи задолбали, извините. Чуть чуть перепутал, и всё, 4 миллиарда, вся логика валится. А>Если int-ы хоть можно проверить на -1, то unsigned-ы как проверишь? Непонятно.
Не совсем понятно, причем здесь именно -1.
Также непонятно, откуда взялись эти 4 миллиарда. Такое могло призойти, например, если где-то в программе перемешиваются знаковые и беззнаковые типы или если где-то из меньшего вычитается большее. В первом случае (если мешать signed и unsigned действительно необходимо) надо было проверять на отрицательные значеня непосредственно перед переходом от signed к unsigned. Во втором случае надо было проверять на вычитание большего из меньшего.
А>Собственно вопрос, оправдан ли такой геморрой? Или сделать :%s/unsigned/int/g и жить счастливой жизнью?
Нет там никакого геммороя. В том виде, в котором все это описано в твоем сообщении, это звучит как "беззнаковые типы не дают мне сделать ошибку и я не могу проверить ее наличие"
А>С одной стороны вроде бы unsigned идеологически лучше.
Лучше.
А>Но с другой, не получается у меня нормально с ними писать. Каждый раз, когда фунцию с аргументом типа unsigned вызываешь, надо проверять, не передастся ли туда отрицательное число, а если бы она была int, то проверку можно было бы сделать внутри функции
Удобство unsigned как раз и состоит в частности в том, что их не надо проверять на отрицательные значения. Их надо проверять только на выход за верхний предел, если таковой существует (такая проверка, как правило, отловит и случайную передачу "отрицательного числа"). А если верзхнего предела не существует в принципе — то и проверять ничего не надо.
Откуда возникает проблема "не передастся ли туда отрицательное число" — мне не понятно, ибо не понятно откуда может даже возникнуть возможность передать отрицательное число — то, что передается, тоже должно быть unsigned. Если же в этом месте происходит необходимый переход от signed к unsigned значениям (что бывает нечасто), то проверка, разумеется, делается в момент перехода, т.е. снаружи функции.
Здравствуйте, Аноним, Вы писали:
А>gcc, -Wall -Wextra А>варнингов нет.
Вы добавьте -ansi -pedantic Может появятся
А> Да и откуда ему знать, что получится при m_width — m_pageWidth: 10 или 0 или -10
К сожалению, gcc не выдаёт предупреждений, но в принципе компилятор c/c++ мог бы выдавать предупреждения, если результат вычитания присваивается беззнаковой переменной.
Здравствуйте, Андрей Тарасевич, Вы писали:
А>>С одной стороны вроде бы unsigned идеологически лучше.
АТ>Лучше.
Чем? На мой взгляд существует очень ограниченный перечень случаев, когда unsigned оправдан, это флаги, коды значений, работа с памятью, может ещё пара случаев и всё. В принципе, ещё можно даже для арифметических операций, если не хватает диапазона значений знакового типа, но это уже грязный стиль и нежелательно.
АТ>Удобство unsigned как раз и состоит в частности в том, что их не надо проверять на отрицательные значения. Их надо проверять только на выход за верхний предел, если таковой существует (такая проверка, как правило, отловит и случайную передачу "отрицательного числа").
Сугубо моё мнение, но у меня сложилось впечатление, что потенциальные проблемы перевешивают преимущества и применение unsigned делает код слегка менее прозрачным, а в некоторых случаях может создать проблемы при модификациях кода. Например, казалось бы логично сделать индекс/счётчик беззнаковым, но в дальнейшем может оказаться, что проще всего присваивать ему отрицательные значения для обработки особых ситуаций.
Дело в том, что использование беззнаковых типов в C/C++, вопреки ожиданиям, практически ничего не даёт в плане контроля за кодом на этапе компиляции, а только добавляет лишнюю сложность и ограничения.
АТ>И? Неправильно реализованный цикл работает, разумеется, неправильно. И что из этого?
Очень хороший пример.
Применение unsigned излишне нагружает программисту мозг, заставляя его лишнего следить за кодом, в то время как казалось бы он мог рассчитывать отдохнуть на более строгом контроле типов.
Здравствуйте, Michael7, Вы писали:
А>>>С одной стороны вроде бы unsigned идеологически лучше.
АТ>>Лучше.
M>Чем? На мой взгляд существует очень ограниченный перечень случаев, когда unsigned оправдан, это флаги, коды значений, работа с памятью, может ещё пара случаев и всё. В принципе, ещё можно даже для арифметических операций, если не хватает диапазона значений знакового типа, но это уже грязный стиль и нежелательно.
Чем — это очень странный вопрос. Беззнаковые по своей природе сущности естественно представлять беззнаковыми типами. Тут, собственно, и вопросов никаких нет. Вопросы возникают именно тогда, когда кто-то начинает пытаться использовать знаковые типы для это цели. Вот тут действительно можно спросить, чем это вдруг оправданно такое странное использование знаковых типов. И в большинестве случаев оказывается, что знаковые типы просто-напросто чаще "прощают" беспечное отношение к коду. Во-первых, я считаю, что правильнее внимательнее относиться к коду, а не пытаться компенсировать лень использованием знаковых типов. Во-вторых, "безопасность" знаковых типов — не более чем иллюзия.
АТ>>Удобство unsigned как раз и состоит в частности в том, что их не надо проверять на отрицательные значения. Их надо проверять только на выход за верхний предел, если таковой существует (такая проверка, как правило, отловит и случайную передачу "отрицательного числа").
M>Сугубо моё мнение, но у меня сложилось впечатление, что потенциальные проблемы
Я не знаком ни с какими "потенциальными проблемами" беззнаковых типов. В моем коде, например, практически все целочисленные типы — беззнаковые, за исключением случаев, когда действительно приходится работать со знаковой величиной.
Вон в соседнем письме приведен пример, в котором использование беззнакового типа приводит к бесконечному циклу. Это пример, который показывает только то, что если писать беззнаковый код в манере знакового, то, разумеется, ничего хорошего их этого не получится. Делать из этого вывод о каких-то недостатках беззнаковых типов — это примерно то же самое, что пытаться писать Паскаль-код на языке С++, и, потерпев неудачу, заявлять, что это свидетельствует о недостатках языка С++.
M>перевешивают преимущества и применение unsigned делает код слегка менее прозрачным, а в некоторых случаях может создать проблемы при модификациях кода.
Я не вижу абсолютно никаких проблем с прозрачностью кода.
M>Например, казалось бы логично сделать индекс/счётчик беззнаковым, но в дальнейшем может оказаться, что проще всего присваивать ему отрицательные значения для обработки особых ситуаций.
А вот это — идеологически неправильно.
M>Дело в том, что использование беззнаковых типов в C/C++, вопреки ожиданиям, практически ничего не даёт в плане контроля за кодом на этапе компиляции, а только добавляет лишнюю сложность и ограничения.
Все эти "сложности и ограничения" — не более чем следствие применить порочные штампы мира знаковых типов к беззнаковым. Просто не надо пытаться это делать.
Также стоит упомянуть, что в С++ во многих контекстах выбор в пользу знаковых типов сдела на уровне языка и стандартной билиотеки. Сделан давно и навсегда. Это и 'size_t', и 'sid::vector<>:size_type' и т.п.
Еще стоит упомянуть, что те проблемы "с индексированием" (или с организацией счетчика цикла), который тут часто навязывают беззнаковым типам, на самом деле существуют в языках С/С++ с самого их рождения: например, в области адресной арифметики. Попробуйте организовать просмотр массива методом "скользящего указателя" в обратном направлении — и вам придется решать те же самые вопросы, которые возникают при обратной индексации массива беззнаковым индексом. Эти же вопросы приходится решать и при работе со стандартным итераторами. Так что говорить, что беззнаковые типы создают какие-то присущие именно им проблемы -совершенно неверно. Это не проблемы знаковых типов. Это соврешенно нормальная реальность программирования на С/С++ к которой рано или поздно все равно придется привыкать. А вот упоминающаяся тут манера безусловного использования знаковых типов — это не более чем попытка убежать от этой реальности.
АТ>>И? Неправильно реализованный цикл работает, разумеется, неправильно. И что из этого?
M>Очень хороший пример.
M>Применение unsigned излишне нагружает программисту мозг, заставляя его лишнего следить за кодом, в то время как казалось бы он мог рассчитывать отдохнуть на более строгом контроле типов.
Нет. Применение unsigned учит программиста специфике программирования в ситуации, когда существует реальная опасность выхода за границы интервала. С точно такой же по своей сути проблемой программист столкнется и в таком случае
int a[N];
int* p = a + N;
while (--p >= a)
*p = 0;
Типичный 'int'-овый программист зачатсую даже и не видит проблемы в этом коде. Более того, такой код зачастую "работает". В то время как на самом деле этот код приводит к неопределенному поведению и его поведение предсказать трудно. Он запросто может привести и к бесконечному циклу. Решить проблему в данном случае беспечным переходом к какому то "знаковому" типу не удастся. Наду учиться жить в "беззнаковом" мире.
Правильная реализация такого цикла
int a[N];
int* p = a + N;
while (p > a)
*--p = 0;
является одним из варинатов "шаблонного" решения, применимого и к примеру с unsigned циклом
Я могу лишь посторить, что специфика "близости края интервала", присущая беззнаковым типам и за которую их часто "не любят", в языках С и С++ присуща далеко не только беззнаковым типам. Если внимательно поискать, найти ее можно практически везде. И спрятаться от нее вам не удастся, как бы вы этого не хотели.
<> АТ>Я не знаком ни с какими "потенциальными проблемами" беззнаковых типов. В моем коде, например, практически все целочисленные типы — беззнаковые, за исключением случаев, когда действительно приходится работать со знаковой величиной.
<>
Не столько проблема, сколько неудобство (иногда).
В знаковой арифметике одно и то же неравенство можно выразить несколькими способами: x<y, x-y<0, 0<y-x, а в беззнаковой — единственным x<y. А если слева и справа — по нескольку слагаемых/вычитаемых, то рукодельная нормализация приводит к плохо читаемому коду.
АТ>>int a[N];
АТ>>int* p = a + N;
АТ>>while (--p >= a)
АТ>> *p = 0;
АТ>>Типичный 'int'-овый программист зачатсую даже и не видит проблемы в этом коде. Более того, такой код зачастую "работает". В то время как на самом
J>Что то я действительно не вижу проблем в таком коде...
если не ошибаюсь, сравнение с указателем который указывает не на "элемент массива или элемент после конечного" -- UB.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Андрей Тарасевич, Вы писали:
К><> АТ>>Я не знаком ни с какими "потенциальными проблемами" беззнаковых типов. В моем коде, например, практически все целочисленные типы — беззнаковые, за исключением случаев, когда действительно приходится работать со знаковой величиной. К><>
К>Не столько проблема, сколько неудобство (иногда). К>В знаковой арифметике одно и то же неравенство можно выразить несколькими способами: x<y, x-y<0, 0<y-x, а в
В знаковой арифметике эти выражения точно так же не эквивалетны. Кроме того, x<0 не эквивалетно -x>0.
Здравствуйте, Michael7, Вы писали:
M>Здравствуйте, Аноним, Вы писали:
А>>Я решил попробовать писать "правильный" код. Для ширины например — unsigned, т.е. она не бывает
M>А я наоборот, полагаю, что unsigned, по возможности, надо объявлять только в редких случаях и только для неарифметических по смыслу переменных. То есть таких, над которыми никогда не проводятся арифметические операции. И даже для них, если нет нужды не надо unsigned, так я почти никогда не объявляю unsigned даже индексные переменные.
Очень плохо.
M>Иначе чревато трудно обнаружимыми ошибками в промежуточных вычислениях.