LINUX.ORG.RU
ФорумTalks

Вопрос по истории C'шечки

 


0

3

Как можно было функции типа atoi, которые не различают отсутствие конвертируемой в число строки от легитимного значения, пропихнуть во все мыслимые стандарты Юникс и C? В те времена С'шникам было покласть на баги?

★★★★★

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

А в стандарты оно попало просто потому, что попало в достандартную библиотеку Си.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)

Просто эта функция не должна применятся если ты не уверен в содержимом входных параметров. А если ты уверен, то нахрена городить допонительные проверки?

Deleted
()
Ответ на: комментарий от tailgunner

А в чём? Если ф-ция не подходит для какого-то применения, то не должна применятся. А раз в стандарт вошла, значит она удобна и широкоприменима.

Deleted
()
Ответ на: комментарий от Deleted

Просто эта функция не должна применятся если [...]

Вопрос был не в этом.

Вот в этом:

"Как можно было функции типа atoi, которые не различают отсутствие конвертируемой в число строки от легитимного значения, пропихнуть во все мыслимые стандарты Юникс и C?"

Т.е. как в стандарт попала функция, которую настолько легко использовать неправильно.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

А почему нет? Изначальный вопрос, при буквальном прочтении, не имеет смысла. Почему в стандарт не может попасть сложная/опасная ф-ция? В станадрт попадают не по критерию опасности или удобности, а по критерию полезности.

Deleted
()

А сама то сишечка тебе не смущает? Функции без всяких рантайм-проверок очень даже гармонично вписываются.

В те времена С'шникам было покласть на баги?

Во все времена. Баги пусть юзеры отлавливают.

bread
()
Ответ на: комментарий от tailgunner

Ты начинаешь меня раздражать.
Разжёвываю ответ: функция atoi попала в стандарт, поскольку она полезна. Полезность не отменяется сложностью или потенциальной опасностью применения.

Deleted
()
Ответ на: комментарий от tailgunner

Тролль в модераторах? Как мило.

Deleted
()
Ответ на: комментарий от tailgunner

Веселящийся tailgunner? Это сколько же нужно веселящего газа напустить. Никто не выживет же.

bread
()

atof() тоже `does not detect errors`.

pacify ★★★★★
()

C'шечки

Б'жмой. Как ты это произносишь, а?

t184256 ★★★★★
()

atoi - выкидыш, за использование которого надо бить по рукам. Есть strtol, есть sscanf.

Deleted
()

Это правильная функция с правильным интерфейсом, как раз в духе C. Считалось, что программист читает доки к фунциям, которые использует. Любой¹ альтернативный интерфейс atoi будет иметь ненулевой оверхед в случаях, когда передаваемая строка заведомо валидна.

¹Возможно, кроме варианта «крэш программы на невалидных данных в atoi»

Crocodoom ★★★★★
()

Она вполне пригодна если 0 нелеегитимно (тогда производится проверка на 0). А так же в случаях когда отсутствие числа можно приравнять к нулю.
Инода наличие внутренних проверок хуже - программа начинает вылетать из-ха битых кнфигов например. В C нет механизма исключений - стараемся обходиться без исключительных случаев

mittorn ★★★★★
()

atoi, которые не различают отсутствие конвертируемой в число строки от легитимного значения

можно пример нелегитимного значения?

waker ★★★★★
()
Ответ на: комментарий от waker

Пустая строка или символ. Это не нуль и появление нуля там может быть ошибкой. atoi недопустим если необходимо определить эти ошибки. Но удобен если определение некорректной строки как 0 допустимо

mittorn ★★★★★
()

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

ixrws ★★★
()
Ответ на: комментарий от mittorn

Пустая строка или символ. Это не нуль и появление нуля там может быть ошибкой. atoi недопустим если необходимо определить эти ошибки. Но удобен если определение некорректной строки как 0 допустимо

эти ситуации элементарно проверяются до вызова atoi.

в документации это вполне доступно описано:

The atoi() function converts the initial portion of the string pointed to by str to int representation.

waker ★★★★★
()

Может еще и поинтеры запретитЬ? Баговый потенциал атои меркнет в свете мощи рав понтеров...

На самом деле стандартизанты думали что умение думать для програмиста является обязательным... В наше время уже нет...

Jetty ★★★★★
()
Ответ на: комментарий от Jetty

На самом деле стандартизанты думали что умение думать для програмиста является обязательным...

Умение думать и умение всегда помнить мелочи - вещи разные. Как литературный талант и красивый почерк.

tailgunner ★★★★★
()
Ответ на: комментарий от mittorn

достаточно isdigit

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

waker ★★★★★
()

Как можно было функции типа atoi, которые не различают отсутствие конвертируемой в число строки от легитимного значения, пропихнуть во все мыслимые стандарты Юникс и C?

А вот к стати не обязательно не валидная строка это ноль. Ноль это когда «не смог распарсить». Строка вида «123ОЛОЛОПЫЩЬПЫЩЬ» будет этой функцией интерпретирована как «123» (цитата с http://www.cplusplus.com «The string can contain additional characters after those that form the integral number, which are ignored and have no effect on the behavior of this function.»).

К вопросу про то, «как сие можно было пропихнуть в стандарт» - как гвоорится «наш стандарт - что хотим, то и пихаем». Опять же, что такое валидная строка? Возможно авторы решили что строка вида «0000» не валидная, и соответственно значение 0 однозначно считается не валидным. Опять же, а тчо она должна делать иначе? Как отличить ноль от не ноля, кидать ошибку? Или делать еще один параметр для возврата значения (или ошибки) по указателю? Так можно еще больше косяков сделать.

RiseOfDeath ★★★★
()
Последнее исправление: RiseOfDeath (всего исправлений: 3)

Ну и давайте зайдем с другой стороны.

Вот сформулирую задачу уровня студента первого курса:

1. Напишите функцию на языке C, которая получает из текстовой строки число, представленное в виде текста. Свое решение обоснуйте.

2. В дополнение к п. 1 не используйте функций не из stdlib.h (там лежит atoiю Фактически это следует понимать как «не использовать функции из других хедеров»). Обоснуйте своей решение.

3. В дополнение к п. 2 сделайте так чтобы компиляторы не игнорили inline для этой функции.

4. В дополнение к п. 3 сделайте проверку входных данных и возврат ошибок.

RiseOfDeath ★★★★
()
Последнее исправление: RiseOfDeath (всего исправлений: 2)
Ответ на: комментарий от RiseOfDeath

Напишите функцию на языке C, которая получает из текстовой строки число, представленное в виде текста. Свое решение обоснуйте.

Задача поставлена неверно. Не указано какая строка (/0, кодировка и тп). Не указано какое число (начиная от целого и заканчивая -.2e5).

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

Я хотел минимизировать «ограничения» и забыл про эти «очевидные» вещи. Спасибо за уточнение. (К стати, в реальных лабах никогда не встречал конкретизации таких вещей, и там была бы именно формулировка как у меня в варианте 1. Я сужу по своему опыту как студента - обычно студенты или делали «как сами поняли» или уточняли это у преподователя, а там или «сделйате как поняли», или «оба, все три, все четыре, любой из» и т.п. (смотря сколько в своем вопросе студент предложит пониманий этой трактовки), или уже уточнялось)

1. Напишите функцию на языке C, которая получает из текстовой строки целое число, текстовая запись которого представленна в десятичной системе. Перед числом может быть указан знак '-', если оно отрицательное или '+', если оно положительное. При отсутствии такового число считается положительным. Число указывается в простом виде (не экспоненциальная запись). Считать что введенное число будет больше или равно INT_MIN, но меньше или равно INT_MAX. Десятичный разделитель отсутствует. Под строкую подразумеватся массив символов в кодировке ASCII, оканчивающийся нулевым символом '\0'.Свое решение обоснуйте. Пример строки "-1000" «100500» «999» «+12352».

2. В дополнение к п. 1 не используйте функций не из stdlib.h (там лежит atoi, фактически это следует понимать как «не использовать функции из других хедеров»). Обоснуйте своей решение.

3. В дополнение к п. 2 сделайте так чтобы компиляторы не игнорили inline для этой функции.

4. В дополнение к п. 3 сделайте проверку входных данных и возврат ошибок.

P.S.

Сейчас я ничего не забыл?

RiseOfDeath ★★★★
()
Последнее исправление: RiseOfDeath (всего исправлений: 5)
Ответ на: комментарий от RiseOfDeath

А вот к стати не обязательно не валидная строка это ноль. Ноль это когда «не смог распарсить». Строка вида «123ОЛОЛОПЫЩЬПЫЩЬ» будет этой функцией интерпретирована как «123»

Невалидная строка это обязательно ноль.
«Не смог распарсить» это и есть невалидная строка, по определению валидности.
И да, "123ОЛОЛОПЫЩЬПЫЩЬ" это валидная строка.

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

Опять же, исходя из документации строка «123ОЛОЛОПЫЩЬПЫЩЬ» ничем не отличается от строки «123», а строка «0бла-бла-бла» ничем не отличается от «бла-бла-бла» и от «0». Т.к., согласно документации, функция игнорирует все пробелы вначале, а затем парсит число; если натыкается на не число, то заканчивает парсить. Закономерно мы пропустили все ноль пробелов, наткнулись на нечисло и закончили парсинг. Строго говоря мы даже «ошибку» не получили. Так что 0 это даже не ошибка (исходя из алгоритма функции), а валидный результат.

p.s.

Опять же, строка «123ОЛОЛОПЫЩЬПЫЩЬ» валидна именно с точки зрения функции atoi. Я бы, например, не считал ее валидной (почему я должен считать валидной строку, в которой присутствуют «левые» символы?)

RiseOfDeath ★★★★
()
Последнее исправление: RiseOfDeath (всего исправлений: 5)
Ответ на: комментарий от RiseOfDeath

Опять же, строка «123ОЛОЛОПЫЩЬПЫЩЬ» валидна именно с точки зрения функции atoi.

Да, я об этом и говорил

Я бы, например, не считал ее валидной (почему я должен считать валидной строку, в которой присутствуют «левые» символы?)

Все парсеры работают по такому определению валидности. Другое дело, что нормальные ещё и возвращают место, где остановились (например strtol), чтобы можно было продолжить разбор дальше. В этом смысле atoi парсер только наполовину, конечно, так как он не позволяет продолжить разбор. Но логика разбора всё равно остаётся такой же. Конечно, он мог бы возвращать 0 на строках с «плохими хвостами», но я не вижу, чем это лучше. А другого выбора нет — сигнатура не позволяет.

Crocodoom ★★★★★
()
Ответ на: комментарий от RiseOfDeath

Считать что введенное число будет больше или равно INT_MIN, но меньше или равно INT_MAX.

А про overflow я и забыл. Такие требования неверны, ибо строка может быть любая. А значит нужно обрабатывать все возможные ситуации.

Сейчас я ничего не забыл?

Ещё нужно уточнить наличие leading/trailing spaces, leading zeros (типа 0001), наличие и тип разделителей (100000 == 100'000 == 100.000).

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

И нужно ли возвращать позицию окончания парсинга. Чтобы можно было продолжать с неё.

PS: у меня просто есть опыт написание своего парсера чисел из строк - это боль. Но иногда требуется.

RazrFalcon ★★★★★
()
Ответ на: комментарий от Crocodoom

Я бы, например, не считал ее валидной (почему я должен считать валидной строку, в которой присутствуют «левые» символы?)

Все парсеры работают по такому определению валидности.

Точно не все. Обычно парсеры разбирают _весь_ входной поток.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Я бы, например, не считал ее валидной (почему я должен считать валидной строку, в которой присутствуют «левые» символы?)

Все парсеры работают по такому определению валидности.

Точно не все. Обычно парсеры разбирают _весь_ входной поток.

Это спор на уровне определений. Я проясню, какого придерживаюсь я.

Парсеры разбирают столько, сколько смогут, и возвращают остаток входного потока, чтобы передать управление дальше. strtol в этом смысле абсолютно корректный парсер (а atoi — нет). Из него можно сделать более сложный, например можно парсить арифметические выражения. Парсер арифметических выражений комбинируется из парсеров чисел типа strtol, а также парсеров, отыскивающих всякие '*' и '+'. Такой парсер всё равно должен отдавать неразобранный остаток дальше. Потому что может быть составной частью ещё более сложного парсера, и так далее. Думаю, мысль понятна.

А проверить то, разобран ли в итоге __весь__ входной поток, очень просто: смотрим, пуст ли остаток входного потока. Зашивать это поведение внутрь самих парсеров (как функций библиотеки) нельзя, потому что это убивает всю идею комбинируемости, то есть построения сложных парсеров из простых.

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

Обычно парсеры разбирают _весь_ входной поток.

Это спор на уровне определений.

Нет. Это спор о том, что является least surprise.

Парсеры разбирают столько, сколько смогут, и возвращают остаток входного потока

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

А проверить то, разобран ли в итоге __весь__ входной поток, очень просто

В начале топика на вопрос «как это могло попасть в stdlib?» из щелей лезут персонажи, объясняющие, как этим пользоваться, а сейчас на утверждение «это не соответствует principle of least surprise» начинаются объяснения, как определить незавершенность разбора.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Обычно парсеры разбирают _весь_ входной поток.

Это спор на уровне определений.

Нет. Это спор о том, что является least surprise.

Я правильно понял, что считаешь возвращение 0 на строках с «плохим хвостом» больше соответствующим принципу least surprise? Потому что на большее atoi не способна в силу своей сигнатуры. Либо возвращать, сколько смогла разобрать, либо возвращать 0, если не дошла до конца строки.

Парсеры разбирают столько, сколько смогут, и возвращают остаток входного потока

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

Я его не придумал. Это называется комбинаторный парсинг, см. https://en.wikipedia.org/wiki/Parser_combinator Если ты не хочешь, чтобы после твоего парсера можно было вызвать какой-нибудь другой, то ты конечно можешь не отдавать остаток строки.

В начале топика на вопрос «как это могло попасть в stdlib?» из щелей лезут персонажи, объясняющие, как этим пользоваться

Вопрос изначально дискусионный, нечётко поставленный, и совершенно субъективный в плане оценки ответов на него. Так что не удивительно, что много людей решили высказаться, что они в целом думают про atoi. Тем более, что мы в Talks.

а сейчас на утверждение «это не соответствует principle of least surprise» начинаются объяснения, как определить незавершенность разбора.

Конкретно такого утверждения я не видел, но ради бога. Так что больше соответствует принципу, возвращение 0? Почему?

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

Я правильно понял, что считаешь возвращение 0 на строках с «плохим хвостом» больше соответствующим принципу least surprise?

Нет. atoi вообще пример как не надо делать - и обработка аргумета, и код возврата. Но я говорил про обработку аргумента.

Это называется комбинаторный парсинг

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

а сейчас на утверждение «это не соответствует principle of least surprise» начинаются объяснения, как определить незавершенность разбора.

Конкретно такого утверждения я не видел

Да? Ну, сейчас ты его видишь.

tailgunner ★★★★★
()

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

RisuX3
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.