Mixing Extern and Const

Mixing extern and const

  • Yes, you can use them together.
  • And yes, it should exactly match the declaration in the translation unit it's actually declared in. Unless of course you are participating in the Underhanded C Programming Contest :-)

The usual pattern is:

  • file.h:
    extern const int a_global_var;
  • file.c:
    #include "file.h"
    const int a_global_var = /* some const expression */;

Edit: Incorporated legends2k's comment. Thanks.

extern const in embedded C programming

Extern const will be loaded from the data section of the compiled binary, which is stored on hard disk, into memory. During execution, it is always in memory. To be precisely, it will be in the data section of the process.

Question about variables with combination of extern and const

if you want to be able to use your constant at compile time (i.e. size an array by it, without using VLA) it has to be known at compile time, i.e. it cannot have external linkage.

However, you could just declare your constant in your header file, and make it available to anyone including it. Still, that won't have the exact same effect as an external linkage.

// a.h
const int MAX = 3;

// a.cpp
#include "a.h"
int b[a];

Mixing declarations with extern, static and no storage specifier in global scope

If you define an identifier without the keyword static, it's published in the object file and can be accessed by other modules. So if you again define that identifier without static in another module, you'll get a conflict: two published identifiers.

If you use the keyword static, then (most of) the rest of this doesn't apply.

The problem is the difference between declaring the identifier and defining it. The first says "there's going to be an identifier X with this type". The second says "here's something that I'm going to call X of this type".

  • With functions it's easy: don't provide the body, and it's merely a declaration. Provide the body, and it's a definition too. You can use extern to make this explicit in a header file but since it's the default, that's not usual.

  • With variables it's harder. Simply declaring the variable defines it too - thus you can initialise it while defining it. If you want to only declare it, you need to use the keyword extern - but then you can't initialise it too. You're saying "there's going to be a variable called X" - so you can't have the temerity of deciding its definition too!

That's why in header files all variables should be explicitly declared either extern or static:

  • The first is usual: there will be a common variable that everyone can access. Don't forget that somewhere, in one module, you need to provide the actual definition, without the extern keyword, with an optional initialisation value.
  • The second is rare: each module that includes the header file will have its own, non-conflicting variable with that particular name. The compiler may be able to not allocate memory for it (especially if it's a constant) - but if it does allocate memory, it'll be different in every module. Why would you do this? Maybe the (forced) inline functions of that header file need each module to have their own copy...

Mixing functions and const/let inside conditional statements cause ReferenceError in Safari

Function declarations inside blocks weren't defined in the specification for many years but they were allowed by different javascript engines.

Since this syntax was not defined in the specification and was allowed by the javascript engines, different engines did different things. Some made it a syntax error, others treated function declarations in block scopes as they were function expressions. Some engines treated functions declarations in a block scope like multiple hoisted declarations in the same scope.

As of ES2015, function declarations are part of the specification and there are two ways they are handled:

  • Standard web semantics
  • Legacy web semantics

Standard Semantics

With standard semantics, function declarations are converted to function expressions, declared with let keyword and are hoisted at the top of the block. Standard semantics are in effect in strict mode.

So in strict mode, your code will be treated by the javascript engine as though it were written like this:

if(true) {
let myFunc = function() {
alert(a);
}

const a = 1;
myFunc();
}

Legacy Web Semantics

In non-strict mode on browsers, legacy web semantics apply. When function declarations in block scope are not treated as syntax errors, there are three scenarios that are handled the same way by all major javascript engines. Those three scenarios are:

  1. Function is declared and referenced within a single block
  2. A function is declared and possibly used within a single Block but also referenced by an inner function definition that is not contained within that same Block.
  3. A function is declared and possibly used within a single block but also referenced within
    subsequent blocks.

In addition to let variable for the function defined in block scope, there's also a variable defined with var in the containing function scope or the global scope. This var assignment isn't hoisted to the top of the block and is done when the function declaration is reached in the code.

Your code in non-strict mode is treated by javascript engine as:

var varMyFunc;

if(true) {
let myFunc = function() {
alert(a);
}

const a = 1;

varMyFunc = myFunc; // at the place of function declaration

myFunc();
}

You shouldn't write code that relies on legacy web semantics. Instead, use strict mode to ensure that your code relies on standard rules for handling function declarations in block scopes. Having said all that, if you have legacy code in non-strict mode that relies on legacy web semantics, you can expect it to work cross-browser.

Will replacing 'extern const' with 'static const' affect performance?

A static const variable will be a compile-time constant unless volatile or initialized by a function.
(with any decent optimizing compiler, anyway)

So if you do go with static const variables, you could get a speed increase and a smaller binary.

An example would be:

extern volatile const int n;

int main(){
volatile int i = n;
}

volatile const int n = 5;

Which has the x86 assembly:

main:
mov eax, DWORD PTR n[rip]
mov DWORD PTR [rsp-4], eax
xor eax, eax
ret
n:
.long 5

we have to use volatile to force the compiler to take the variable's address (that would happen if the variable weren't volatile but were in another translation unit) and not optimize out the int i.

That same example with static const:

static const int n = 5;

int main(){
volatile int i = n;
}

has the x86 assembly:

main:
mov DWORD PTR [rsp-4], 5
xor eax, eax
ret

We don't have to use volatile on the constant because we would be exposing the variable exactly the same way as if we were using a header, but we still need to stop the compiler optimizing out i.

You can see that the static const way has one less instruction and that the extern way has extra data added to the binary for the literal that needs to be stored for reference.

So we get better performance and a smaller binary. Although, I admit, these examples are pretty trivial.

This is actually still not a perfect representation, either. If we had const int n defined in another translation unit, without link-time optimizations, the compiler wouldn't be able to output n: .long 5 and would have to reference another variable. But we'll give the example the benefit of the doubt.

These optimizations are extremely common and you can essentially rely on it being available.

The only thing to watch out for is if you write something like this:

static const int n = some_func();

int main(){
volatile int i = n;
}

the compiler won't be able to substitute n for its literal value. This would add bloat to your binary because you're defining it in a header and it will be re-declared once in every translation unit. So extern would be better for space in that case, maybe not in speed though; you can test that yourself. Just mix and match if you really need to micro-optimize.

[all of the examples were compiled with gcc 4.9.2 from https://gcc.godbolt.org/ and used the flag -O3]

C++ How to share constants with extern between cpp - Error: storage class specified

Firstly, these don't seem to be class members. The way you're using extern it looks like you intended for these to be free. Perhaps in a namespace. Take them out of that class.

Then, when you define them, leave out the extern.

In this context it means "find this variable elsewhere". You don't want to find it elsewhere. It's here!

// Header
namespace Constants {
extern const std::string DELIMITER;
extern const std::string FILENAME;
extern const int BLUCAR;
extern const int REDCAR;
extern const int EMPTY;
extern const int SPARSE_LIM;
}

// Source
namespace Constants {
const std::string DELIMITER = ",";
const std::string FILENAME = "cars.csv";
const int BLUCAR = 1;
const int REDCAR = 2;
const int EMPTY = 0;
const int SPARSE_LIM = 5;
}

Remember, you'd do the same for static object definitions!



Related Topics



Leave a reply



Submit