C++ понимает компилятор и компоновщик через #include

Как работает компилятор

оригинал 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();
    }
    
    

Je suppose que tu aimes

Origine blog.csdn.net/qq_31362767/article/details/126971415
conseillé
Classement