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

от admin

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

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

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

  • decl.h:

  • main.cpp:

  • implementation.cpp:

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

  • main.cpp:

  • implementation.cpp:

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

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

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

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

6.7 – Внешнее связывание

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

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

По умолчанию функции имеют внешнее связывание

В уроке «2.7 – Программы с несколькими файлами исходного кода» вы узнали, что вы можете вызывать функцию, определенную в одном файле, из другого файла. Это потому, что функции по умолчанию имеют внешнее связывание.

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

Приведенная выше программа напечатает:

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

Если бы функция sayHi() имела вместо этого внутреннее связывание, компоновщик не смог бы связать вызов функции с определением функции, что привело бы к ошибке линковки.

Глобальные переменные с внешним связыванием

Глобальные переменные с внешним связыванием иногда называют внешними переменными. Чтобы сделать глобальную переменную внешней (и, следовательно, доступной для других файлов), мы можем использовать для этого ключевое слово extern :

Неконстантные глобальные переменные по умолчанию являются внешними (если используется ключевое слово extern , оно игнорируется).

Предварительные объявления переменных через ключевое слово extern

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

Ниже показан пример использования предварительного объявления переменной:

В приведенном выше примере a.cpp и main.cpp ссылаются на одну и ту же глобальную переменную с именем g_x . Таким образом, даже если g_x определена и инициализирована в a.cpp , мы можем использовать ее значение в main.cpp через предварительное объявление g_x .

Обратите внимание, что ключевое слово extern в разных контекстах имеет разное значение. В некоторых контекстах extern означает «дать этой переменной внешнее связывание». В других контекстах extern означает «это предварительное объявление для внешней переменной, которая определена где-то еще». Да, это сбивает с толку, поэтому мы суммируем все эти использования в уроке «6.11 – Резюме по области видимости, продолжительности и связывании».

Предупреждение

Если вы хотите определить неинициализированную неконстантную глобальную переменную, не используйте ключевое слово extern , иначе C++ подумает, что вы пытаетесь выполнить предварительное объявление переменной.

Предупреждение

Хотя переменные constexpr могут получить внешнее связывание с помощью ключевого слова extern , они не могут быть предварительно объявлены, и поэтому нет смысла давать им внешнее связывание.

Обратите внимание, что для предварительных объявлений функций ключевое слово extern не требуется – компилятор может определить, определяете ли вы новую функцию или делаете предварительное объявление, в зависимости от того, указываете ли вы тело функции или нет. Для предварительных объявлений переменных ключевое слово extern необходимо, чтобы отличать определения переменных от предварительных объявлений переменных (так как в остальном они выглядят одинаково):

Область видимости файла и глобальная область видимости

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

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

Переменная g_x имеет область видимости файла в global.cpp – ее можно использовать от точки определения до конца файла, но ее нельзя непосредственно увидеть за пределами global.cpp .

Внутри main.cpp предварительное объявление g_x также имеет область видимости файла – ее можно использовать от точки объявления до конца файла.

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

Проблема порядка инициализации глобальных переменных

Инициализация глобальных переменных происходит при запуске программы перед выполнением функции main . Это происходит в два этапа.

Первый этап называется статической инициализацией. На этапе статической инициализации глобальные переменные с инициализаторами constexpr (включая литералы) инициализируются своими значениями. Кроме того, глобальные переменные без инициализаторов инициализируются нулем.

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

Вот пример инициализатора, не являющегося constexpr :

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

Этот код напечатает:

Гораздо более серьезной проблемой является то, что не определен порядок инициализации для нескольких файлов. При наличии двух файлов, a.cpp и b.cpp , любой из них может первым инициализировать свои глобальные переменные. Это означает, что если переменные в a.cpp зависят от значений в b.cpp , существует 50%-ная вероятность того, что эти переменные будут еще не инициализированы.

Предупреждение

Динамическая инициализация глобальных переменных вызывает в C++ множество проблем. По возможности избегайте ее.

Краткое резюме

Небольшой тест

Вопрос 1

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

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

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

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

Глобальные переменные могут получить внутреннее или внешнее связывание с помощью ключевых слов static и extern соответственно.

Как вызвать функции из одного файла .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 вы используете угловые скобки (<>) для системных заголовков и кавычек («») для ваших собственных. Это связано с тем, где их искать в первую очередь.

Проект

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

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

Сборка проекта

Пусть наш проект состоит из файлов a.c и b.c. Мы хотим собрать его в hello.exe.

Для сборки проекта с помощью gcc из командной строки нужно:

Или, если нас устраивает имя исполняемого модуля по умолчанию (a.out), то собираем проект и запускаем программу командами:

Функция из другого файла

Пусть в файле а.с определена функция void foo(int x). В файле а.с, если хотим вызвать foo(7) до реализации функции, нужно написать прототип функции.

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

Соберем и запустим программу. Получим в консоли:

static — функция только для этого файла

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

Зачем делать функции static

Пусть в программе есть два несвязанных куска — работа с сетью и рисование графического интерфейса. Они написаны в двух разных файлах: net.c и gui.c. Над программой работают 2 разных программиста. Один пишет работу с сетью, второй программирует графический интерфейс.

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

Если они сделают две функции void dump(), то при сборке в общую программу возникнет ошибка — линковщик не будет знать при вызове функции dump() какую из этих двух функций вызвать. Заметим, что функция нужна только в файле net.c или в файле gui.c.

Если обе функции будут описаны static void dump(), то при вызове из того же файла, линковщик будет знать, какую именно функцию dump вызывать (определенную в этом же файле!).

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

Переменная из другого файла

В файле a.c определена глобальная переменная int x.

Как обратиться к ней в файле b.c?

Нужно добавить «прототип» переменной. Т.е. написать объявление переменной с ключевым словом extern.

В чем отличие int x и extern int x?

int x; — это объявление переменной. Создается переменная х типа int. Выделяется память под эту переменную. В нашем случае int x = 7; сразу происходит явная инициализация.

extern int x; — НЕ создает переменной. Это обещание компилятору, что переменная х объявлена в каком-то файле. Если такая переменная нигде не будет создана, то во время связывания, возникнет ошибка.

И extern, и объявление

В файле можно и определить переменную как extern, и объявить ее.

static переменная

Переменная со словом static, объявленная вне всяких блоков и функций — НЕ глобальная. Она видна только в данном файле.

Нельзя написать extern static int x;

Где определять макросы?

Если нужно использовать макрос в обоих файлах, то нужно определить его во всех файлах, где собираетесь использовать

Структуры и typedef

В b.c недостаточно декларировать будущее определение структуры struct _D. Ее нужно полностью определять, как и typedef:

Итого что где определяем

  • Функция — нужно написать прототип функции, можно написать прототип в том же файле, где реализуем функцию.
  • Переменная — объявляем в одном файле, во всех других пишем extern, можно написать extern до объявления переменной в том же файле.
  • Макросы — дублируем везде, где используем.
  • определение структуры, typedef — дублируем везде, где используем.

Заголовочные файлы

Как удобно дублировать макросы и структуры, описывать прототипы функции и внешние переменные?

Используйте для этого заголовочный файл a.h (h — от слова header).

Заголовочный файл вставляется с помощью команды препроцессора #include

Условная компиляция

В a.h мы тоже можем написать директивы #include. Например, #include <stdio.h>

Вдруг мы напишем b.h, который включает a.h, который включает b.h, который включает a.h. Как разорвать круг рекурсивных вставок?

Или файлы b.h и c.h оба содержат a.h и включаются в файл main.c. Если в файле a.h определена структура или макрос, то будет ошибка о двойном определении.

Нам поможет условная компиляция. Напишите файл a.h в таком виде:

При первом включении a.h макрос _A_H не определен, значит код между #ifndef. #endif будет включен. При выполнении этого кода определяется _A_H.

При последующем включении a.h #ifndef _A_H будет ложно (мы уже определили _A_H при первом включении) и код от #ifndef до #endif будет выброшен.

Похожие статьи