Подтвердить что ты не робот

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

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

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

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

main.c
add.c

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

4b9b3361

Ответ 1

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

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

int square(int i) {
    return i*i;
}

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

int square(int i);

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

Теперь, если у вас есть два разных файла, которые вы хотите взаимодействовать (например, скажем, что функция "квадрат" определена в add.c, и вы хотите вызвать квадрат (10) в main.c), вам нужно сделать определение и декларацию. Сначала вы определяете квадрат в add.c. Затем вы объявляете его в начале main.c. Это позволяет компилятору знать, когда он компилирует main.c, что существует функция "квадрат" , которая определена в другом месте. Теперь вам нужно скомпилировать как main.c, так и add.c в объектные файлы. Вы можете сделать это, позвонив

gcc -c main.c
gcc -c add.c

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

gcc -o executable main.o

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

gcc -o executable main.o add.o

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

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

abc
#include "wtf.txt"
def

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

abc
hello world
def

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

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

int square(int i);

а затем в верхней части main.c, напишем

#include "add.h"

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

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

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

#include "add.h"
#include "divide.h"
#include "multiply.h"
#include "eat-pie.h"
...

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

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

double square(int i);

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

#include "add.h"
int square(int i) {
    return i*i;
}

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

double square(int i);
int square(int i) {
    return i*i;
}

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

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

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

Защитники заголовков

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

#include "derp.h"

и derp.h с линией

#include "herp.h"

Теперь, если вы подумаете об этом, #include "derp.h" будет преобразовано в содержимое derp.h, которое, в свою очередь, содержит строку #include "herp.h", которая будет преобразована в содержимое of herp.h, и это содержит... и так далее, поэтому компилятор будет продолжать вечно просто расширять входящие. Аналогично, если main.h # включает в себя как herp.h, так и derp.h, и оба herp.h и derp.h включают add.h, мы видим, что в main.h мы получаем две копии add.h, один в результате #including herp.h, и один в результате включения derp.h. Итак, решение? "Защитник заголовка", т.е. Фрагмент кода, который предотвращает включение второго заголовка в два раза. Например, для add.h обычный способ сделать это:

#ifndef ADD_H
#define ADD_H

int sqrt(int i);
...
#endif

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

Ответ 2

Проблема заключается в том, что вы не должны быть #include с .c файлом.

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

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

Если вы измените его на #include "add.h", где add.h выглядит примерно так:

#ifndef ADD_H
#define ADD_H

extern int add(int x, int y);

#endif /* ADD_H - Google "include guard" for more info about this trickery */

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

Ответ 3

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

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

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

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

Ответ 4

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

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

main.c

#include"function.h"
int main()
{
     int a = sum(1,2);
     return a;
}

function.c

int function(int a,int b)
{
    return a+b;
}

function.h

int function(int,int);

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

  g++ main.c function.c -o main

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

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

http://www.cplusplus.com/forum/beginner/34691/

https://social.msdn.microsoft.com/Forums/en-US/4ea70f43-a0d5-43f8-8e24-78e90f208110/calling-a-function-in-a-file-from-another-file?forum = winembplatdev

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