Я не могу разобраться с заголовками и охранниками заголовков. Я прочитал другие вопросы и ответы на них, но я все еще не могу сделать эту работу в Visual Studio 2013:
main.cpp
#include "stdafx.h"#include <iostream>
#include "add.h"
int _tmain(int argc, _TCHAR* argv[]) {
std::cout << "3 + 4 = " << add(3, 4) << std::endl;
system("pause");
return 0;
}
add.cpp
#include "stdafx.h" //ADDED LATER; NOW WORKING (AND SEE QUESTION 2 BELOW)
#include "add.h" //ADDED LATER; NOR WORKING (AND SEE QUESTION 2 BELOW)
int add(int x, int y) {
return x + y;
}
add.h
#ifndef ADD_H
#define ADD_H
int add(int x, int y);
#endif
Когда я компилирую, окно консоли мигает на экране, а затем исчезает. Список ошибок содержит:
Ошибка 1 ошибка LNK2019: неразрешенный внешний символ «int __cdecl
add (int, int) «(? add @@ YAHHH @ Z), на который есть ссылка в функции
_wmain c: \ Users \ Danny \ Documents \ visual studio 2013 \ Проекты \ Программа дополнения \ main \ main.obj mainОшибка 2: ошибка LNK1120: 1 не устранена
externals c: \ users \ danny \ Documents \ visual studio
2013 \ Проекты \ Дополнительная программа \ Debug \ main.exe main
add.h
, это делает main.cpp
знать о декларации add(int x, int y)
, но тогда как оно находит свое определение?Мой код сейчас компилируется. Причина, по которой мой код не компилировался, заключалась в том, что я собирался Файл> Новый> Файл … чтобы добавить файлы в мой проект, в отличие от добавления их через Исходные файлы а также Заголовочные файлы разделы Обозреватель решений в Visual Studio. Мне также нужно было добавить #include "stdafx.h
в файл add.cpp.
Думайте об этом так: каждый .cpp
Файл предварительно обрабатывается, а затем компилируется полностью отдельно от других файлов.
Итак, давайте сначала препроцесс main.cpp
, Это предполагает просмотр всех строк, начинающихся с #
, Файл main.cpp
только имеет #include
строки, которые просто копируют содержимое файла, который они включают. Я собираюсь представлять содержимое stdafx.h
а также iostream
с комментарием, но я на самом деле скопировать содержимое add.h
в:
// Contents of stdafx.h
// Contents of iostream
#ifndef ADD_H
#define ADD_H
int add(int x, int y);
#endif
int _tmain(int argc, _TCHAR* argv[]) {
std::cout << "3 + 4 = " << add(3, 4) << std::endl;
system("pause");
return 0;
}
Теперь посмотрите, что содержимое add.h
были введены в main.cpp
? И так получилось, что это ввело еще несколько директив препроцессора, поэтому нам нужно будет делать то, что они говорят. Первый проверяет, если ADD_H
еще не определено (в этом файле), что не является, и поэтому оставляет все до #endif
:
// Contents of stdafx.h
// Contents of iostream
#define ADD_H
int add(int x, int y);
int _tmain(int argc, _TCHAR* argv[]) {
std::cout << "3 + 4 = " << add(3, 4) << std::endl;
system("pause");
return 0;
}
Теперь оставшаяся директива препроцессора определяет ADD_H
и мы остались с финалом блок перевода:
// Contents of stdafx.h
// Contents of iostream
int add(int x, int y);
int _tmain(int argc, _TCHAR* argv[]) {
std::cout << "3 + 4 = " << add(3, 4) << std::endl;
system("pause");
return 0;
}
Теперь этот файл можно скомпилировать. Если вы вызываете функцию, такую как add
компилятору нужно только видеть объявление этой функции для успешной компиляции. Ожидается, что функция будет определена в некоторых Другой переводческий блок.
Итак, теперь давайте посмотрим на предварительную обработку add.cpp
, По факту, add.cpp
не имеет никаких директив предварительной обработки, поэтому ничего не должно произойти. Как правило, вы бы #include "add.h"
, но ваша программа все равно будет компилироваться, если вы этого не сделаете. Итак, после предварительной обработки у нас все еще есть:
int add(int x, int y) {
return x + y;
}
Затем это компилируется, и теперь у нас есть определение add
функция.
В конце концов .cpp
файлы были скомпилированы, затем они связанный. Линкер отвечает за то, чтобы скомпилированный main.cpp
использует функцию add
и так ищет свое определение. Находит определение в скомпилированном add.cpp
и связывает их вместе.
Тогда вы можете удивиться, почему у нас вообще есть охрана. В этом примере это казалось бесполезным. Правильно, в этом примере это не имело никакого смысла. Предусмотрено включение защиты, чтобы предотвратить включение одного и того же заголовка дважды в один файл. Это может легко произойти, если у вас есть более сложная структура проекта. Однако давайте посмотрим на нереальный пример, где main.cpp
включает в себя add.h
дважды:
#include "stdafx.h"#include <iostream>
#include "add.h"#include "add.h"
int _tmain(int argc, _TCHAR* argv[]) {
std::cout << "3 + 4 = " << add(3, 4) << std::endl;
system("pause");
return 0;
}
Предварительная обработка дает вам:
// Contents of stdafx.h
// Contents of iostream
#ifndef ADD_H
#define ADD_H
int add(int x, int y);
#endif
#ifndef ADD_H
#define ADD_H
int add(int x, int y);
#endif
int _tmain(int argc, _TCHAR* argv[]) {
std::cout << "3 + 4 = " << add(3, 4) << std::endl;
system("pause");
return 0;
}
Первый #ifndef
будет процесс, он увидит, что ADD_H
еще не определено, и все до #endif
останется. Это тогда определяет ADD_H
,
Потом второй #ifndef
обрабатывается, но на этом этапе ADD_H
был определен, так что все до #endif
будут отброшены
Это очень важно, потому что наличие нескольких определений функции (и многих других) приведет к ошибке.
Среда IDE компилирует каждый файл .cpp по очереди, чтобы создать объектный файл (машинный код) для этого конкретного файла.
После всего этого он записывает биты, чтобы сформировать исполняемый файл. IDE знает, что должно быть связано с чем.
Это немного упрощенный ответ