От новичка до гуру: Курсы программирования на CyberDuff

Историческая причина объявления перед использованием, включение и разделение заголовка / источника. Нужно найти подходящую ссылку

TL; DR: см. Последний абзац этого вопроса.

Я изучаю информатику, пытаюсь закончить текст своей магистерской диссертации о создании транспилятора (тематическое исследование).

Что касается магистерской диссертации, то часть моего текста посвящена сравнению задействованных языков. Один из языков - C ++.

Теперь я пытаюсь объяснить разницу в семантике импорта / включения и историческую причину, по которой C ++ сделал это именно так. Я знаю, как это работает на C / C ++, поэтому мне не нужны технические объяснения.

Тщательно исследуя Google и Stackoverflow, я пришел к нескольким объяснениям stackoverflow и другим ссылкам на эту тему:

Зачем нужны форвардные объявления?

Что такое форвардные объявления в C ++?

Зачем C ++ нужен отдельный файл заголовка?

http://en.wikipedia.org/wiki/Include_directive

http://www.cplusplus.com/forum/articles/10627/

https://softwareengineering.stackexchange.com/questions/180904/are-header-files-actually-good

http://en.wikipedia.org/wiki/One-pass_compiler

Зачем нужны файлы заголовков и файлы .cpp в C ++?

И наконец, что не менее важно, книга Бьярна Страуструпа «Дизайн и эволюция C ++ (1994)» (страницы 34 - 35).

Если я правильно понимаю, этот способ импорта / включения пришел из C и появился по следующим причинам:

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

  • В то время память была очень ограниченной и дорогой. Поэтому на большинстве компьютеров было невозможно хранить всю таблицу символов в памяти. Вот почему C позволяет программистам напрямую объявлять прототипы функций и глобальные переменные, которые им действительно нужны. Заголовки были созданы для того, чтобы разработчики могли централизованно хранить эти объявления, чтобы их можно было легко повторно использовать в модулях, которым требуются эти символы.

  • Заголовочные файлы были полезным способом абстрагировать интерфейс от реализации.

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

Вышеупомянутое было иллюстрацией того, что я обнаружил, сначала выполнив поиск;) Проблема в том, что я не могу найти подходящую ссылку на исторические причины этой стратегии включения, кроме как здесь, в Stackoverflow. И я очень сомневаюсь, что мой университет будет доволен ссылкой на stackoverflow. Ближайшее, что я нашел, - это справочник «Дизайн и эволюция C ++», но в нем не упоминаются аппаратные ограничения, являющиеся причиной для стратегии включения. Я думаю, этого следовало ожидать, потому что дизайн этой функции был разработан на C. Проблема в том, что я еще не нашел хорошего источника, описывающего это проектное решение на C, желательно с учетом аппаратных ограничений.

Может ли кто-нибудь указать мне в правильном направлении?

Спасибо!

09.06.2014

  • Ваша первая ссылка ясно объясняет это. Что касается поиска ссылок, я не уверен, сможете ли вы найти подходящие ссылки для каждого решения, принятого в начале 1970-х годов ... но удачи :) 09.06.2014
  • Я знаю, именно отсюда и пришла большая часть моих объяснений;) Но я сомневаюсь, что мой университет примет эту ссылку. Вот почему я спрашиваю. Но все равно спасибо за ответ;) Я очень надеюсь, что еще смогу его найти. 09.06.2014

Ответы:


1

Вы правы, причина того, что C ++ делает это таким образом, заключается в том, что C сделал это таким образом. Причина, по которой C сделал это, также основана на истории; в самом начале (Б) деклараций не было. Если вы написали f(), то компилятор предполагает, что f где-то функция. Что вернуло слово, так как все в B было словом; типов не было. Когда был изобретен C (для добавления типов, поскольку все является словом, не очень эффективно для машин с байтовой адресацией), основной принцип не изменился, за исключением того, что предполагалось, что функция возвращает int (и принимает аргументы типа вы его дали). Если он не вернул int, вам нужно было переадресовать его объявление с типом возвращаемого значения. В ранние дни C нередко можно было встретить приложения, которые не использовали include, и которые просто повторно объявлялись, например char* malloc() в каждом исходном файле, который использовал malloc. Препроцессор был разработан, чтобы избежать повторного набора одного и того же слова несколько раз, и в самом начале его наиболее важной особенностью, вероятно, была #define. (В раннем C все функции в <ctype.h> и символьный ввод-вывод в <stdio.h> были макросами.)

Что касается того, почему объявление должно предшествовать использованию: основная причина, несомненно, заключается в том, что в противном случае компилятор принял бы неявное объявление (функция, возвращающая int, и т. Д.). А в то время компиляторы обычно выполняли один проход, по крайней мере, для синтаксического анализа; считалось слишком сложным возвращаться к «правильному» предположению, которое уже было сделано.

Конечно, в C ++ язык не так сильно ограничен этим; В C ++ всегда требовалось, например, объявлять функции, а в определенных контекстах (например, в функциях-членах класса) не требуется, чтобы объявление предшествовало их использованию. (Как правило, однако, я считаю, что функции-члены класса являются ошибкой, которую следует избегать по причинам удобочитаемости. Тот факт, что определения функций должны находиться в классе в Java, является основной причиной не используйте этот язык в больших проектах.)

09.06.2014
  • Я считаю, что OP точно знает бит почему. OP просто запрашивает авторитетную ссылку на то же самое (например, статью Денниса Ричи или что-то подобное). 09.06.2014
  • Да, Голубая луна права, это не совсем то, что я ищу, но, тем не менее, он интересен. Я знал, что C предполагает возвращаемый тип int, если тип возвращаемого значения не указан, но я не знал, что компилятор C не будет жаловаться на неопределенные символы. Мне это кажется очень странным. Спасибо за ответ, я еще разберусь с этим;) Возможно, я тоже мог бы упомянуть это в своем тексте. 09.06.2014
  • @ Moonsurfer_1 Он будет (и всегда будет) жаловаться на необъявленные переменные, хотя даже там неявный int управляется. (Например, вы можете написать static a;, и это будет интерпретировано как static int a;.) И я признаю, что здесь не так много вы могли бы процитировать в университетской статье - это более личные воспоминания. Спровоцировано в основном заявлениями о том, что это было связано с медленными машинами или чем-то еще; Это был просто более или менее стандартный способ делать что-то в то время (и правил даже в таких языках, как Паскаль, в которых не было никаких неявных деклараций). 09.06.2014
  • Новые материалы

    Путь AWS  — «Изучение машинного обучения — 10 начинающих ИИ и машинного обучения на AWS».
    Универсальный ресурсный центр для изучения искусственного интеллекта и машинного обучения. НОЛЬ или ГЕРОЙ, начните свое путешествие здесь. Получите решения и пройдите обучение у экспертов AWS...

    5 простых концепций Python, ставших сложными
    #заранее извините 1) Переменные x = 4 y = 5 Переменная в Python — это символическое представление объекта. После присвоения некоторого объекта переменной Python мы приобретаем..

    «Освоение вероятности: изучение совместной, предельной, условной вероятности и теоремы Байеса —…
    Виды вероятности: Совместная вероятность Предельная вероятность Условная вероятность Диаграмма Венна в вероятностях: В “Set Theory” мы создаем диаграмму Венна...

    Основы Spring: Bean-компоненты, контейнер и внедрение зависимостей
    Как лего может помочь нашему пониманию Когда мы начинаем использовать Spring, нам бросают много терминов, и может быть трудно понять, что они все означают. Итак, мы разберем основы и будем..

    Отслеживание состояния с течением времени с дифференцированием снимков
    Время от времени что-то происходит и революционизирует часть моего рабочего процесса разработки. Что-то более забавное вместо типичного утомительного и утомительного процесса разработки. В..

    Я предполагаю, что вы имеете в виду методы обработки категориальных данных.
    Я предполагаю, что вы имеете в виду методы обработки категориальных данных. Пожалуйста, проверьте мой пост Инструментарий специалиста по данным для кодирования категориальных переменных в..

    Игра в прятки с данными
    Игра в прятки с данными Я хотел бы, чтобы вы сделали мне одолжение и ответили на следующие вопросы. Гуглить можно в любое время, здесь никто не забивается. Сколько регионов в Гане? А как..