Каталог статей
Как работает компилятор
оригинал Math.cpp
:
int Multiply(int a, int b) {
int result = a * b;
return result;
}
Теперь демо нужно разделить на два файла:
EndBrance.h
}
Math.cpp
int Multiply(int a, int b) {
int result = a * b;
return result;
#include "EndBrance.h"
Скомпилировать Math.cpp
без ошибок.
Роль компилятора: открыть EndBrance.h
файл, скопировать и вставить все содержимое внутри в #include "EndBrance.h"
нужное место.
Узнайте, почему, предварительно обработав выходной файл
Измените конфигурацию свойства проекта (после настройки теста не забудьте отменить настройку):
Перекомпилируйте Math.cpp
, в каталоге Debug будет сгенерирован файл Math.i
, откройте его для просмотра содержимого:
Видно, что содержимое, сгенерированное компилятором, Math.cpp
содержится в исходном коде, и компилятор встраивает #include "EndBrance.h"
его в свой файл в расположение.)
#include "EndBrance.h"
Точно так же измените код и проверьте еще раз
такие как использование#define
#define INTERGER int
INTERGER Multiply(int a, int b) {
INTERGER result = a * b;
return result;
}
После перекомпиляции:
Например, используйте #if
, скомпилируйте и просмотритеMath.i
#if 0
int Multiply(int a, int b) {
int result = a * b;
return result;
}
#endif
Например, используйте его #include <iostream>
, проверьте его после компиляции, в нем более 50 000 строк, и последнее — это код, который я написал сам. Это связано с тем, что iostream
в .
Как работает линкер
LNK1561
Math.cpp
#include <iostream>
void Log(const char* message) {
std::cout << message << std::endl;
}
int Multiply(int a, int b) {
Log("Multiply");
return a * b;
}
Скомпилируйте напрямую, и появится ошибка: LNK1561, точка входа должна быть определена. LNKxxx указывает, что сообщается об ошибке при компоновке, а Cxxx указывает, что сообщается об ошибке при компиляции.
Через свойства проекта вы можете настроить запись. Точка входа не обязательно является основной функцией, обычно это основная функция.
Добавьте основную функцию, и перекомпиляция будет в порядке.
int main() {
std::cout << Multiply(3, 5) << std::endl;
std::cin.get();
}
ЛНК2019
Если вы измените Log.cpp
имя функции Log в файле на Logger, а затем просто скомпилируете (Crtl+F7) Math.cpp
, то никакой ошибки не будет, и ссылка в этом процессе не использовалась.
Затем скомпилируйте весь проект (Ctrl+F5), и появится ошибка LN2019.
1>------ 已启动生成: 项目: demo_include, 配置: Debug Win32 ------
1>Math.obj : error LNK2019: 无法解析的外部符号 "void __cdecl Log(char const *)" (?Log@@YAXPBD@Z),该符号在函数 "int __cdecl Multiply(int,int)" (?Multiply@@YAHHH@Z) 中被引用
1>E:\keeplearning\CppCode\VS2017\demo_include\Debug\demo_include.exe : fatal error LNK1120: 1 个无法解析的外部命令
1>已完成生成项目“demo_include.vcxproj”的操作 - 失败。
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========
Попробуйте следующее:
-
После комментирования
Log("Multiply");
скомпилируйте весь проект и успешно скомпилируйте#include <iostream> void Log(const char *); int Multiply(int a, int b) { // Log("Multiply"); return a * b; } int main() { std::cout << Multiply(3, 5) << std::endl; std::cin.get(); }
-
Закомментировав
main
вызов в функции, скомпилируйте весь проект, и компиляция не удалась#include <iostream> void Log(const char *); int Multiply(int a, int b) { Log("Multiply"); return a * b; } int main() { // std::cout << Multiply(3, 5) << std::endl; std::cin.get(); }
1>------ 已启动生成: 项目: demo_include, 配置: Debug Win32 ------ 1>Math.obj : error LNK2019: 无法解析的外部符号 "void __cdecl Log(char const *)" (?Log@@YAXPBD@Z),该符号在函数 "int __cdecl Multiply(int,int)" (?Multiply@@YAHHH@Z) 中被引用 1>E:\keeplearning\CppCode\VS2017\demo_include\Debug\demo_include.exe : fatal error LNK1120: 1 个无法解析的外部命令 1>已完成生成项目“demo_include.vcxproj”的操作 - 失败。 ========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========
Причина в том, что даже если функция Multiply на самом деле не вызывается в этом файле, она может в определенной степени использоваться в других файлах, поэтому компоновщику необходимо связать.
-
Если вы скажете компоновщику, что функция Multiply используется только в других файлах
Math.cpp
и не будет использоваться в других файлах, вы не сможете связать? Конечно, это тоже возможно, на этот раз поменяйте Multiply на static (имеется в виду, что он используется только в этом файле), перекомпилируйте весь проект, и ошибки линковки не будет.#include <iostream> void Log(const char *); static int Multiply(int a, int b) { Log("Multiply"); return a * b; } int main() { // std::cout << Multiply(3, 5) << std::endl; std::cin.get(); }
Конечно, если на этом основании
// std::cout << Multiply(3, 5) << std::endl;
добавить вступительный комментарий в основную функцию, а потом компилировать проект, тоже будет ошибка.
Помимо изменения Log.cpp
имени функции Log в файле на Logger, если будут внесены другие изменения, будет ошибка ссылки и компиляция завершится ошибкой:
#include <iostream>
int Log(const char* message) {
// 改变返回值类型
std::cout << message << std::endl;
return 0;
}
#include <iostream>
void Log(const char* message, int level) {
// 改变入参个数
std::cout << message << std::endl;
}
ЛНК2005
Проверьте, есть ли функции или переменные с одинаковыми и повторяющимися именами или функциями. Например, две функции с одинаковым именем имеют одинаковое возвращаемое значение и входные параметры. В это время компоновщик не знает, какую из них связать.
-
Сцена 1
Log.cpp
#include <iostream> void Log(const char* message) { std::cout << message << std::endl; }
Math.cpp
#include <iostream> void Log(const char *); void Log(const char* message) { std::cout << message << std::endl; } int Multiply(int a, int b) { Log("Multiply"); return a * b; } int main() { std::cout << Multiply(3, 5) << std::endl; std::cin.get(); }
Программный проект, ошибка:
1>------ 已启动全部重新生成: 项目: demo_include, 配置: Debug Win32 ------ 1>Log.cpp 1>Math.cpp 1>正在生成代码... 1>Math.obj : error LNK2005: "void __cdecl Log(char const *)" (?Log@@YAXPBD@Z) 已经在 Log.obj 中定义 1>E:\keeplearning\CppCode\VS2017\demo_include\Debug\demo_include.exe : fatal error LNK1169: 找到一个或多个多重定义的符号 1>已完成生成项目“demo_include.vcxproj”的操作 - 失败。 ========== 全部重新生成: 成功 0 个,失败 1 个,跳过 0 个 ==========
-
сцена 2
Log.h
#pragma once void Log(const char* message) { std::cout << message << std::endl; }
Log.cpp
#include <iostream> #include "Log.h" void InitLog() { Log("Initialized Log"); }
Math.cpp
#include <iostream> #include "Log.h" static int Multiply(int a, int b) { Log("Multiply"); return a * b; } int main() { std::cout << Multiply(3, 5) << std::endl; std::cin.get(); }
Скомпилируйте проект и сообщите об ошибке
1>------ 已启动生成: 项目: demo_include, 配置: Debug Win32 ------ 1>Log.cpp 1>Math.obj : error LNK2005: "void __cdecl Log(char const *)" (?Log@@YAXPBD@Z) 已经在 Log.obj 中定义 1>E:\keeplearning\CppCode\VS2017\demo_include\Debug\demo_include.exe : fatal error LNK1169: 找到一个或多个多重定义的符号 1>已完成生成项目“demo_include.vcxproj”的操作 - 失败。 ========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========
#include
Как работает инструкция: при включении заголовочного файла мы просто помещаем содержимое заголовочного файла туда, где существует инструкция.Таким образом, запись в
Log.cpp
и приведет к повторным определениям функции журнала, что приведет к той же проблеме, что и в сценарии 1.Math.cpp
#include "Log.h"
Решение этой проблемы:
-
метод 1
Измените
Log.h
метод на статический, другие файлы останутся без изменений, и проект компиляции пройдет#pragma once static void Log(const char* message) { std::cout << message << std::endl; }
-
Способ 2
Замените
Log.h
встроенный метод другими файлами без изменений#pragma once inline void Log(const char* message) { std::cout << message << std::endl; }
-
Способ 3
Log.h
#pragma once void Log(const char* message);
Log.cpp
#include <iostream> #include "Log.h" void Log(const char* message) { std::cout << message << std::endl; } void InitLog() { Log("Initialized Log"); }
Math.cpp
#include <iostream> #include "Log.h" int Multiply(int a, int b) { Log("Multiply"); return a * b; } int main() { std::cout << Multiply(3, 5) << std::endl; std::cin.get(); }