When / how to use extern modifier

By #including header files, with #include guards in them, in any .cpp file which wants to see a constant or variable declared / defined in the .h file, would I have ever have to use "extern"?

Is it not recommended to simply #include relevant .h files in any .cpp file which needs declarations / definitions from the .h file? That is to say, should I only #include a .h file in its associated .cpp file, and then use the "extern" modifier in other files which need visibility to a given constant or variable declared / defined in another file, be it an .h or a .cpp file?

By #including header files, with #include guards in them, in any .cpp file which wants to see a constant or variable declared / defined in the .h file, would I have ever have to use "extern"?

The include guards prevent a given include file from being included more than once in a compilation unit. They do not prevent an include file from being used in two or more compilation units.

If the include file results in the creation of a variable in each compilation unit, you will have duplicate definitions.

The duplicate definitions are not resolved by using extern, though.

Is it not recommended to simply #include relevant .h files in any .cpp file which needs declarations / definitions from the .h file?

It is.

That is to say, should I only #include a .h file in its associated .cpp file,

Not true. You need to include the header file in any file that needs the definitions in the header file.

and then use the "extern" modifier in other files which need visibility to a given constant or variable declared / defined in another file, be it an .h or a .cpp file?

There is no blanket, one-size-fits-all answer to that question.

If you have a specific example we could talk about, that would be better than general hand-waving.

Keep in mind that the terms declare and define are not the same. Suppose you have a source code file and you have the statement:

int myGlobal;

in that source file with global scope. Now, in a different file you want to use myGlobal from the first file. The problem is that you have defined myGlobal in the first file, but the second file knows nothing about it. That is, storage is allocated in the first file for myGlobal, but the second file doesn't know its address and, hence, cannot access it. By using the statement:

extern int myGlobal;

in the second file, you are declaring it for use in that file. A definition of a variable means you construct an attribute list for it (e.g., its type and name) plus you allocate storage for it in memory. In the second file, you only need the attribute list so the compiler can manipulate myGlobal in the file. It's the linker's responsibility to provide myGlobal's memory address (i.e., lvalue). It is the allocation of storage that separates a definition from a declaration. If you were to leave out the extern keyword in the second file, the statement becomes a definition for myGlobal and the compiler would try to allocate storage for it. You would now get a duplicate definition error.

1 Like

PaulS & econjack:

Thanks for your replies. The information is helpful.

...that would be better than general hand-waving.

The problem I have is I don't know what I don't know. I had a project wherein I was getting duplicate definitions error. I had thought that the #include guards would only allow a .h file to be added to the project once. When you (PaulS) mentioned the "duplicate definition error" that got my attention.

As a way for me to learn more about this let me describe the situation I had.

I had a .h file which defined two const strings - an SSID and passcode - which were to be used in two different .cpp files in the project. In a .h file I had,

const char[] SSID = "\"mySSID\"";

Not germane to my central concern here, but, to avoid confusion, the included quotes as part of the SSID string are necessary for where it is going to be used.

I had that code in a .h file, with #include guards, and #included it in two different .cpp files in the project. I kept getting the duplicate definition error, which I thought the #include guards were there to prevent. That is, I thought the #include guards were there to prevent the .h file from being read more than once. Then I read,

The include guards prevent a given include file from being included more than once in a compilation unit.

What is meant by a "compilation unit"? Does it mean a single combination of a .cpp file and any #included .h files?

If I understand what else has been said, I could solve my problem by having that sample code in a .h file. That .h file, when #included in one .cpp file will both declare and define the string SSID with global scope. For use in the second .cpp file I would put in its .h file, or directly in the .cpp file,

extern const char[] SSID;

Will that work? Does the const modifier change anything?

Thanks for taking the time.

What is meant by a "compilation unit"? Does it mean a single combination of a .cpp file and any #included .h files?

Yes. Keep in mind that the ino file is converted to a cpp file, so that is one compilation unit. Your header file and source file are another compilation unit. That's two...

More libraries == more compilation units.

That .h file, when #included in one .cpp file will both declare and define the string SSID with global scope. For use in the second .cpp file I would put in its .h file, or directly in the .cpp file,

Yes, but...

If you want to use that library in other places, or share it with other people, they would need to edit the .h file to put their SSID value in. Not a good idea. The library (or libraries) should all use extern, and expect the sketch to define/declare SSID.

PaulS,

Thanks. I have it now.

Also, I understand the part about not requiring others using the .h file (= library, right?) to edit it with their to their own SSID. You make a good point about my example scenario.