Томпсон:
Ошибки есть ошибки. Вы пишете код с ошибками, просто потому что вы так пишете. Если язык безопасен в смысле безопасности времени выполнения, то операционная система упадет, а не переполнит буфер, став при этом уязвимой. Например, ping of death - атака на IP-стек операционной системы. Мне кажется, что будут еще атаки такого рода. Это будут не атаки типа “полностью захватить машину и стать суперпользователем”. Это будут ping of death.Сейбел:
Но ведь есть разница между отказом в обслуживании и уязвимостью, когда вы можете стать суперпользователем и делать, что захотите.Томпсон:
Есть две возможности стать суперпользователем: одна состоит в переполнении буфера, а другая - в том, чтобы указать программе сделать то, чего она делать не должна. В большинстве случаев реализуется вторая возможность, а не переполнение буфера. Вы можете стать суперпользователем без всяких переполнений. Так что ваш аргумент не работает. Все, что нужно сделать, - это уговоритьСейбел:
Ладно. Не будем говорить о том, что ведет к краху программы, уязвимости или чему-то подобному; есть такой класс ошибок, которые случаются в Си и C++, но почему-то не случаются, скажем, в Java. Есть ли для определенных типов приложений какие-то преимущества, весомые по сравнению с тем, что эти ошибки будут происходить и вызывать неприятности?Томпсон:
Думаю, этот класс ошибок доставляет не так уж много проблем. Разумеется, каждый раз когда я пишу один из вызовов функции, которая не проверяет длину строки, strcpy и тому подобное, я знаю, что фактически пишу ошибку. Но каким-то образом я принимаю решение о том, оправданна ли эта ошибка, нужно ли экономить аргументы. Сейчас обычно я их экономлю. Но имеется семантическая проблема: если вы обрезаете строку и используете обрезанную строку, то появляется новая проблема. Ошибка по-прежнему там, просто буфер еще не переполнен.Сейбел:
Какие инструменты вы используете при отладке?Томпсон:
В основном вывод на печать. При разработке программы я размещаю очень много операторов вывода на печать. И пока я их не убрал или не закомментировал, вывод на печать достаточно надежен. Мне редко приходится возвращаться к этому снова.Сейбел:
И что именно вы печатаете?Томпсон:
Все, что нужно, все, что удобно иметь под рукой. Инварианты. Но по большей части я печатаю во время разработки. Так я и выполняю отладку. Я не пишу программы с чистого листа - я беру программу и изменяю ее. Даже в большой программе я вывожу: “main, left, right, print, hello”. Да, “hello” - это не то, что я ожидаю от этой программы. Я вывожу на печать то, что ожидаю увидеть, и отлаживаю эту часть. При разработке я запускаю программу двадцать раз в час.Сейбел:
Вы печатаете инварианты; а используете ли вы утверждения, которые проверяют эти инварианты?Томпсон:
Редко. Мне проще убедить себя, что они правильны, и либо закомментировать вывод на печать, либо отбросить их.Сейбел:
Почему же тогда вам проще напечатать, что инвариант верен, чем использовать assert для автоматической проверки?Томпсон:
Потому что при печати вы действительно видите, что происходит, а не частные значения, и вы печатаете к тому же много всего, не только инварианты. Просто я делаю вот так. Я не предлагаю это как общую парадигму. Но сам я всегда делаю так.Сейбел:
Когда мы говорили о том, как вы создаете программы, вы упоминали процесс разработки снизу вверх. Вы строите эти кирпичики отдельно друг от друга?Томпсон:
Иногда.Сейбел:
А вы пишете тестовые модули для тестирования низкоуровневых функций?Томпсон:
Да, я так часто поступаю. Все зависит от программы, над которой я работаю. Если программа - это транслятор из А в Б, то я предложу целый спектр возможных А и соответствующих Б. Для регрессионного тестирования исполню все варианты А и проверю, насколько им будут соответствовать Б. Компилятор, транслятор, поиск регулярных выражений. Что-то вроде этого. Но есть программы, которые совсем не похожи на эти. Я никогда много не занимался тестированием и в этих программах мало понимаю. Я проведу несколько проверок, но они редко будут значительными, потому что, например, их будет тяжело проводить внутри самой программы. Главным образом это будут просто регрессионные тесты.Сейбел:
Под более сложными для тестирования программами вы понимаете, например, драйверы устройств и сетевые протоколы?Томпсон:
Вообще, они ведь запускаются каждый раз, когда вы запускаете систему.Сейбел:
То есть, по-вашему, таким образом можно избавиться от части ошибок?Томпсон:
Да, конечно. Что может быть лучше для тестирования операционной системы, чем постоянно запускать ее?Сейбел:
Еще один важный аспект программирования - оптимизация. Одни приступают к оптимизации с самого начала. Другие предпочитают сначала написать код, а потом уже думать о дальнейшей оптимизации. Каков ваш подход?