Опубликован: 06.10.2011 | Доступ: свободный | Студентов: 1681 / 107 | Оценка: 4.67 / 3.67 | Длительность: 18:18:00
Дополнительный материал 4:

От C++ к C

Язык C++ является расширением C и представляет редкий пример языка почти полной обратной совместимости с предшественником - правильная C-программа обычно является и правильной C++ программой и дает те же результаты.

Язык C++ не заменяет C. В своем исходном состоянии язык C сохранил свою значимость для хорошо определенной области приложений - программирование на уровне операционной системы и аппаратуры. В частности, поэтому едва ли любой процессор существует без C-компилятора. А может быть все наоборот: никто не отважится выпустить процессор без C-компилятора, поскольку этого ожидает рынок. В любом случае C де-факто является стандартом для низкоуровневого программирования.

Большинство C-программистов знают и C++. По этой причине в данном приложении не описываются основы языка: предполагается, что вы прочли предыдущее приложение, так что здесь просто перечисляются конструкции C++, недоступные в C. Это можно сделать быстро, так что приложение короткое.

Такая концепция описания (указание различий с другим языком) объясняет также, почему, в отличие от других приложений, обсуждение основ и стиля C вынесено в самый конец.

Отсутствующие элементы

Исходя из описания C++, нисходя от C++ к C, следует опустить:

  • все ОО-механизмы: классы и объекты, члены функции (для структур), наследование, виртуальные функции, конструкторы, деструкторы, ссылки (но указатели остаются);
  • шаблоны (в C нет универсальности);
  • аргументы по умолчанию (функции с переменным числом аргументов доступны через библиотечный механизм, известный как varargs.);
  • механизмы управления доступом и friend - механизм друзей, а также операцию разрешения области;
  • пространства имен;
  • исключения.

Большинство из того, что осталось, включает:

  • статические функции;
  • слияние операторов и выражений;
  • указатели (не ссылки) и возможность манипулировать ими, используя адресную арифметику;
  • структуры управления, кроме исключений;
  • операции с побочным эффектом, такие как ++;
  • синтаксические соглашения: скобки вместо ключевых слов, знак = для присваивания, точка с запятой для завершения операторов;
  • доступность препроцессора, в частности, переменных времени компиляции, позволяющих управлять условной компиляцией (#ifdef compile_time_variable, где значение compile_time_variable устанавливается вне программы, например, как опция компиляции).

Основы языка и стиль

Язык C возник в результате исследований, проводимых в AT&T's Bell Laboratories в конце шестидесятых с целью получения преимуществ от идей структурного программирования, с сохранением при этом возможности прямого доступа к механизмам машинного уровня. Последнее требование связано не только с проблемами производительности; другая причина состояла в том, что на C предстояло написать операционную систему - первую версию Unix. Этот проект был успешным, и все последующие версии Unix (и нескольких других операционных систем) были написаны на C.

Язык C ценится за возможность управления низкоуровневыми аспектами приложения - то, что называется возможностью работы на уровне операционной системы и аппаратуры. Такая работа поддерживается возможностью прямого манипулирования адресами, в частности, использованием указателей (* и & -операций), адресной арифметики и идеи о том, что целью присваивания может быть любое выражение (l-value), которое может обозначать адрес.

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

Компромисс устанавливает пределы разумного использования C. Хотя C продолжает применяться для разработки больших систем, но это не лучшее его использование. Две важных области применения остаются для C: небольшие программы для прямого доступа к ресурсам и целевой язык для переносимых компиляторов.

В своей первой роли C остается инструментом прямого использования программистами. Наблюдения показывают, что для основной части любого приложения нет нужды в низкоуровневых аспектах C, они будут только страдать от них, например, рискуя получить самые неприятные ошибки доступа к памяти в период выполнения. Некоторой специализированной части приложения может, однако, понадобиться прямое взаимодействие с платформой (аппаратура плюс операционная система). Программисты должны обеспечить эти механизмы в форме четко специфицированных и тщательно прописанных функций, типично написанных на C и точно так же типично коротких. Примером может быть процедура, посылающая информацию через сокет (абстрактное сетевое соединение). Такие программы, обычно не более нескольких строчек или нескольких десятков строчек, должны быть сгруппированы в библиотеку и доступны остальной части ПО через тщательно разработанный интерфейс - API.

Библиотека EiffelBase использует этот подход при реализации таких абстракций, как массивы и файлы. Для остального мира соответствующие классы являются нормальными Eiffel-классами с контрактами; их реализации просто вызывают короткие внешние C-функции.

В своей второй роли C служит целевым языком для компилятора некоторого языка программирования - ЯП, предлагающего более высокий уровень абстракции, чем C. Такой прием представляет важные преимущества.

  • Почти универсальная доступность C-компиляторов облегчает конструирование переносимых компиляторов (где переносимость означает возможность поддержки разных платформ). Компилятор ЯП может сконцентрироваться на аспектах компиляции ЯП-программ, не зависящих от платформы, создавая результирующий код на C и оставляя C-компилятору задачу дальнейшего преобразования кода в машинный код для соответствующей платформы.
  • Генерируемый C код может все же включать и элементы, зависящие от платформы, используя возможности условной компиляции.
  • Технология C-компиляции хорошо понятна; значительная работа по оптимизации выполняется C-компиляторами. Авторы ЯП-компиляторов могут концентрироваться на ЯП-специфических оптимизациях и могут обычно полагаться на C-компилятор для выполнения стандартных оптимизаций конструкций нижнего уровня, таких как арифметические выражения, оптимизацию вычислений которых не нужно делать для каждого частного языка.

Этот подход успешно используется многими компиляторами, включая Eiffel-компиляторы.

Другие применения C, отличные от двух только что описанных, кажется, с трудом будут преодолевать ограничения C. Тем не менее, в своей области создано множество полезных решений за сорок лет существования этого высоко успешного языка.

Дальнейшее чтение

Brian W. Kernighan and Dennis M. Ritchie: The C Programming Language, second edition, Prentice Hall, 1988.

На русском языке это букинистическая редкость, но доступна на многих сайтах.

Библия C-программирования от авторов языка, известная как "K&R", ценимая за ясность и выразительность. Есть множество других книг по C, но трудно, кажется, прочесть что-либо другое, отличное от "K&R", для овладения C.