Как подключить cpp файл c

Обновлено: 05.07.2024

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

• Вы должны использовать компилятор C++, когда компилируете тело функции main() (например, для статической инициализации).
• Ваш компилятор C++ должен управлять процессом линковки (например, могут использоваться его специальные библиотеки).
• Ваши компиляторы C и C++ возможно должны быть от одного производителя, и иметь совместимые друг с другом версии (например, должны использовать одинаковые соглашения о вызовах функций).

Кроме того, Вам следует разобраться (см. далее), как сделать свои функции C вызываемыми из кода C++, и/или свои функции C++ вызываемыми из C.

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

Как вызывать функцию C из C++? Просто декларируйте C-функцию в коде C++ с помощью директивы extern "C", и вызовите её (из кода C или кода C++). Пример:

Реализация функций может быть совершенно стандартная для C:

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

Также имейте в виду, что действуют правила типов C++, не правила типов C. Таким образом, Вы не сможете вызвать функцию, которая определена через extern "C", с неправильным количеством аргументов. Например:

Как вызывать функцию C++ из кода C? Просто декларируйте функцию C++ с директивой extern "C" (в коде C++) и вызовите её (из кода C или C++). Пример:

Теперь функция f() может использоваться следующим образом:

В реальности это работает только для функций, которые не являются членами класса. Если Вы хотите вызвать функцию - члена класса (включая функции virtual) из C, то понадобится реализовать простую обертку. Например:

Теперь функция C::f() может быть вызвана следующим образом:

Если Вы хотите вызвать перегруженные (overloaded) функции из C, то нужно предоставить обертки с разными именами для каждой реализации перегруженной функции, чтобы их можно было использовать в коде C. Например:

Теперь разные перегрузки функции f() можно вызвать следующим образом:

Обратите внимание, что эти техники могут использоваться для вызова библиотечных функций C++ из кода C, даже если Вы не можете (или не хотите) модифицировать заголовочные файлы C++.

Как можно подключить стандартный заголовочный файл C в код C++? Для подключения стандартного хедера (такого как < cstdio>) Вы не должны делать ничего необычного. Например:

Если Вы думаете, что часть std:: в вызове std::printf() необычным, то лучшее, что следует сделать, это просто привыкнуть к этому. Другими словами, это стандартный способ использовать имена в стандартной библиотеке, так что начинайте к этому привыкать.

Однако если Вы компилируете код C компилятором C++, то скорее всего не захотите переделывать все эти вызовы из printf() в std::printf(). К счастью, в этом случае код C будет использовать заголовочный файл старого стиля < stdio.h> вместо заголовочного файла нового стиля < cstdio>, и обо всем остальном позаботится магия пространства имен (namespaces):

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

Шаг 1: поместите следующие строки в начало заголовочного файла C (символ __cplusplus автоматически определяется компилятором C++, компилятор C этот символ не определяет):

Шаг 2: Поместите следующие строки в конец заголовочного файла C:

После этого Вы можете свободно подключать оформленный таким образом заголовок как из кода C, так и из кода C++, не заботясь ни о каких директивах extern "C":

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

Несколько таких функций можно сгруппировать в блок фигурными скобками:

После этого просто вызовите функцию, как если бы это была функция C++:

Как создать C++ функцию f(int,char,float), которую можно вызывать из кода C? Компилятор C++ должен четко знать, что функция f(int,char,float) будет вызываться из кода компилятора C. Такое указание компилятору C++ делает конструкция extern "C":

Строка extern "C" говорит компилятору C++, что информация, посылаемая линкеру, должна использовать соглашения о вызовах C, и также должна использоваться декорация имен языка C (name mangling, т. е. присоединение к имени функции префикса одиночного нижнего подчеркивания). Поскольку перегрузка имен функций не поддерживается языком C, то Вы не можете сделать некоторые перегружаемые функции одновременно вызываемыми из программы на C.

Почему линкер выдает ошибки на функции C/C++, которые вызываются из кода C++/C? Если Вы не всегда правильно используете extern "C" (если вообще делаете это), то будете иногда сталкиваться с ошибками линкера вместо ошибок компиляции, когда смешиваете код C и C++. Причина здесь в том, что фактически компиляторы C++ обычно декорируют ("mangle") имена функций не так, как это делают компиляторы C, и поэтому линкер не находит ожидаемые имена в объектных или библиотечных файлах.

См. два предыдущих вопроса о том, как использовать extern "C".

Как передать объект класса C++ в функцию C и из неё? Ниже дан пример (как использовать extern "C", см. предыдущие вопросы).

Заголовочный файл Fred.h:

В отличие от кода C++, коду на C нельзя указать, что два указателя ссылаются на тот же самый объект, если указатели не будут иметь совершенно одинаковый тип. Например, на C++ просто проверить, указывает ли dp (указатель на объект производного класса Derived*) на тот же объект, что и указатель bp (указатель на объект базового класса Base*), это делается обычной проверкой if (dp == bp) . Компилятор C++ автоматически преобразует оба указателя в одинаковый тип, в этом случае к базовому типу, после чего проверит их равенство. В зависимости от подробностей реализации компилятора C++, это преобразование иногда меняет биты значения указателя.

Технический момент: большинство компиляторов C++ используют двоичную структуру объекта, которая заставляет это преобразование происходить с поддержкой множественного наследования (multiple inheritance) и/или виртуального наследования (virtual inheritance). Однако язык C++ не навязывает ограничения на реализацию этого принципа преобразования, так что оно может произойти даже с не виртуальным одиночным наследованием (non-virtual single inheritance).

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

Важное замечание: особенно осторожным следует быть при преобразовании обоих указателей в void*, поскольку такое приведение типов не даст возможности ни компилятору C, ни компилятору C++, выполнить правильное преобразование! Сравнение (x == y) может быть false даже если (b == d) равно true:

Как было показано выше, эти преобразования указателя обычно будут происходить с множественным и/или виртуальным наследованием, однако не рассматривайте эти случаи как исчерпывающий список преобразований указателей, которые будут происходить. По крайней мере, Вас предупредили.

Если Вы все-таки очень хотите использовать указатели void*, вот безопасный способ:

Может ли моя функция C получить прямой доступ к данным объекта класса C++? Не всегда. Базовые инструкции по передаче данных объектов C++ в функцию C и из функции C см. в предыдущих вопросах.

Вы можете безопасно получить доступ к данным объекта C++ из функции C, если этот класс C++:

• Не имеет виртуальных функций (включая унаследованные виртуальные функции).
• Помещает все свои данные на одном и том же уровне доступа (private/protected/public).
• Не имеет полностью встроенных подчиненных объектов (fully-contained subobjects) с виртаульными функциями.

Если класс C++ основан на любом базовом классе (или если любые полностью встроенные подчиненные объекты основаны на базовом класса), то доступ к данным класса технически будет не портируемым, потому что двоичная структура наследуемых объектов не не определяется синтаксисом языка. Однако на практике все компиляторы C++ делают это одинаково: объект базового класса появляется первым (в порядке слева направо при событии множественного наследования), и за ним идут объекты члены класса.

Кроме того, если класс (или любой его базовый класс) содержит любые виртуальные функции, почти все компиляторы C++ помещают указатель void* в объект либо на месте первой виртуальной функции, или в самом начале объекта. Опять-таки, этот принцип не определяется языком, просто "все делают это".

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

Почему с языком C++ кажется, что я "удаляюсь от машины", в отличие от языка C? Ни почему, просто это так и есть.

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

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

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

C++ не пытается запретить плохим программистам писать плохие программы. Вместо этого C++ дает возможность разумным разработчикам создавать превосходные программы.

По мере увеличения размера программ весь код уже не помещается в нескольких файлах, записывать каждый раз предварительные объявления для функций, которые мы хотим использовать, но которые находятся в других файлах, становится всё утомительнее и утомительнее. Хорошо было бы, если бы все предварительные объявления находились в одном месте, не так ли?

Заголовочные файлы из Стандартной библиотеки C++

Рассмотрим следующую программу:

Результат выполнения программы:

Как правило, в заголовочных файлах записываются только объявления, без определений. Следовательно, если cout только объявлен в заголовочном файле iostream, то где же он определяется? Ответ: в Стандартной библиотеке С++, которая автоматически подключается к вашему проекту на этапе линкинга.


Пишем свои собственные заголовочные файлы

Теперь давайте вернемся к примеру, который мы обсуждали на предыдущем уроке. У нас было два файла: add.cpp и main.cpp.

int add ( int x , int y ) ; // предварительное объявление с использованием прототипа функции std :: cout << "The sum of 3 and 4 is " << add ( 3 , 4 ) << std :: endl ;

Примечание: Если вы создаете все файлы заново, то не забудьте добавить add.cpp в свой проект, чтобы он был подключен к компиляции.

И здесь нам на помощь приходят заголовочные файлы. Достаточно просто написать один заголовочный файл и его можно будет повторно использовать в любом количестве программ. Также и вносить изменения в такой код (например, добавление еще одного параметра) гораздо легче, нежели чем шерстить по всем файлам в поисках используемых функций.

Написать свой собственный заголовочный файл не так уж и сложно. Заголовочные файлы состоят из двух частей:

Директивы препроцессора — в частности, header guards, которые предотвращают вызов заголовочного файла больше одного раза из одного и того же файла (об этом детально на следующем уроке).

Содержимое заголовочного файла — набор объявлений.

Все ваши заголовочные файлы (которые вы написали самостоятельно) должны иметь расширение .h .

// Начнем с директив препроцессора. ADD_H – это произвольное уникальное имя (обычно используется имя заголовочного файла) int add ( int x , int y ) ; // прототип функции add() (не забывайте точку с запятой в конце!)

Чтобы использовать этот файл в main.cpp, вам сначала нужно будет подключить его к проекту.

main.cpp, в котором мы подключаем add.h:

std :: cout << "The sum of 3 and 4 is " << add ( 3 , 4 ) << std :: endl ;

add.cpp остается без изменений:

Если вы получили ошибку от линкера, что функция аdd() не определена, то убедитесь, что вы корректно подключили add.cpp к вашему проекту (и к компиляции тоже)!

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

Почему iostream пишется без окончания .h?

Когда C++ только создавался, все файлы библиотеки Runtime имели окончание .h. Оригинальные версии cout и cin объявлены в iostream.h. При стандартизации языка С++ комитетом ANSI, решили перенести все функции из библиотеки Runtime в пространствo имен std, чтобы предотвратить возможность возникновения конфликтов имен с пользовательскими идентификаторами (что, между прочим, является хорошей идеей). Тем не менее, возникла проблема: если все функции переместить в пространство имен std, то старые программы переставали работать!

Когда вы подключаете заголовочный файл из Стандартной библиотеки C++, убедитесь, что вы используете версию без .h (если она существует). В противном случае, вы будете использовать устаревшую версию заголовочного файла, который уже больше не поддерживается.

Кроме того, многие библиотеки, унаследованные от языка Cи, которые до сих пор используются в C++, также были продублированы с добавлением префикса c (например, stdlib.h стал cstdlib). Функционал этих библиотек также перенесли в пространство имен std, чтобы избежать возможность возникновения конфликтов имен с пользовательскими идентификаторами.

Можно ли записывать определения в заголовочных файлах?

Язык C++ не будет жаловаться, если вы это сделаете, но так делать не принято.

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

Советы

Вот несколько советов по написанию собственных заголовочных файлов:

Всегда используйте директивы препроцессора.

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

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

Обычно подключаемые файлы заканчиваются на '.h' и следует избегать использования других стандартов.

Эта модификация используется для подключения системных файлов. При ее выполнении производится поиск файла с именем FILE в списке указанных заранее каталогов, а затем в стандартном списке системных каталогов. С помощью опции '-I' указываются каталоги для поиска подключаемых файлов. Опция '-nostdinc' запрещает поиск в стандартных системных каталогах и производит поиск только в указанных каталогах.

Аргумент FILE не может содержать символа '>', хотя он может содержать символ '<'.

Эта модификация применяется для подключаемых файлов для программ пользователя. Сначала файл FILE просматривается в текущем каталоге, а затем в каталогах для системных подключаемых файлов. Текущим каталогом является каталог текущего обрабатываемого файла. Он просматривается в первую очередь, так как предполагается, что в нем находятся файлы, имеющие отношение к текущему обрабатываемому файлу. (Если указана опция '-I-', то текущий каталог не просматривается.)

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

и основная программа с именем 'program.c', использующая этот файл. Данные, полученные при обработке программы 'program.c' будут выглядеть следующим образом: Для подключаемых файлов нет ограничений на объявления и макроопределения. Любой фрагмент С программы может быть включен в другой файл. Подключаемый файл может даже содержать начало выражения, заканчивающееся в исходном файле или окончание выражения, начало которого находится в исходном файле. Хотя комметарии и строковые константы не могут начинаться подключаемом файле и продолжаться в исходном файле. Не завершенный комментарий, стороковая или символьная константа в подключаемом файле приводят к возникновению ошибки в конце файла.

Подключаемый файл может содержать начало или окончание сиснтаксической конструкции, такой как определение функции.

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

Обычно это достигается путем заключения в условие всего содержимого этого файла, как показано ниже:

Макрос 'FILE_FOO_SEEN' указывает на то, что файл уже однажды вкючался. В подключаемых файлах пользователя макрос не должен начинаться с символа '_'. В системных подключаемых файлах его имя не должно начинаться с символа '__' во избежание возникновения конфликтов с программами пользователя. Каким бы ни был файл, имя макроса должно содержать имя файла и некоторый дополнительный текст во избежание вознкновения конфликтов с другими подключаемыми файлами.

"Наследование" это то, что происходит, когда какой либо объект или файл образует некоторую часть своего содержимого путем виртуального копирования из другого объекта или файла. В случае подключаемых С файлов наследование означает, что один файл включает другой файл, а затем заменяет или добавляет что-либо.

Иногда необходимо чтобы у наследуемого и основного подключаемого файла были одинаковые имена.

Например, предположим, что прикладная программа использует системный подключаемый файл 'sys/signal.h', но версия файла '/usr/include/sys/signal.h' на данной системе выполняет того, что требуется в прикладной программе. Будет удобнее определить локальную версию, возможно с именем '/usr/local/include/sys/signal.h' для замены или добавления к версии, поставляемой с системой.

У меня есть C ++ .h и .cpp файл из другого проекта, который я хочу включить в свой проект.
Я не хочу копировать файлы в свой проект, так как я хочу, чтобы любые изменения этих файлов были применены к обоим проектам.

Я включил каталог папки файла в

Свойства-> Каталоги VC ++-> Включить каталоги

Я также включил папку в

Свойства-> C / C ++ -> Общие -> Дополнительные каталоги включения

Кажется, что файлы .h работают. Если я переименую включить что-либо, кроме

Файл cpp получает неизвестные определения.

Когда я компилирую. Ошибка

Если я удалю файл cpp из проекта. Затем я получаю длинный список нерешенных функций.

Как я могу включить файлы .h и .cpp во второй проект?

Решение

Для файлов cpp вы можете просто щелкнуть правой кнопкой мыши по проекту, выбрать «добавить» -> существующий элемент.
Затем они должны скомпилироваться с другими, когда начнется сборка.

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

В зависимых проектах после настройки Дополнительные каталоги включения Вы можете сослаться на эти включает в себя как

Этот подход поможет вам в будущем, так как вы можете повторно использовать этот общий модуль в каждом проекте, который вы хотите.

Другие решения

Попробуйте перетащить их в свое решение?
Вы можете создать новую папку в своем решении и перетащить их все в эту папку!

Вы не можете просто выбрать такие файлы. Есть два разумных способа решить это. 1, предоставил общий доступ к файлу с помощью системы управления версиями кода (например, svn / git). 2, скомпилируйте .cpp в библиотеку и создайте ссылку на эту библиотеку.

Если cpp может использоваться несколькими проектами, это должно означать, что код является чем-то общим. Это означает, что вы должны скомпилировать этот код в библиотеку, а затем совместно использовать эту библиотеку. Компиляция одного и того же cpp в несколько библиотек может привести к конфликтам позже, если две такие библиотеки когда-либо понадобятся для совместной работы.

В проекте С++, включая файлы .h из исходных файлов C, вы получите много ошибок из-за разных стандартов между C и С++.

Как использовать исходные файлы C в проекте С++ (или в main.cpp)?

спросил(а) 2012-12-04T04:13:00+04:00 8 лет, 11 месяцев назад

Для максимальной надежности:

    Скомпилируйте источник C с компилятором C.
    Скомпилировать исходный код С++ с помощью компилятора С++
    Предпочтительно, напишите функцию main() в С++.
    Свяжите программу с компилятором С++.

Убедитесь, что заголовки C либо сами знают С++, либо что код С++ содержит заголовки C внутри блока extern "C" < . >.

Либо (файл заголовка C cheader.h ):

или (исходный код на С++):

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

Однако, если компилятор MSVC говорит, что программы, использующие MFC, должны быть записаны исключительно на С++ (MFC требует, чтобы компиляция С++ (с использованием суффикса .cpp) была сообщенной ошибкой), тогда у вас может не быть выбора, кроме как обеспечить, чтобы ваш код C можно компилировать как код на С++. Это означает, что вам нужно будет отображать возвращаемые значения из malloc() et al; вам нужно беспокоиться о других местах, где вы не используете приведение, чтобы преобразовать void * в другой тип указателя; вам нужно беспокоиться о sizeof('a') == 4 в C и sizeof('a') == 1 в С++; вы должны обеспечить, чтобы каждая функция была объявлена ​​до ее использования; вы должны убедиться, что ваш C-код не использует ключевые слова С++ (в частности, typename , class , а также inline ), но полный список довольно большой).

В некоторых кругах вам придется беспокоиться об использовании функций на C99, которые не находятся в С++ 2003 или С++ 2011, например, о гибких элементах массива, назначенных инициализаторах, сложных литералах, массивах переменной длины, и так далее. Однако, если код C для MSVC, то это, вероятно, не будет проблемой; эти функции не поддерживаются компилятором MSVC C (он поддерживает только C89, а не C99).

FWIW: У меня есть script для поиска ключевых слов на С++. Он содержит следующий комментарий:

Читайте также: