Включая файл заголовка C с большим количеством глобальных переменных
У меня есть файл include со 100 + глобальными переменными. Он используется в библиотеке, но некоторые программы, которые я связываю с lib, также нуждаются в доступе к глобалам.
как он был построен:
// In one library .c file
#define Extern
// In the programs that use the globals
#define Extern extern
// In the .h file
Extern int a,b,c;
мне было трудно понять, почему оригинальный программист сделал это, поэтому я удалил это определение Extern. Теперь я думаю, что понимаю вещь о TU с помощью stackoverflow: 1, 2, 3.
теперь я понимаю, что я должен определить глобальные переменные в одном .C файл в библиотеке и использовать extern в .H-файл. Проблема в том, что я не хочу дублировать код.
должен ли я вернуться к этому #define Extern voodoo?
8 ответов
фокус в том, что ... файл h используется двумя разными способами - он используется как обычный .H файл, в котором объявлены все глобалы extern
и он также используется для определение сами глобалы (без extern
). Это уродливый хак, но вы можете понять, почему кто-то посчитал это необходимым, если у вас есть большое количество глобалов (верный признак очень плохого дизайна программного обеспечения !).
в любом случае, есть несколько более элегантное решение - вы можете положить все ваши глобалы в одной глобальной структуре, например
//
// globals.h
//
typedef struct {
int a;
int b;
// ...
int z;
} Globals;
extern Globals globals; // declaration
-
//
// globals.c
//
#include "globals.h"
Globals globals; // definition
-
тогда, когда вам нужно обратиться к глобальному, это, например,globals.a
вместо a
, что может показаться неудобством, но это, возможно, яснее и более управляемо, чем просто наличие голых глобалов, разбросанных по всему коду.
это плохой шаблон, чтобы определить Extern в каждом .файл c. Удаление его, вероятно, лучше всего, но вам нужно как-то заменить эту функциональность. Один из подходов заключается в том, что вы можете использовать #define в .файл C, который должен определить эти глобалы. Это определение будет сигналом .h не исключать глобальные переменные.
например: В одной библиотеке .файл c:
#define FOO_LIBRARY_C
#include "foo_library.h"
другие .с файлы:
#include "foo_library.h"
foo_library.h:
#ifdef FOO_LIBRARY_C
int a,b,c
#else
extern int a,b,c
#endif
или
#ifdef FOO_LIBRARY_C
#define GLOBAL_PREFIX
#else
#define GLOBAL_PREFIX extern
#endif
GLOBAL_PREFIX int a,b,c
это уменьшает необходимость определять одно и то же в каждом исходном файле (кроме одного) и поможет уменьшить ошибки. Я также не назвал бы это Extern, так как это может вызвать путаницу, так как это может быть или не быть "extern"
может быть, я тоже что-то упускаю, но я всегда используйте защиту включения для всех файлов заголовков, которые я создаю:
фу.h:
#ifndef FOO_H
#define FOO_H
extern int foo;
#endif
фу.c:
#include "foo.h"
int foo = 0;
бар.c:
#include "foo.h"
#include <stdio.h>
int main(int argc, char** argv)
{
printf("foo:%d\n",foo);
return 0;
}
материал макроса глуп для этого. Просто положите
extern int myGlobal;
в вашем заголовочном файле и в одном .C файл (обычно с тем же именем, что и у .h файл), put
int myGlobal;
нет необходимости подчеркивать этот уровень "дублирования".
возможно, я что-то упускаю, но я не вижу разницы между #define
thingy и просто с помощью extern
ключевое слово.
в основном, вы должны объявить vars в a .H файл и определите их в a .файл c. Не считайте это дублированием кода - я думаю, что изменение точки зрения-лучшее, что вы можете сделать здесь :). Вы можете написать больше строк кода, но они будут читаемыми: D.
в больших программах очень важно иметь одну строку для объявления и определения глобальной переменной. Поэтому макро-подход, описанный выше, является правильным способом решения этой проблемы.
// globals.h
#ifndef GLOBALS_H
#define GLOBALS_H
#ifndef EXTERN
#define EXTERN extern
#endif
EXTERN int i;
#endif
// globals.c
#define EXTERN
#include "globals.h"
как правило-не используйте глобальные переменные. Иногда вам нужно использовать статические переменные в файле, но лучше всего избегать их вообще:
- вы не можете правильно тестировать код, который содержит глобальные переменные
- нет чистого разделения между модулями, которое может вызвать проблемы с изоляцией ошибок
- обычно не потокобезопасным. Вы должны обернуть их мьютексами и т. д. если вы хотите использовать потоки (как правило, чтобы быть лучше и лучше идея, как мы получаем все больше и больше ядер) вы можете столкнуться с проблемами с записываемым общим состоянием.
иногда в C вы не можете избежать их (особенно в коде, унаследованном кем-то), но лучше всего держать их вне.
что касается объявления-это может быть полезно в таком случае:
// globals.h
#ifndef GLOBALS_H
#define GLOBALS_H
#ifndef EXTERN
#define EXTERN extern
#endif
EXTERN int i;
#endif
// globals.c
#define EXTERN
#include "globals.h"
хотя это может раздражать сначала, это поможет вам избежать необходимости вводить вещь дважды или забывать включать что-то в a .C файл вообще. Я видел:
#define FOO_C_
#include "foo.h"
#include "bar.h"
int foo_doit(int a, int b, int c) {
...
}
С foo.ч быть:
#ifndef FOO_H_
#define FOO_H_
#ifdef FOO_C_
#define GLOBAL
#define DECLARE( type, name, value) type name = value
#else
#define GLOBAL extern
#define DECLARE( type, name, value) extern type name;
#endif
GLOBAL int foo_doit(int a, int b, int c);
GLOBAL int foo_it; // uninitialized global variable
DECLARE(char, that[], "that");
// and sometimes using:
#ifdef FOO_C_
char word[] = letters;
#else
extern char word[];
#endif
#endif // FOO_H_