Как вызвать функцию из другого файла c
Перейти к содержимому

Как вызвать функцию из другого файла c

Воспользоваться функцией из другого cpp/h файла

Во-первых, в .h -файлах нет никаких функций. Это просто кусок текста, вставляемый перед компиляцией вместо соответствующих директив #include . Соответственно, с точки зрения компилятора нет никакой разницы, включили ли вы заголовочный файл или просто взяли и скопировали его содержимое в свой .cpp руками.

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

  • decl.h:

  • main.cpp:

  • implementation.cpp:

и повтором объявления в каждом .cpp -файле, которому нужна эта функция:

  • main.cpp:

  • implementation.cpp:

Во-вторых, .cpp -файл — это «вещь в себе», чёрный ящик с двоичными данными и списком того, что этот ящик импортирует/экспортирует, и по каким байтовым смещениям в вышеуказанных данных это требуется/реализуется соответственно:

Если в .cpp -файле производится обращение к чему-то, нереализованному в этом файле, и у компилятора есть объявление (то есть прототип для проверки корректности обращения), генерируется импорт этого чего-то по имени.

Если в .cpp -файле объявляется не- static переменная либо функция (методы классов, кстати, тоже полноценные функции), генерируется экспорт.

И только потом, на этапе компоновки производится связывание соответствующих импортов и экспортов этих «чёрных ящиков» (а также подобных «ящиков», составляющих реализацию стандартной библиотеки).

Вызов функции из другого файла в том же каталоге в C

Я изучаю C, но у меня большой опыт работы с языками программирования более высокого уровня, такими как Java.

Я читал о файлах заголовков, поэтому я играл с ними, однако я заметил, что могу вызвать функцию из другого файла без #включая его (он находится в том же каталоге), как это возможно ?! Это файл make, компоновщик, который настроен таким образом или что ?

у нас есть два файла

main.c вызывает функцию add(int x,int y) С добавить добавить.c, но я ошибочно скомпилировал перед #включая add.c и это сработало ! Что делает его более запутанным, так это то, что когда я #include add.c, он дает ошибку множественного определения на функции add

4 ответов

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

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

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

и компилятор ожидал бы, что функция «квадрат» определена в другом месте.

теперь, если у вас есть два разных файла, которые вы хотите взаимодействовать (например, предположим, что функция «квадрат» определена в add.c, и вы хотите вызвать квадрат (10) в main.c), вам нужно сделать и определение и объявление. Во-первых, вы определяете квадрат вдобавок.c. Тогда ты!—25—>объявить это в начале main.c. Это позволяет компилятору знать, когда он компилирует main.c что существует функция «квадрат», которая определена в другом месте. Теперь вам нужно скомпилировать оба main.c и add.C в объектные файлы. Вы можете сделать это, позвонив

это приведет к созданию файлов main.O и добавить.o. Они содержат скомпилированные функции,но не являются полностью исполняемыми. Важно понять, что здесь главное.o «неполный» в некотором смысле. При компиляции основной.o, вы сказали ему, что функция «квадрат» существует, но функция «квадрат» не определена внутри main.o. Таким образом, главный.o имеет своего рода» висячую ссылку «на функцию»квадрат». Он не будет компилироваться в полную программу, если вы не объедините ее с другой .o (или a .так или иначе .а) файл, который содержит определение «квадрат». Если вы просто попытаетесь ссылке main.о в программу, т. е.

вы получите ошибку, потому что компилятор попытается resolve болтающаяся ссылка на функцию «квадрат», но не найдет для нее никакого определения. Однако, если вы включаете add.o при связывании (связывание-это процесс разрешение все эти ссылки на неопределенные функции при преобразовании .o файлы в исполняемые файлы или .so files), то не будет никаких проблем. т. е.

вот как функциональные использовать функции в файлах C, но стилистически, то, что я только что показал вам, — это «неправильный путь». Единственная причина, по которой я сделал это, потому что я думаю, что это лучше поможет вам понять, что происходит, а не полагаться на «#include magic». Теперь вы, возможно, заметили, что все становится немного грязным, если вам нужно переопределить каждую функцию, которую вы хотите использовать в верхней части main.c вот почему часто программы C используют вспомогательные файлы, называемые «заголовками», которые имеют a .продление сек. Идея заголовка заключается в том, что он содержит просто объявления функций,без их определения. Таким образом, для компиляции программы используются функции, определенные в add.c, вам не нужно вручную объявлять каждую функцию, которую вы используете, и вам не нужно #включать все добавление.C файл в коде. Вместо этого вы можете #include add.H, который просто содержит декларации всех функций add.c.

теперь, обновление на #include: #include просто копирует содержимое одного файла в другой. Так, например, код

в точности эквивалентно

предполагая, что wtf.txt содержит текст «hello world».

Итак, если мы поместим все объявления add.c in add.h (т. е.

, а затем в верхней части Главного.c, пишем

это функционально то же самое, как если бы мы только что вручную объявили функцию «квадрат» в верхней части главный.c.

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

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

вместо этого люди часто перемещают все эти #includes в main.H файл заголовка и просто #include main.h от main.С. В этом case, файл заголовка служит два целей. Он объявляет функции в main.c для использования при включении в другие файлы,и он включает в себя все зависимости main.c при включении из main.c. Использование его таким образом также позволяет цепи зависимостей. Если вы #include add.h, вы не только получаете функции, определенные в add.c, но вы также неявно получаете любые функции, которые добавляют.c использует и любые функции они использовать, и так на.

кроме того, более тонко, #включая файл заголовка из собственного.C файл неявно проверяет наличие ошибок, которые вы делаете. Если, например, вы случайно определили квадрат как

in add.h, вы обычно не могли бы понять, пока вы не связываете это основное.o ищет один определение квадрата и добавление.o обеспечивает другое, несовместимое один. Это приведет к тому, что вы получите ошибки при связывании, поэтому вы не поймете ошибку пока позже в процессе сборки. Однако, если вы #include add.h от add.c, для компилятора ваш файл выглядит как

который после обработки оператора #include будет выглядеть как

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

почему вы может использовать функцию, не объявляя ее

как вы заметили, в некоторых случаях вы можете использовать функцию без объявления ее или#, включая любой файл, который ее объявляет. Это глупо, и все согласны, что это глупо. Тем не менее, это унаследованная особенность языка программирования C (и компиляторов C), что если вы используете функцию, не объявляя ее сначала, она просто предполагает, что это функция, возвращающая тип «int». Таким образом, использование функции неявно объявляя эту функцию как функцию, которая возвращает «int», если она еще не объявлена. Это очень странное поведение, если вы думаете об этом, и компилятор должен предупредить вас, если вы это делаете.

Заголовок Охранники

еще одна распространенная практика-использование «защитников заголовка». Чтобы объяснить это, давайте рассмотрим возможную проблему. Допустим, у нас есть два файла: herp.c и derp.C, и они и хотите используйте функции, содержащиеся друг в друге. Следуя приведенным выше рекомендациям, у вас может быть герпес.h с линией

и баттхерт.h с линией

теперь, если вы думаете об этом, #включить «derp.h » будет преобразован в содержимое derp.h, который в свою очередь содержит строку #include «herp.h», который будет преобразован в содержимое herp.h, и это содержит. и так далее, поэтому компилятор будет продолжать вечно просто расширять включает. Аналогично, если main.h #включает в себя оба герпеса.h и derp.h, и оба герпеса.h и derp.ч добавить.h, мы видим это в основном.h, мы заканчиваем с два копии add.h, один в результате #включая herp.h, и один в результате включения derp.h. Итак, решение? «Защита заголовка», т. е. фрагмент кода, который предотвращает включение любого заголовка #дважды. Для добавления.h, например, обычный способ сделать это:

этот кусок кода по сути указание препроцессору (части компилятора, которая обрабатывает все операторы «#XXX») проверить, определен ли» ADD_H». Если это не так (еслиndef) затем он сначала определяет » ADD_H «(в этом контексте ADD_H не должен быть определен as ничего, это просто логическое значение, которое либо определено, либо нет), а затем определяет остальную часть содержимого заголовка. Однако, если ADD_H уже определен, то #включая этот файл будет делать ничего, потому что нет ничего за пределами #ifndef блок. Таким образом, идея заключается в том, что только в первый раз, когда он включен в любой данный файл, он фактически добавит любой текст в этот файл. После этого #including не добавит никакого дополнительного текста в ваш файл. ADD_H-это просто произвольный символ, который вы выбираете для отслеживания добавления.h еще не включен. Для каждого заголовка вы используете другой символ, чтобы отслеживать, включен он или нет. Например, герпес.ч вероятно, использовать HERP_H вместо ADD_H. Использование «header guard» исправит любую из проблем, перечисленных выше, где у вас есть дубликаты включенных копий файла или бесконечный цикл #includes.

проблема в том, что вы не должны быть #include ing a .файл c.

чтобы использовать функцию в другом файле, вам нужно ее объявить. Обычно, каждый .C файл (кроме main.c)имеет связанный заголовок (.h) файл, который правильно объявляет все функции, определенные в .файл c. Ты можешь!—11—>объявить столько раз, сколько вы хотите (пока все объявления идентичны), но может быть только один определение.

что происходит, когда вы #include «add.c» это текст добавить.c входит в состав main.c, давая main.с определение (и, как побочный эффект, в объявлении) add . Затем при компиляции add.c самостоятельно, что создает другое определение add . Таким образом, есть два определения функции, и компилятор волнуется, потому что он не знает, какой из них использовать.

если вы измените его на #include «add.h» , где добавить.ч выглядит примерно так:

затем main.c имеет заявление add и может использовать функцию, но определение of add довольно твердо только в add.C файл, и поэтому он существует только один раз, и поэтому он будет компилироваться правильно.

вот простой пример вызова функции из другой программы C

позвольте мне назвать основную программу как основную.c и программа, которая содержит функцию как функцию.c для функции.c я создаю файл заголовка, называемый function.h

Для компиляции используйте приведенную ниже команду

G++ main.функция c.c-o главный

Вот подробное объяснение. В основной программе я вызвал функцию для суммирования 2 чисел. Значения 1 и 2 в основной программе были поданы в функцию в функции.c через функцию заголовка.h, который содержит точку доступа или мост к функции.с

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

добавить инструкцию печати, чтобы проверить результат или использовать echo $? после выполнения файла main

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

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

если вы включаете определения функций, они будут экспортированы, если вы не укажете их хранение с помощью static . Поскольку вы также компилируете и связываете add.c , вы не можете добавить это с тех пор ни один или оба ваших объектных файла затем будут экспортировать add .

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

Как вызвать функции из одного файла .cpp в другом файле .cpp?

Я попытался найти это и получил смешанные результаты, используя заголовочные файлы и тому подобное.

В основном у меня есть несколько файлов cpp со всеми моими функциями, которые я сделал для использования с двоичными деревьями, BST, связанными списками и т. Д.

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

И быть в состоянии звонить и использовать свои собственные функции.

Какие шаги для этого?
Сделать заголовочный файл со всеми прототипами функций, которые я использовал?
И где разместить файл cpp и header со всеми действующими функциями?
Есть ли способ, которым я могу вызвать каталог файла функций напрямую?

То есть Я больше думаю о том, чтобы поместить его в одну папку с основным исходным cpp-файлом, чтобы поделиться с некоторыми из моих коллег.

Как я могу сделать это?

Редактировать: Windows. Компилятор miniGW

Решение

Объявите функции в заголовке

Реализуйте их в MyFunctions.cpp

Включите заголовок в любой файл, который вы хотите

Я не знаю miniGW, но он должен выглядеть примерно так: g ++ otherfile.cpp MyFunctions.cpp …

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

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

Вы обычно указываете компилятору компилировать каждый из ваших файлов cpp. Когда файл cpp имеет #include заявление, что в основном копирует и вставляет include d файл в ваш файл cpp перед компиляцией (это делает препроцессор). Каждый из этих полных модулей (файл cpp, с include), обработанных компилятором, называется блок перевода. Каждая единица перевода производит один объектный файл.

Объектные файлы содержат скомпилированный код, но они часто не завершены. То есть они содержат ссылки на код, который не содержится в них. По сути, они могут сказать: «Теперь вызовите эту функцию, я не знаю, где она и что она делает, но вы должны вызывать ее».

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

Библиотеки бывают двух видов: общий библиотеки ( .dll в Windows) и статический библиотеки. Статическая библиотека связана с исполняемым файлом (или другой библиотекой) с помощью компоновщика, то есть вы можете использовать исполняемый файл без библиотеки (соответствующий код библиотеки становится частью исполняемого файла). Вы также можете связать исполняемый файл / библиотеку с общей библиотекой, и в этом случае вам потребуется копия этой общей библиотеки каждый раз, когда вы запускаете свой исполняемый файл — операционной системе потребуется динамически связывать ваш скомпилированный код с общей библиотекой до того, как запустить его.

Итак, вернемся к вашему вопросу.

У вас есть, в общем, три варианта: скомпилировать и связать все ваши cpp файлы непосредственно каждый раз в одном проекте; скомпилировать полезный повторно используемый код в статическую библиотеку, а затем связать ваш проект с ним; или скомпилируйте полезный повторно используемый код в общую библиотеку, свяжите свой проект с ним и убедитесь, что поставили общую библиотеку вместе с результатом, чтобы ее можно было запустить.

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

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

Это довольно просто, так как JMAA сказал, что вы должны провести некоторое исследование, чтобы понять эти концепции, но, будучи практичным, вы должны сделать следующее:

Вы определите functionsExample.cpp где вы должны определить все свои функции, а также functionsExample.h где вы будете объявлять свои функции.

Вы будете иметь это как functionsExample.cpp:

Ответ это как functionsExample.h:

Затем в CPP вы хотите запустить пример функцию, просто добавьте:

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

Самый простой способ сделать это — использовать инструмент сборки. Мое личное предпочтение — сборка мезонов, но CMake более популярен. Есть и другие, но вы не ошибетесь ни с одним из них. Оба они кроссплатформенные и поддерживают разные наборы инструментов. Это означает, что если ваш код является стандартным C ++, вы можете скомпилировать его так же легко, используя MinGW, Visual Studio, G ++ или Clang, ничего не меняя. Они позволяют вам выбирать из доступных наборов инструментов на вашей платформе. У каждого есть краткое руководство на своем веб-сайте.

Файл конфигурации, который вы пишете, указывает, какие исходные файлы программы использовать и какие исполняемые файлы для сборки. Используемая цепочка сборки выбирается на этапе настройки, и после этого вы можете собрать исполняемые файлы, выполнив make или ninja (требуется meson, рекомендуется для CMake). Если вы измените исходный код, вам нужно только повторно запустить make или ninja. Конечно, только измененные детали перестраиваются. Только исполняемые файлы, которые затронуты вашими изменениями, также повторно связаны.

Может быть полезно посмотреть несколько подробных сборок, чтобы ознакомиться с процессом сборки в вашей системе.

ps Традиционно в #include вы используете угловые скобки (<>) для системных заголовков и кавычек («») для ваших собственных. Это связано с тем, где их искать в первую очередь.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *