avva: (Default)
[personal profile] avva
Сегодня один из классических багов Перла привёл к тому, что в только что созданные журналы/коммьюнити невозможно было отправить записи, в течение 5 часов примерно. То есть баг не самого перла как языка, а типичный баг в программах на нём: в данном случае, путаница между 0 и undef.

Однако, несмотря на такие баги, отдельное значение undef в Перле себя оправдало, по-моему.

Другой типичный баг, который время от времени всплывает в исходниках ЖЖ, связан с тем фундаментальным фактом, что в Перле нет отдельных типов строк и чисел; скаляр в Перле может содержать целое число, или действительное число, или строку, и переводить одно в другое в зависимости от контекста. Но это значит, в частности, что проверка if("0") не проходит, т.к. "0" это то же самое, что 0, т.е. false. Для строк, возможно, удобнее было бы иметь другой критерий истинности, при котором любая непустая строка истинна, а пустая - ложна, но не получается. Из-за забывания этого иногда получаются баги.

Третий баг возникает как раз вследствие единственного нарушения вышеописанного принципа. Оператор & (двоичный and) действует по-разному в зависимости от того, что расположено внутри скаляров, на которые он действует: числа или строки.

$ perl -e 'print 12 & 5;'
4
$ perl -e 'print "12" & "5";'
1


Почему результаты разные? Потому что & действует на двоичное представление скаляра, не пытаясь привести его к канонической форме (которая в данном случае должна быть численной, конечно). Когда выполняется 12 & 5, выходит двоичный and между 1100 и 0101; результат - 0100, 4. Когда же выполняется "12" & "5", происходит двоичный and между ASCII-представлением строки "12", т.е.
00110001 00110010 , и ASCII-представлением строки "5", т.е. 00110101 . В результате выходит то же, самое, что "1" & "5", т.к. второму байту строки "12" ничего не соответствует в строке "5"; а
"1" & "5" выходит 00110001, т.е. "1", что корректно преобразуется при надобности в 1.

Таким образом, "x" & "y" и x & y дают одинаковый результат, если x и y - цифры от 0 до 9 (это выходит благодаря тому, что ASCII-представления цифр 0..9 начинаются с 48, т.е. совпадают с самими цифрами в четырёх последних двоичных регистрах), но если числа выходят за предел 0..9, результаты выходят разными.

Особенно больно это бьёт по разработчику, если он использует & для вычисления двоичной маски секьюрити, скажем, в целях обеспечения привилегированного доступа к какому-то объекту. Если при этом его числа, с которыми он делает &, на самом деле не числа, а в данный момент строки (например, потому, что их вернул модуль доступа к БД, всегда возвращающий строки, или Перл их внутри перевёл в строки для какой-то операции, того же print), то результат операции будет неверен, и легко может случиться так, что непривилегированный юзер получит доступ к объекту (всё это касается и двоичного or, кстати, т.е. оператора |, но он используется намного реже на практике, так что редко случаются баги с ним).


Четвёртый... пусть будет на баг, а kludge довольно забавный внутри Перла.
Цитируя документацию функции ioctl:
              The return value of "ioctl" (and "fcntl") is as follows:
 
                       if OS returns:          then Perl returns:
                           -1                    undefined value
                            0                  string "0 but true"
                       anything else               that number
 
               Thus Perl returns true on success and false on failure, yet you
               can still easily determine the actual value returned by the
               operating system:
 
                   $retval = ioctl(...) || -1;
                   printf "System returned %d\n", $retval;
 
               The special string "0 but true" is exempt from -w complaints
               about improper numeric conversions.

То же верно ещё для нескольких функций, являющихся перловскими обложками для системных функций: они возвращают обычно результат системной функции или undef в случае ошибки, но, если системная функция возвращает 0 и это для неё не ошибка, они возвращают "0 but true", таким образом пытаясь предотвратить второй баг, описанный выше: если неосторожный разработчик напишет
if(ioctl(...)), то это сработает, т.к. "0 but true" истинно, в отличие от "0"; а если нужно перевести в числовой контекст, то "0 but true" переведётся в 0:
$ perl -e 'print "0 but true" + 5;'
5


Но если мы запускаем perl с опцией -w (warnings), то обычно перевод строки с не-числовым "мусором" в число выдаёт предупреждение:
$ perl -w -e 'print "12garbage" + 5;' 
Argument "12garbage" isn't numeric in addition (+) at -e line 1.
17


А со строкой "0 but true" это не происходит, как и обещано в документации выше:

$ perl -w -e 'print "0 but true" + 5;' 
5


И это верно только для этой строки. На уровне исходников Перла это происходит так. В файле sv.c, имплементирующем операции со скалярами (sv=scalar value), есть функция looks_like_number(). Её вызывают, когда нужно определить, можно ли перевести данную строку в число; она смотрит на строку, и возвращает ноль, если эта строка не представляет из себя число, и ненулевое значение в обратном случае, причём тогда она возвращает значение, указывающее на то, какую функцию надо использовать для перевода данной строки в число (atol() для целых чисел, atof() для действительных). И вот, в этой функции, когда она уже проверила, выглядит ли строка как целое или действительное число, и вернула соответствующие значения в таких случаях, и уже совсем готова возвращать 0, указывая на неудачу, там стоит:

    if (len == 10 && memEQ(sbegin, "0 but true", 10))
        return IS_NUMBER_TO_INT_BY_ATOL;
    return 0;


Классический пример того, что по-английски называют kludge, по-моему.

Date: 2003-10-18 11:38 am (UTC)
From: [identity profile] avva.livejournal.com
Это не является ли одним из примеров того что Perl -- просто плохо продуманный язык?

Нет. Перл - хорошо продуманный язык, который сознательно идёт на множество "некрасивых" и неэффективных трюков внутри имплементации runtime-среды ради удобства программиста, которое ставится на первое место. Mutability чисел в строки и обратно - исключительно удобное для программиста свойство языка, и хорошо продуманное к тому же; отдельные недочёты, типа этой проблемы с &, этого факта не меняют. В любом языке есть свои недочёты. А проблему с & обещают исправить в следующей версии, кстати.

Date: 2003-10-18 02:49 pm (UTC)
From: [identity profile] igorlord.livejournal.com
удобства программиста, которое ставится на первое место

Это же анафема! Вот я и говорю что это очень большая ошибка допускать в индустрию язык, который ставит на первое место "удобства" программиста. Нужны системы которые на 1ое место ставят читабельность и поддерживаемость кода. Пусть програмист потратит в 2 раза больше времени пиша нечто чтобы другой человек смог на много быстрее разобраться что к чему и, очень важно, был уверен что меняя нечто он не ломает какой-то "хитрый трюк". К сожалению, в индустрии слишком много "хэкеров", готовых изощряться до умопомрачения "для совего удобства". Если ещё и язык этому способствует, то получается тихий ужас.

Date: 2003-10-18 03:37 pm (UTC)
From: [identity profile] msh.livejournal.com
Читабельность и поддерживаемость зависит от языка чуть больше чем от выбранного стиля indent-ов и заметно меньше, чем от актуальности комментариев. То, что действительно сильно влияет на поддерживаемость - никак не зависит от языка.

Date: 2003-10-18 04:36 pm (UTC)
From: [identity profile] igorlord.livejournal.com
Ну, не скажи. Возмём, C например. Куча ошибок бывает из-за pointer typecasts. Если бы не было произвольных typecasts, то програмисты бы обходились без них и код был-бы более логичен. То-же самое, как я уже писал, если бы не был овозможности изпользовать любое цисленное выражение как conditional, то аккуратненько бы все сравнивали результаты ioctrl с правильным знаечнием enumeration. При чём, я бы запретил использование int вместо enum.

Кроме того, большая проблема с C -- если функция возвращает значение, то невозможно на уровне языка C определить обязательно ли это значение проверять клиенту или можно игнорировать. Java и C++ на много лучше в этом -- в них есть exceptions.

В C (и многих других языках) буффер памяти не знает свой размер. Если бы это было (обязательной!) частья языка, то было бы на много меньше дырок в программах.

В Perl, как я понимаю, из-за того что он такой "мощный" есть множество способов писать "sloppy" или слишком "хитрый" код. Это я считаю проблемой языка.

Date: 2003-10-18 05:13 pm (UTC)
From: [identity profile] msh.livejournal.com
Извините, я не понимаю

Куча ошибок бывает из-за pointer typecasts

Я вообще не могу вспомнить ни одной ошибки которую я бы допустил или хотя бы видел из-за pointer typecasts. Вообще, он делается в редких случаях, ну, например, чтобы передать в аргументе ioctl структуру и потом ее достать назад

если бы не был овозможности изпользовать любое цисленное выражение как conditional, то аккуратненько бы все сравнивали результаты ioctrl с правильным знаечнием enumeration.

Все нормальные люди сравнивают результат ioctl с enumeration. hint: ioctl не возвращает conditional


Кроме того, большая проблема с C -- если функция возвращает значение, то невозможно на уровне языка C определить обязательно ли это значение проверять клиенту или можно игнорировать. Java и C++ на много лучше в этом -- в них есть exceptions.


В Java и C++ тоже нельзя определить на уровне языка надо ли это значение проверять или можно игнорировать. В C++ exceptions можно просто не ловить, а в Java можно downcast then ignore

В C (и многих других языках) буффер памяти не знает свой размер. Если бы это было (обязательной!) частья языка, то было бы на много меньше дырок в программах

При этом бы C потерял свою нишу.

В Perl, как я понимаю, из-за того что он такой "мощный" есть множество способов писать "sloppy" или слишком "хитрый" код. Это я считаю проблемой языка.

Извините, можно я прямо в лоб спрошу - как хорошо вы знаете Perl и какого размера был самый большой проект, над которым Вы работали? Можно не отвечать, конечно.

Я могу назвать три самых больших источника проблем при поддержке кода по моему опыту

1. Функциональность, не покрытая тестами, которую вдруг начинают использовать
2. Race conditions, вдруг проявившиеся у клиентов из-за их специфичной нагрузки и hardware
3. Изменение окружающего мира - платформы, другого софта и т.п.

ни один из пунктов существенно от языка не зависит

Date: 2003-10-18 06:27 pm (UTC)
From: [identity profile] igorlord.livejournal.com
По-вашему "нормальные" люди ошибок просто не делают. :) Или у вас небольшой опыт или плохая память. Ошибки деляют все. Многие так-же из-за недостатка времени, сил, желания находят "shortcuts". Ну очень часто я слышал: "Зачем я буду это делать как по инструкции? Ведь еси тут вот так выделаться, то смотри, всё работает! А ты мне говоришь портатить день(!) на то чтобы это сделать "кошерно"."

К стати, вот вы говорите "Все нормальные люди сравнивают результат ioctl с enumeration" когда здесь в соседмен thread говорят что его сравнивают с 0 или -1! Значит здесь много "ненормальных" сидят?

в Java можно downcast then ignore
Если специально вредить, то ничего ен поможет. Но если язык заставляет что-то сделать, то скорее всего это сделают правильно. Если определить все exceptions которые клиент обязан словить или упомянуть, то нет шансов что клиент просто поленился посмотреть какие exceptions могут быть вообще у данной финкции.

Ничего бы C не потерял. Записать 1 слово и проверить 1 слово будет незаметно. А если это делается в цикле, то компилятор может оптимизировать.

Perl знаю плохо -- на уровне простеньких програм. А опыт и размер софта над каким работаю -- так это Oracle RDBMS. Достаточно большая програмка ии нет? :) Поверьте, я мноо кода перечитал, включая код, который существовал уже лет 15 и актовно модифицировался. Иногда очень хочешь прибить кого-то за "фантазию".

Вот мой список из-за чего проблемы:

1. Функциональность, не покрытая тестами, которую вдруг начинают использовать

2. Изменения кода без понимания всех последствий (из-за слишком запутанного начального кода или плохой архитектуры).

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

Date: 2003-10-19 01:03 am (UTC)
From: [identity profile] msh.livejournal.com
По-вашему "нормальные" люди ошибок просто не делают. :)

Нормальные люди делают ошибки вне зависимости от языка

К стати, вот вы говорите "Все нормальные люди сравнивают результат ioctl с enumeration" когда здесь в соседмен thread говорят что его сравнивают с 0 или -1!

Его сравнивают с явной или неявной enumeration где -1 - это ошибка. А не используют как conditional (т.е. 0/не 0)

Вот мой список из-за чего проблемы ..

Ну, и какой пункт из этого списка завист от того, на каком языке написано? ;-)

Date: 2003-10-19 08:09 am (UTC)
From: [identity profile] igorlord.livejournal.com
Нормальные люди делают ошибки вне зависимости от языка

Да, но в каком количестве? Прежде чем отвечать, можно ведь было потратить 30 секунд чтобы задуматься над этим вопросом и, может засунуть его в google. Тогда было бы всё сразу понятно. Ладно, это сделал я. Вот самая первое что google возвращает на: "compare rate programmer errors per line of code Java".

Его сравнивают с явной или неявной enumeration где -1 - это ошибка.
??? Да у вас-же на глазах убсуждают: if( ioctl(...) == 0 ) или if( ioctl(...) != 0 ) или if( ioctl(...) ) или if( ioctl(...) == -1 )
Проблема #1. ioctl использует int как ресультат вместо enum.
Проблема #2. Даже если бы он использовал enum, C разрешил бы сравнивать с int всё равно.

И ещё, я может C полохо знаю, но понятия implicit enum в языке нет. ;)

И опять ещё. Я считаю это большой проблемой C то что люди (см выже) заботятся сильно а таких вещах как "надо ли != 0 писать или лучше опустить". Язык должен быть строже.

какой пункт из этого списка завист от того, на каком языке написано
#2. Корявый исходный код и плохая архитектура. Чем более "вольный" язык -- тем больше корявого кода. Если в языке нет неких базовых концептов и принципов -- тем хуже архитектура (на пример exceptions, objects, ...)

Date: 2003-10-19 10:53 am (UTC)
From: [identity profile] msh.livejournal.com
Ссылка интересная, но

The experiment used one programmer, one C++ project
and one Java project.

за это сразу двойку ставят, это не результат вообще ;-)


Date: 2003-10-19 01:16 pm (UTC)
From: [identity profile] igorlord.livejournal.com
Вот ещё интересная ссылка. Это не про частоту ошибок, но хорошо отражает моё менние.

Date: 2003-10-19 01:36 am (UTC)
From: [identity profile] msh.livejournal.com

Ничего бы C не потерял. Записать 1 слово и проверить 1 слово будет незаметно. А если это делается в цикле, то компилятор может оптимизировать.


(ну не слово, конечно)

C бы потерял нишу эффективного языка. Это не незаметно. На современных процессорах
*p++ = *q++ если каждый раз проверять, что поинтер не вышел за границу будет дольше на порядок. И это было бы везде

a->10 - надо проверять
a[10]=b[2] - надо проверять два раза

char* p = q+2 - тут вообще надо пересчитывать ограничение, которое было у q и записывать его для p.

Учитывая, что обычно C программы и состоят из такого, то смысла в таком C просто не будет - можно сразу уже переходить на Java/C#

Date: 2003-10-19 08:24 am (UTC)
From: [identity profile] igorlord.livejournal.com
Нечто типа:
*p++ = *q++
лучще выглядят как
p[i] = q[i]; i++;
Компилятор запросто разберётся как это оптимизировать. И проверка границы массива тоже будет оптимизирована запросто если это делается в цикле.

На надо
char* p = q+2;
без этого можно запросто обойтись. Используй имя массива и индех в него вместо ссыкли в его середину. Или, ещё вариант, если оечнь хочешь ссылку в середину массива, хорошо, но тип "массив данных такого типа" должен отличаться от типа "ссылка на данные такого типа". Таким образом арифметика ссылок разрешена только с типом "массив" а не со случайной "ссылкой".

Date: 2003-10-19 10:44 am (UTC)
From: [identity profile] msh.livejournal.com
Компилятор не разберется. Чуть более сложный случай и любой dereference (1-2 команды на современных процессорах) становится примерно 10-20 командами, переходом и лишним обращением к памяти


Таким образом арифметика ссылок разрешена только с типом "массив" а не со случайной "ссылкой".


Вот я и говорю - из языка убирается произвольная поинтерная арифметика. То что остается - это как раз Java, зачем мне тогда такой "C'?

Date: 2003-10-19 12:37 pm (UTC)
From: [identity profile] igorlord.livejournal.com
Комиляторы бывают довольно умными. :) Кроме того, чем более строгий язык, тем лучше компилятор будет разбираться т.к. ему всегда надо предпологать худшее исхищрение програмиста, а в более строгих языках такое "худшее" не так уж и "плохо" (с точки зрения компилятора). :)

Да. Абсолютно произвольная поинтерная арифметика убирается. Остаётся "ограниченная (лимитированная) арифметика". Главный вопрос -- будет ли этого достаточно для всего или нет? Я думаю что будет. А до Java здесь о-очень далеко. Я не предлагаю "garbage collection".

Date: 2003-10-19 08:39 am (UTC)
From: [identity profile] 109.livejournal.com
Майк, у человека есть пойнт. granted, лично ты пишешь очень ясный и грамотный код. но сколько программистов могут так писать? 1%? а большая часть написанного в индустрии кода - это полный п-ц. и п-ц этот периодически приходится кому-то разгребать. и вот тут таки есть разница.

Date: 2003-10-19 10:30 am (UTC)
From: [identity profile] msh.livejournal.com
Я не случайно спрашивал про максимальный размер системы на перле, которую писал уважаемый оппонент. Не просто с целью померяться пиписьками. Дело в том, что у перла есть граница, до которой код на нем пугает, а после нее ты начинаешь его понимать любой, кроме специально obfuscated. После чего становится ясно, что система одного размера написанная одинаковыми программистами будет одинаково запутанной на любом языке - потому что сложность лежит не в семантике языка, а в структуре программы.



Date: 2003-11-15 02:12 pm (UTC)
From: [identity profile] xfyre.livejournal.com
Дело в том, что у перла есть граница, до которой код на нем пугает, а после нее ты начинаешь его понимать любой, кроме специально obfuscated.

кстати, да.

Date: 2003-10-20 09:08 pm (UTC)
From: [identity profile] snaky.livejournal.com
Нету её. Количество п-ца размазанное по 100 экранам среднеиндусской джавы и по 15 экранам сренднеиндусского же перла одинаково.

Date: 2003-10-21 05:38 am (UTC)
From: [identity profile] 109.livejournal.com
нет, не одинаково, по вполне объективным причинам, а именно потому, что джава не позволяет написать такой п-ц, который позволяет написать перл.

Date: 2003-10-21 08:21 am (UTC)
From: [identity profile] snaky.livejournal.com
Да, она позволяет написать другой п-ц, но в N раз больше. Итоговое количество п-ца всё равно получается одинаковым.

Date: 2003-10-21 08:43 am (UTC)
From: [identity profile] 109.livejournal.com
ну, как минимум, вы согласны, что удельный п-ц в перле выше?

Date: 2003-10-21 09:46 am (UTC)
From: [identity profile] snaky.livejournal.com
Удельный на единицу чего? На строку кода - конечно, только кого это волнует? На фортра.., простите, джаве, кода будет в N раз больше, с удельным п-цом в N раз меньше. Сумма п-ца остаётся неизменной.

(no subject)

From: [identity profile] 109.livejournal.com - Date: 2003-10-21 09:59 am (UTC) - Expand

(no subject)

From: [identity profile] snaky.livejournal.com - Date: 2003-10-21 02:10 pm (UTC) - Expand

Date: 2003-10-19 12:44 am (UTC)
stas: (Default)
From: [personal profile] stas
Идея, что кто-то "допускает в индустрию" языки, меня изрядно веселит. Куда только не забирается комплекс диктатора...

Date: 2003-10-19 07:34 am (UTC)
From: [identity profile] igorlord.livejournal.com
Я нахожу некие решения некого манажемента неправильными. Считаю что CTO, или ещё кто, должен был бы поступить по-другому. При чём здесь диктатура? Разбрасывайтесь словами по-аккуратнее.

Date: 2003-10-19 08:25 am (UTC)
stas: (Default)
From: [personal profile] stas
Чей CTO? Все сразу?
Забавный вы человек - считаете возможным давать советы целой "индустрии", не вдаваясь в подробности :)

December 2025

S M T W T F S
  123 4 56
78 9 10 11 1213
1415 1617181920
21 22 23 24 2526 27
2829 30 31   

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 1st, 2026 10:17 pm
Powered by Dreamwidth Studios