Как использовать extern для обмена переменными между исходными файлами?

public class TestClass{    
      public static void main(String args[] ){       
          int i = 0 ;
          int[] iA = {10, 20} ;
          iA[i] = i = 30 ;
          System.out.println(""+ iA[ 0 ] + " " + iA[ 1 ] + "  "+i) ;     
 } }

Он будет печатать 30 20 30

Утверждение iA [i] = i = 30; будет обрабатываться следующим образом:

iA [i] = i = 30; => iA [0] = i = 30; => i = 30; iA [0] = i; => iA [0] = 30;

Вот что говорит JLS об этом:

1 Оцените левую операнду Сначала 2 Оцените операнды до операции 3 Оценка Уважение скобок и приоритет 4 Списки аргументов оцениваются слева направо

Для массивов: во-первых, выражения измерения оцениваются слева направо. Если какая-либо из оценок выражений завершается внезапно, выражения, находящиеся справа от нее, не оцениваются.

911
задан Lundin 22 May 2017 в 07:01
поделиться

6 ответов

Adding an extern turns a variable definition into a variable declaration. See this thread as to what's the difference between a declaration and a definition.

15
ответ дан 19 December 2019 в 20:21
поделиться

extern сообщает компилятору доверять вам, что память для этой переменной объявлена ​​в другом месте, поэтому он не пытается выделить / проверить память.

Следовательно, вы можете скомпилировать файл, имеющий ссылку на extern, но вы не можете связать, если эта память где-то не объявлена.

Полезно для глобальных переменных и библиотек, но опасно, потому что компоновщик не проверяет тип.

18
ответ дан 19 December 2019 в 20:21
поделиться

Мне нравится думать о переменной extern как об обещании, которое вы даете компилятору.

При обнаружении extern компилятор может узнать только его тип, а не то, где он «живет» ", поэтому он не может разрешить ссылку.

Вы говорите ему:« Поверьте мне. Во время ссылки эта ссылка будет разрешена ».

26
ответ дан 19 December 2019 в 20:21
поделиться

Extern - это ключевое слово, которое вы используете, чтобы объявить, что сама переменная находится в другой единице перевода.

Таким образом, вы можете решить использовать переменную в единицу перевода, а затем получить к ней доступ из другой единицы,

40
ответ дан 19 December 2019 в 20:21
поделиться

Переменная extern - это объявление (благодаря sbi за исправление) переменной, которая определена в другой единице перевода. Это означает, что хранилище для переменной выделено в другом файле.

Допустим, у вас есть два .c -файла test1.c и test2.c . Если вы определяете глобальную переменную int test1_var; в test1.c и хотите получить доступ к этой переменной в test2.c , вы должны использовать ] extern int test1_var; в test2.c .

Полный пример:

$ cat test1.c 
int test1_var = 5;
$ cat test2.c
#include <stdio.h>

extern int test1_var;

int main(void) {
    printf("test1_var = %d\n", test1_var);
    return 0;
}
$ gcc test1.c test2.c -o test
$ ./test
test1_var = 5
124
ответ дан 19 December 2019 в 20:21
поделиться

Using extern is only of relevance when the program you're building consists of multiple source files linked together, where some of the variables defined, for example, in source file file1.c need to be упоминается в других исходных файлах, таких как file2.c .

Важно понимать разницу между определением a переменная и объявление a переменная :

  • Переменная объявляется , когда компилятору сообщается, что переменная существует (и это ее тип); он не выделяет хранилище для переменной в этот момент.
  • Переменная определена , когда компилятор выделяет хранилище для переменная.

Вы можете объявить переменную несколько раз (хотя одного раза достаточно); вы можете определить его только один раз в пределах заданной области. Определение переменной также является объявлением, но не все переменные объявления - это определения.

Лучший способ объявить и определить глобальные переменные

Чистый и надежный способ объявления и определения глобальных переменных - это использовать файл заголовка, содержащий объявление extern переменной.

Заголовок включен в один исходный файл, который определяет переменную и всеми исходными файлами, которые ссылаются на переменную. Для каждой программы один исходный файл (и только один исходный файл) определяет переменная. Точно так же один файл заголовка (и только один файл заголовка) должен объявлять переменная. Заголовочный файл имеет решающее значение; это позволяет перекрестную проверку между независимые ЕП (единицы перевода - думаю, исходные файлы) и обеспечивает последовательность.

Хотя есть и другие способы добиться этого, этот метод прост и надежный. Это демонстрируется file3.h , file1.c и file2.c :

file3.h

extern int global_variable;  /* Declaration of the variable */

file1.c

#include "file3.h"  /* Declaration made available here */
#include "prog1.h"  /* Function declarations */

/* Variable defined here */
int global_variable = 37;    /* Definition checked against declaration */

int increment(void) { return global_variable++; }

file2 .c

#include "file3.h"
#include "prog1.h"
#include <stdio.h>

void use_it(void)
{
    printf("Global variable: %d\n", global_variable++);
}

Это лучший способ объявить и определить глобальные переменные.


Следующие два файла дополняют исходный код для prog1 :

Полные показанные программы используют функции, поэтому в объявлениях функций есть прокрался. И C99, и C11 требуют, чтобы функции были объявлены или определены до того, как они используются (тогда как C90 не использовал по уважительным причинам). Я использую ключевое слово extern перед объявлениями функций в заголовках. для согласованности - чтобы соответствовать extern перед переменной объявления в заголовках. Многие люди предпочитают не использовать extern перед функцией. декларации; компилятору все равно - и, в конечном счете, я тоже пока вы последовательны, по крайней мере, в исходном файле.

prog1.h

extern void use_it(void);
extern int increment(void);

prog1.c

#include "file3.h"
#include "prog1.h"
#include <stdio.h>

int main(void)
{
    use_it();
    global_variable += 19;
    use_it();
    printf("Increment: %d\n", increment());
    return 0;
}
  • prog1 использует prog1.c , file1. c , file2.c , file3.h и prog1.h .

Файл prog1.mk make-файл только для prog1 . Он будет работать с большинством версий make , выпущенных примерно с этого же дня. тысячелетия. Он не привязан конкретно к GNU Make.

prog1.mk

# Minimal makefile for prog1

PROGRAM = prog1
FILES.c = prog1.c file1.c file2.c
FILES.h = prog1.h file3.h
FILES.o = ${FILES.c:.c=.o}

CC      = gcc
SFLAGS  = -std=c11
GFLAGS  = -g
OFLAGS  = -O3
WFLAG1  = -Wall
WFLAG2  = -Wextra
WFLAG3  = -Werror
WFLAG4  = -Wstrict-prototypes
WFLAG5  = -Wmissing-prototypes
WFLAGS  = ${WFLAG1} ${WFLAG2} ${WFLAG3} ${WFLAG4} ${WFLAG5}
UFLAGS  = # Set on command line only

CFLAGS  = ${SFLAGS} ${GFLAGS} ${OFLAGS} ${WFLAGS} ${UFLAGS}
LDFLAGS =
LDLIBS  =

all:    ${PROGRAM}

${PROGRAM}: ${FILES.o}
    ${CC} -o $@ ${CFLAGS} ${FILES.o} ${LDFLAGS} ${LDLIBS}

prog1.o: ${FILES.h}
file1.o: ${FILES.h}
file2.o: ${FILES.h}

# If it exists, prog1.dSYM is a directory on macOS
DEBRIS = a.out core *~ *.dSYM
RM_FR  = rm -fr

clean:
    ${RM_FR} ${FILES.o} ${PROGRAM} ${DEBRIS}

Рекомендации

Правила должны нарушаться только экспертами и только по уважительной причине:

  • Заголовочный файл содержит только extern ] объявления переменных - никогда статические или неквалифицированные определения переменных.
  • Для любой заданной переменной ее объявляет только один файл заголовка (SPOT - Single Point of Truth).
  • Исходный файл никогда не содержит extern объявлений переменных - исходные файлы всегда включают (единственный) заголовок, который их объявляет.
  • Для любой заданной переменной только один исходный файл определяет переменную, желательно также инициализировать его. (Хотя в этом нет необходимости явно инициализировать нулем, это не причинит вреда и может принести пользу, потому что может быть только одно инициализированное определение конкретного глобальная переменная в программе).
  • Исходный файл, который определяет переменную, также включает заголовок для убедитесь, что определение и объявление согласованы.
  • Функция никогда не должна объявлять переменную с использованием extern .
  • По возможности избегайте глобальных переменных - используйте вместо них функции.

Исходный код и текст этого ответа доступны в моем SOQ (Stack Overflow Questions) repository on GitHub in the src/so-0143-3204 sub-directory.

If you're not an experienced C programmer, you could (and perhaps should) stop reading here.

Not so good way to define global variables

With some (indeed, many) C compilers, you can get away with what's called a 'common' definition of a variable too. 'Common', here, refers to a technique used in Fortran for sharing variables between source files, using a (possibly named) COMMON block. What happens here is that each of a number of files provides a tentative definition of the variable. As long as no more than one file provides an initialized definition, then the various files end up sharing a common single definition of the переменная:

file10.c

#include "prog2.h"

int i;   /* Do not do this in portable code */

void inc(void) { i++; }

file11.c

#include "prog2.h"

int i;   /* Do not do this in portable code */

void dec(void) { i--; }

file12.c

#include "prog2.h"
#include <stdio.h>

int i = 9;   /* Do not do this in portable code */

void put(void) { printf("i = %d\n", i); }

Этот метод не соответствует букве стандарта C и 'одно правило определения' - это официально неопределенное поведение:

J.2 Неопределенное поведение

Используется идентификатор с внешней связью, но в программе есть не существует ровно одно внешнее определение для идентификатора, или идентификатор не используется и существует несколько внешних определения идентификатора (6.9).

§6.9 Внешние определения ¶5

Внешнее определение - это внешнее объявление, которое также является определение функции (кроме встроенного определения) или объект. Если идентификатор, объявленный с внешней связью, используется в выражение (кроме как часть операнда sizeof или Оператор _Alignof , результатом которого является целочисленная константа), где-то в всю программу должно быть ровно одно внешнее определение для идентификатор; в противном случае не должно быть более один. 161)

161) Таким образом, если идентификатор, объявленный с внешней связью не используется в выражении, не требуется внешнего определения для

Однако стандарт C также перечисляет его в информативном Приложении J как один из Общие расширения .

J.5.11 Множественные внешние определения

Может быть более одного внешнего определения для идентификатора объект с явным использованием ключевого слова extern или без него; если определения не совпадают, или инициализировано более одного, поведение не определено (6.9.2).

Поскольку этот метод не всегда поддерживается, лучше избегать using it, especially if your code needs to be portable. Using this technique, you can also end up with unintentional type punning. If one of the files declared i as a double instead of as an int, C's type-unsafe linkers probably would not spot the mismatch. If you're on a machine with 64-bit int and double, you'd not even get a warning; on a machine with 32-bit int and 64-bit double, you'd probably get a warning about the different sizes — the linker would use the largest size, exactly as a Fortran program would take the наибольший размер среди общих блоков.


Следующие два файла завершают исходный код для prog2 :

prog2.h

extern void dec(void);
extern void put(void);
extern void inc(void);

prog2.c

#include "prog2.h"
#include <stdio.h>

int main(void)
{
    inc();
    put();
    dec();
    put();
    dec();
    put();
}
  • prog2 использует prog2 .c , file10.c , file11.c , file12.c , prog2.h .

Предупреждение

Как указано в комментариях здесь и как указано в моем ответе на аналогичный вопрос с использованием нескольких определения глобальной переменной приводят к неопределенному поведению (J.2; §6.9), что является стандартным способом сказать «все может случиться». Одна из вещей, которые могут случиться, - это то, что программа ведет себя так, как вы ожидать; а в J.5.11 примерно сказано: «вам может везти чаще чем вы заслуживаете ". Но программа, которая полагается на несколько определений внешней переменной - с явным ключевым словом extern или без него - не является строго соответствующая программа и не гарантированно работает везде. Эквивалентно: он содержит ошибку, которая может проявляться, а может и не проявляться.

Нарушение правил

Конечно, есть много способов, которыми эти правила могут быть нарушены. Иногда может быть веская причина нарушить правила, но такие случаи крайне необычны.

faulty_header.h

int some_var;    /* Do not do this in a header!!! */

Примечание 1: если заголовок определяет переменную без ключевого слова extern , затем каждый файл, который включает заголовок, создает предварительное определение переменной. Как отмечалось ранее, это часто работает, но стандарт C не работает. гарантировать, что он будет работать.

broken_header.h

int some_var = 13;    /* Only one source file in a program can use this */

Примечание 2: если заголовок определяет и инициализирует переменную, то только один исходный файл в данной программе может использовать заголовок. Поскольку заголовки предназначены в первую очередь для обмена информацией, это немного глупо. чтобы создать тот, который можно использовать только один раз.

seldom_correct.h

static int hidden_global = 3;   /* Each source file gets its own copy  */

Примечание 3: если заголовок определяет статическую переменную (с или без инициализация), то каждый исходный файл получает свой собственный версия "глобальной" переменной.

Если переменная на самом деле представляет собой сложный массив, например, это может привести к к крайнему дублированию кода. Иногда это может быть разумный способ добиться некоторого эффекта, но это очень необычно.


Резюме

Используйте метод заголовка, который я показал первым. Работает надежно и везде. Обратите внимание, в частности, что заголовок, объявляющий global_variable , является включены в каждый файл, который его использует, включая тот, который его определяет. Это гарантирует, что все самосогласованно.

Аналогичные проблемы возникают при объявлении и определении функций - применяются аналогичные правила. Но вопрос был конкретно о переменных, поэтому я оставил ответ только на переменные.

Конец исходного ответа

Если вы не опытный программист на C, вам, вероятно, следует прекратить читать здесь.


Позднее основное добавление

Как избежать дублирования кода

Одна проблема, которая иногда (и законно) поднимается о описан механизм 'объявления в заголовках, определения в источнике' здесь есть два файла, которые нужно синхронизировать - заголовок и источник. Обычно за этим следует наблюдение, что макрос может использоваться, чтобы заголовок выполнял двойную функцию - обычно объявление переменных, но когда конкретный макрос установлен перед заголовок включен, вместо этого он определяет переменные.

Другая проблема может заключаться в том, что переменные должны быть определены в каждом из ряд «основных программ». Обычно это ложное беспокойство; ты можно просто ввести исходный файл C для определения переменных и ссылки объектный файл, созданный каждой из программ.

Типичная схема работает следующим образом, с использованием исходной глобальной переменной illustrated in file3.h:

file3a.h

#ifdef DEFINE_VARIABLES
#define EXTERN /* nothing */
#else
#define EXTERN extern
#endif /* DEFINE_VARIABLES */

EXTERN int global_variable;

file1a.c

#define DEFINE_VARIABLES
#include "file3a.h"  /* Variable defined - but not initialized */
#include "prog3.h"

int increment(void) { return global_variable++; }

file2a.c

#include "file3a.h"
#include "prog3.h"
#include <stdio.h>

void use_it(void)
{
    printf("Global variable: %d\n", global_variable++);
}

The next two files complete the source for prog3:

prog3.h

extern void use_it(void);
extern int increment(void);

prog3.c

#include "file3a.h"
#include "prog3.h"
#include <stdio.h>

int main(void)
{
    use_it();
    global_variable += 19;
    use_it();
    printf("Increment: %d\n", increment());
    return 0;
}
  • prog3 uses prog3.c, file1a.c, file2a.c, file3a.h, prog3.h.

Variable initialization

The problem with this scheme as shown is that it does not provide for initialization of the global variable. With C99 or C11 and variable argument lists for macros, you could define a macro to support initialization too. (With C89 and no support for variable argument lists in macros, there is no easy way to handle arbitrarily long initializers.)

file3b.h

#ifdef DEFINE_VARIABLES
#define EXTERN                  /* nothing */
#define INITIALIZER(...)        = __VA_ARGS__
#else
#define EXTERN                  extern
#define INITIALIZER(...)        /* nothing */
#endif /* DEFINE_VARIABLES */

EXTERN int global_variable INITIALIZER(37);
EXTERN struct { int a; int b; } oddball_struct INITIALIZER({ 41, 43 });

Reverse contents of #if and #else blocks, fixing bug identified by Denis Kniazhev

file1b.c

#define DEFINE_VARIABLES
#include "file3b.h"  /* Variables now defined and initialized */
#include "prog4.h"

int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }

file2b.c

#include "file3b.h"
#include "prog4.h"
#include <stdio.h>

void use_them(void)
{
    printf("Global variable: %d\n", global_variable++);
    oddball_struct.a += global_variable;
    oddball_struct.b -= global_variable / 2;
}

Clearly, the code for the oddball structure is not what you'd normally write, but it illustrates the point. The first argument to the second invocation of INITIALIZER is { 41 and the remaining argument (singular in this example) is 43 }. Without C99 or similar support for variable argument lists for macros, initializers that need to contain commas are very problematic.

Correct header file3b.h included (instead of fileba.h) per Денис Княжев


Следующие два файла дополняют исходный код для prog4 :

prog4.h

extern int increment(void);
extern int oddball_value(void);
extern void use_them(void);

prog4.c

#include "file3b.h"
#include "prog4.h"
#include <stdio.h>

int main(void)
{
    use_them();
    global_variable += 19;
    use_them();
    printf("Increment: %d\n", increment());
    printf("Oddball:   %d\n", oddball_value());
    return 0;
}
  • prog4 использует prog4.c , file1b.c , file2b.c , prog4.h , file3b.h .

Защита заголовков

Любой заголовок должен быть защищен от повторного включения, поэтому введите определения (типы перечислений, структур или объединений или определения типов в целом) не вызвать проблемы. Стандартная техника - обертывание тела заголовок в защите заголовка, например:

#ifndef FILE3B_H_INCLUDED
#define FILE3B_H_INCLUDED

...contents of header...

#endif /* FILE3B_H_INCLUDED */

Заголовок может быть включен дважды косвенно. Например, если file4b.h включает file3b.h для определения типа, которое не показано, и file1b.c должен использовать оба заголовка file4b.h и file3b.h , тогда you have some more tricky issues to resolve. Clearly, you might revise the header list to include just file4b.h. However, you might not be aware of the internal dependencies — and the code should, ideally, continue to work.

Further, it starts to get tricky because you might include file4b.h before including file3b.h to generate the definitions, but the normal header guards on file3b.h would prevent the header being reincluded.

So, you need to include the body of file3b.h at most once for declarations, and at most once for definitions, but you might need both in a single translation unit (TU — a combination of a source file and the headers it uses).

Multiple inclusion with variable definitions

However, it can be done subject to a not too unreasonable constraint. Let's introduce a new set of file names:

  • external.h for the EXTERN macro definitions, etc.
  • file1c.h to define types (notably, struct oddball, the type of oddball_struct).
  • file2c.h to define or declare the global variables.
  • file3c.c which defines the global variables.
  • file4c.c which simply uses the global variables.
  • file5c.c which shows that you can declare and then define the global variables.
  • file6c.c which shows that you can define and then (attempt to) declare the global variables.

In these examples, file5c.c and file6c.c directly include the header file2c.h several times, but that is the simplest way to show that the механизм работает. Это означает, что если заголовок был включен косвенно дважды, это также будет безопасно.

Ограничения для этого:

  1. Заголовок, определяющий или объявляющий глобальные переменные, не может сам по себе определять любые типы.
  2. Непосредственно перед включением заголовка, который должен определять переменные, c , file4c.c , file1c.h , file2c.h , external.h .

  3. Эта схема позволяет избежать большинства проблем. Вы столкнетесь с проблемой, только если заголовок, определяющий переменные (например, file2c.h ), включается другой заголовок (скажем, file7c.h ), который определяет переменные. Нет простой способ обойти это, кроме «не делай этого».

    Вы можете частично обойти проблему, изменив file2c.h в file2d.h :

    file2d.h

    /* Standard prologue */
    #if defined(DEFINE_VARIABLES) && !defined(FILE2D_H_DEFINITIONS)
    #undef FILE2D_H_INCLUDED
    #endif
    
    #ifndef FILE2D_H_INCLUDED
    #define FILE2D_H_INCLUDED
    
    #include "external.h"   /* Support macros EXTERN, INITIALIZE */
    #include "file1c.h"     /* Type definition for struct oddball */
    
    #if !defined(DEFINE_VARIABLES) || !defined(FILE2D_H_DEFINITIONS)
    
    /* Global variable declarations / definitions */
    EXTERN int global_variable INITIALIZE(37);
    EXTERN struct oddball oddball_struct INITIALIZE({ 41, 43 });
    
    #endif /* !DEFINE_VARIABLES || !FILE2D_H_DEFINITIONS */
    
    /* Standard epilogue */
    #ifdef DEFINE_VARIABLES
    #define FILE2D_H_DEFINITIONS
    #undef DEFINE_VARIABLES
    #endif /* DEFINE_VARIABLES */
    
    #endif /* FILE2D_H_INCLUDED */
    

    Проблема заключается в следующем: «Должен ли заголовок включать #undef DEFINE_VARIABLES ?» Если вы опустите это из заголовка и оберните любой определяющий вызов с помощью #define и #undef :

    #define DEFINE_VARIABLES
    #include "file2c.h"
    #undef DEFINE_VARIABLES
    

    в исходном коде (поэтому заголовки никогда не изменяют значение DEFINE_VARIABLES ), тогда вы должны быть чисты. Это просто неприятность have to remember to write the the extra line. An alternative might be:

    #define HEADER_DEFINING_VARIABLES "file2c.h"
    #include "externdef.h"
    

    externdef.h

    /*
    ** This header must not contain header guards (like <assert.h> must not).
    ** Each time it is included, the macro HEADER_DEFINING_VARIABLES should
    ** be defined with the name (in quotes - or possibly angle brackets) of
    ** the header to be included that defines variables when the macro
    ** DEFINE_VARIABLES is defined.  See also: external.h (which uses
    ** DEFINE_VARIABLES and defines macros EXTERN and INITIALIZE
    ** appropriately).
    **
    ** #define HEADER_DEFINING_VARIABLES "file2c.h"
    ** #include "externdef.h"
    */
    
    #if defined(HEADER_DEFINING_VARIABLES)
    #define DEFINE_VARIABLES
    #include HEADER_DEFINING_VARIABLES
    #undef DEFINE_VARIABLES
    #undef HEADER_DEFINING_VARIABLES
    #endif /* HEADER_DEFINING_VARIABLES */
    

    This is getting a tad convoluted, but seems to be secure (using the file2d.h, with no #undef DEFINE_VARIABLES in the file2d.h).

    file7c.c

    /* Declare variables */
    #include "file2d.h"
    
    /* Define variables */
    #define HEADER_DEFINING_VARIABLES "file2d.h"
    #include "externdef.h"
    
    /* Declare variables - again */
    #include "file2d.h"
    
    /* Define variables - again */
    #define HEADER_DEFINING_VARIABLES "file2d.h"
    #include "externdef.h"
    
    int increment(void) { return global_variable++; }
    int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }
    

    file8c.h

    /* Standard prologue */
    #if defined(DEFINE_VARIABLES) && !defined(FILE8C_H_DEFINITIONS)
    #undef FILE8C_H_INCLUDED
    #endif
    
    #ifndef FILE8C_H_INCLUDED
    #define FILE8C_H_INCLUDED
    
    #include "external.h"   /* Support macros EXTERN, INITIALIZE */
    #include "file2d.h"     /* struct oddball */
    
    #if !defined(DEFINE_VARIABLES) || !defined(FILE8C_H_DEFINITIONS)
    
    /* Global variable declarations / definitions */
    EXTERN struct oddball another INITIALIZE({ 14, 34 });
    
    #endif /* !DEFINE_VARIABLES || !FILE8C_H_DEFINITIONS */
    
    /* Standard epilogue */
    #ifdef DEFINE_VARIABLES
    #define FILE8C_H_DEFINITIONS
    #endif /* DEFINE_VARIABLES */
    
    #endif /* FILE8C_H_INCLUDED */
    

    file8c.c

    /* Define variables */
    #define HEADER_DEFINING_VARIABLES "file2d.h"
    #include "externdef.h"
    
    /* Define variables */
    #define HEADER_DEFINING_VARIABLES "file8c.h"
    #include "externdef.h"
    
    int increment(void) { return global_variable++; }
    int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }
    

    The next two files complete the source for prog8 and prog9:

    prog8.c

    #include "file2d.h"
    #include <stdio.h>
    
    int main(void)
    {
        use_them();
        global_variable += 19;
        use_them();
        printf("Increment: %d\n", increment());
        printf("Oddball:   %d\n", oddball_value());
        return 0;
    }
    

    file9c.c

    #include "file2d.h"
    #include <stdio.h>
    
    void use_them(void)
    {
        printf("Global variable: %d\n", global_variable++);
        oddball_struct.a += global_variable;
        oddball_struct.b -= global_variable / 2;
    }
    
    • prog8 uses prog8.c, file7c.c, file9c.c.
    • prog9 uses prog8.c, file8c.c, file9c.c.

    However, the problems are relatively unlikely to occur in practice, especially if you take the standard advice to

    Avoid global variables


    Does this exposition miss anything?

    Confession: The 'avoiding duplicated code' scheme outlined here was developed because the issue affects some code I work on (but don't own), and is a niggling concern with the scheme outlined in the first part of the answer. However, the original scheme leaves you with just two places to modify to keep variable definitions and declarations synchronized, which is a big step forward over having exernal variable declarations scattered throughout the code base (which really matters when there are thousands of files in total). However, the code in the files with the names fileNc.[ch] (plus external.h and externdef.h) shows that it can be made to work. Clearly, it would not be hard to create a header generator script to give you the standardized template for a variable defining and declaring header file.

    NB These are toy programs with just barely enough code to make them marginally interesting. There is repetition within the examples that could be removed, but isn't to simplify the pedagogical explanation. (For example: the difference between prog5.c and prog8.c is the name of one of the headers that are included. It would be possible to reorganize the code so that the main() function was not repeated, but it would conceal more than it revealed.)

1686
ответ дан 19 December 2019 в 20:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: