144
включая даже и сам машинный код программы, что может приводить к
непредсказуемым эффектам. Несмотря на то, что большинство указателей, как
правило, указывают на безопасные места, они легко могут быть передвинуты в
уже небезопасные области памяти с помощью арифметики указателей; память,
на которую они указывают, может быть освобождена и использована по
-
другому («висячие указатели»); они могут быть не инициализированы («дикие
указатели»); или же они просто могут получить любое значение путём
приведения типов или присваивания значения другого повреждённого
указателя. Другие языки пытаются решить эти проблемы путём использования
более ограниченных типов – ссылок.
Одна из таких проблем заключается в том, что автоматически и
динамически
создаваемые объекты не инициализируются, поэтому в начале они
имеют такое значение, какое осталось в памяти, выделенной для них от ранее
удалённых объектов. Такое значение полностью непредсказуемо, оно меняется
от одной машины к другой, от запуска к запуску, от вызова функции к вызову.
Если программа попытается использовать такое неинициализированное
значение, то придёт
к непредсказуемому результату. Большинство
современных компиляторов пытаются обнаружить эту проблему в некоторых
случаях.
Функции с переменным количеством аргументов также являются
потенциальным источником проблем. В отличие от обычных функций,
имеющих прототип, стандартом не регламентируется проверка функций с
переменным числом аргументов. Если передаётся неправильный тип данных, то
возникает непредсказуемый, если не фатальный
результат. Например,
семейство функций printf стандартной библиотеки языка Си, используемое для
генерации форматированного текста для вывода, хорошо известно своим
потенциально опасным интерфейсом с переменным числом аргументов,
которые описываются строкой формата. Проверка типов в функциях с
переменным числом аргументов является задачей каждой конкретной
реализации такой функции, однако многие современные компиляторы
проверяют типы в
каждом вызове printf, генерируя предупреждения в случаях,
когда список аргументов не соответствует строке формата. Следует заметить,
что невозможно статически проконтролировать даже все вызовы функции
printf, поскольку строка формата может создаваться в программе динамически,
поэтому, как правило, никаких проверок других функций с переменным числом
аргументов компилятором не производится. Для помощи программистам на Си
в решении этих и многих других проблем было создано большое число
отдельных от компиляторов инструментов. Такими инструментами являются
программы дополнительной проверки исходного кода и поиска
распространённых ошибок, а также библиотеки, предоставляющие
дополнительные функции, не входящие в стандарт языка, такие как проверка
границ массивов или ограниченная форма сборки мусора.