В настоящее время я просматриваю код, написанный старшим инженером. Код работает нормально, но я пытаюсь выяснить одну деталь.
Он использует довольно много глобальных переменных, и его код разбит на множество отдельных файлов. Поэтому он использует технику, чтобы убедиться, что глобальные переменные объявляются везде, где ему нужно получить к ним доступ, но они определяются только один раз.
Техника является новой для меня, но я прочитал несколько статей в Интернете и получил некоторое представление о том, как она работает. Он использует
#undef EXTERN
с последующим условным определением EXTERN как пустой строки или фактического extern. Есть очень хорошая статья Вот объясняя, как это работает. Также есть обсуждение Вот
Что меня смущает, так это то, что во всех примерах, которые я видел в Интернете, предлагается регулярно включать заголовочный файл во все исходные файлы, которые в нем нуждаются, кроме одного. В этой отдельной строке особого случая, которая включает заголовок, предшествует определение символа, который будет гарантировать, что EXTERN будет определен как пустая строка и т. Д. (См. Ссылку выше). Обычно этот отдельный особый случай находится в основном или в отдельном исходном файле, предназначенном для объявления глобальных переменных.
Однако в коде, который я смотрю на этот особый случай всегда в исходном файле, который соответствует заголовку. Вот минимальный пример:
«peripheral1.h«:
#undef EXTERN
#ifndef PERIPHERAL_1_CPP
#define EXTERN extern
#else
#define EXTERN
#endif
EXTERN void function1(void);
«peripheral1.cpp«:
#define PERIPHERAL_1_CPP
#include "peripheral1.h"
function1()
{
//function code code here
}
В другом месте в коде он просто делает
#include "peripheral1.h"
Мой вопрос как а также Зачем это работает? Другими словами, как компилятор узнает, где определить и где просто объявить функцию (или переменную, или класс …)? И почему в приведенном выше примере нормально иметь строки:
#define PERIPHERAL_1_CPP
#include "peripheral1.h"
в фактическом Периферийном.cpp, а не в main.cpp или где-либо еще?
Или я что-то упускаю здесь очевидное?
Ааа, это возвращает меня на справедливый путь (около 20 лет или около того).
Это способ для кода C определять глобальные переменные в нескольких файлах: вы определяете переменную один раз, используя макрос, чтобы убедиться, что она определена ровно только один раз, а затем извлекаете ее в других файлах кода C, чтобы вы могли использовать ее. В настоящее время он во многих случаях является излишним, однако он все еще занимает свое место в унаследованном коде и, скорее всего, будет работать во многих современных компиляторах, но это код C, а не C ++.
Обычно такие, как #define PERIPHERAL_1_CPP
используется для обеспечения уникальности включения как #pragma once
В моем собственном коде я бы использовал что-то вроде:
#ifndef PERIPHERAL_1_CPP
#define PERIPHERAL_1_CPP
// my includes here
// my code here
#endif
Таким образом, вы можете #include файл столько раз, сколько вы хотите во всем коде, даже в каждом файле кода, и вы избежите многократных ошибок определения. Честно говоря, я обычно делаю это с файлами .h и получаю что-то вроде:
// for absolutely insane safety/paranoia
#pragma once
// normally sufficient
#ifndef PERIPHERAL_1_H
#define PERIPHERAL_1_H
// my includes here
// my code here
#endif
Я никогда не пробовал это на файлах cpp, но сегодня вечером я посмотрю, есть ли какая-то выгода тем или иным способом 🙂
Напишите мне, если вам нужно больше информации 🙂
Все исходные файлы, кроме «perripheral1.cpp», после предварительной обработки содержат последовательность
из объявления внешних переменных лайк:
extern int a;
extern int b;
extern int c;
В peripheral1.cpp
только после предварительной обработки будет последовательность объявлений:
int a;
int b;
int c;
int d;
которые предварительные определения соответствующих переменных, которые в нормальных условиях эквивалентны внешние определения :
int a = 0;
int b = 0;
int c = 0;
int d = 0;
Конечным результатом является то, что переменные объявляются везде, но определяются только один раз.
PS. Чтобы быть совершенно ясно …
Другими словами, как компилятор знает, где определять и где
просто объявить функцию (или переменную, или класс …)?
Компилятор знает, где объявлять всякий раз, когда он встречает грамматическую конструкцию, которая определена в стандарте, чтобы иметь семантику объявления.
Компилятор знает, где определять, когда он встречает грамматическую конструкцию, которая определена в стандарте, чтобы иметь семантику определения.
Другими словами, компилятор не знать — Вы говорите это явно, что вы хотите, чтобы он сделал.