Я имею два файла C++, говорю file1.cpp и file2.cpp как
//file1.cpp
#include<cstdio>
void fun(int i)
{
printf("%d\n",i);
}
//file2.cpp
void fun(double);
int main()
{
fun(5);
}
Когда я компилирую их и связываю их как файлы C++, я получаю ошибку "неопределенная ссылка на забаву (дважды)".
Но когда я делаю это как C файлы, я не получаю ошибку, и 0 печатается вместо 5.
Объясните причину.
Кроме того, я хочу спросить, должны ли мы объявить функцию прежде, чем определить его потому что
Я не объявил это в file1.cpp, но никакая ошибка не прибывает в компиляцию.
Это, скорее всего, из-за перегрузки функции. При компиляции с помощью C вызов fun (double)
транслируется в вызов функции сборки _fun
, которая будет связана на более позднем этапе. Фактическое определение также имеет имя сборки _fun
, хотя оно принимает int вместо double, и компоновщик с радостью воспользуется этим при вызове fun (double)
.
C ++, с другой стороны, искажает имена сборок, поэтому вы получите что-то вроде _fun @ int
для fun (int)
и _fun @ double
for fun (double)
, чтобы перегрузка работала. Компоновщик увидит, что у них разные имена, и выдаст ошибку, что не может найти определение для fun (double)
.
Что касается вашего второго вопроса, всегда рекомендуется объявлять прототипы функций, обычно это делается в заголовке, особенно если функция используется в нескольких файлах. В вашем компиляторе должна быть опция предупреждения об отсутствии прототипов, gcc использует -Wmissing-prototypes
. Ваш код был бы лучше, если бы он был настроен как
// file1.hpp
#ifndef FILE1_HPP
#define FILE1_HPP
void fun(int)
#endif
// file1.c
#include "file1.hpp"
...
// file2.c
#include "file1.hpp"
int main()
{
fun(5);
}
. Тогда в вашей программе не будет нескольких конфликтующих прототипов.
#include <stdio.h>
int Sum(int j, int f)
{
int k;
k = j + f;
return k;
}
main()
{
int i=3;
int j = 6;
int k = sum(i,j);
printf("%d",k);
}
Вывод: 9
C ++ (садистский зверь, согласитесь) любит искажать имена функций. Таким образом, в вашем заголовочном файле для части C: вверху:
#ifdef __cplusplus
extern "C" {`
#endif
внизу:
#ifdef __cplusplus
}
#endif
Это убедит его не искажать некоторые имена. {{ 1}} Посмотрите здесь
ИЛИ в своем cpp вы можете сказать
extern "C" void fun( double );
Это потому, что C++ позволяет перегружать функции, а C - нет. Это допустимо в C++:
double fun(int i);
double fun(double i);
...
double fun(int i) { return 1;}
double fun(double i) { return 2.1; }
но не в C.
Причина, по которой вы не можете скомпилировать это с помощью компилятора C++, заключается в том, что компилятор C++ видит объявление как double и пытается найти для него определение. В компиляторе Си вы должны получить ошибку и на это, я бы подумал, что вы не ввели код точно так, как вы сказали, когда тестировали это с компилятором Си.
Основной момент: C++ имеет перегрузку функций, C - нет.
Унаследованная от языка C позволяет вызывать функции, не требуя фактического объявления, видимого в переводе - это просто предполагает, что все аргументы таких функций имеют тип int.
В вашем примере C ++ допускает перегрузку и не поддерживает неявные объявления функций - компилятор использует видимую функцию fun (double), а компоновщик терпит неудачу, потому что функция fun (double) никогда не реализуется. fun (int) имеет другую сигнатуру (в C ++) и существует как уникальный символ, тогда как компилятор C (или компоновщик, в зависимости от видимости) выдает ошибку, если вы объявляете fun (int) и fun (double) как Символы C.
Именно так языки развивались с годами (или нет). Вероятно, у вашего компилятора есть предупреждение об этой проблеме (неявные объявления функций).
Вы увидите разные результаты, когда объявите функции как функции C (в вашем примере они объявлены как функции C ++ при компиляции как исходные файлы C ++).
C ++ требует, чтобы функция была объявлена перед ее использованием, C - нет (если вы не укажете компилятору предупредить вас о проблеме).
При компиляции как C ++ вам разрешено иметь две функции с одинаковым именем (при условии, что у них разные параметры). В C ++ используется искажение имен, чтобы компоновщик мог различать их:
fun_int(int x);
fun_double(double x);
При компиляции на C существует только одна функция с определенным именем.
Когда вы компилируете file1.c, он генерирует функцию, которая считывает целое число из стека и печатает его.
Когда вы компилируете file2.c, он видит, что fun () принимает двойное значение. Таким образом, он преобразует входной параметр в двойное нажатие на стек, а затем вставляет вызов fun () в код. Поскольку функция находится в другом модуле компиляции, фактический адрес здесь не разрешается, а разрешается только при вызове компоновщика. Когда компоновщик вызывается, он видит, что вызов fun необходимо разрешить, и вставляет правильный адрес, но у него нет информации о типе, с помощью которой можно было бы проверить вызов.
Во время выполнения 5 теперь преобразуется в double и помещается в стек. Затем вызывается fun (). fun () считывает целое число из стека и затем печатает его. Поскольку double имеет макет, отличный от целого, то, что будет напечатано, будет определяться реализацией и зависит от того, как double и int размещаются в памяти.