Static Const VS Extern Const

static const Vs extern const

Your first variant

static NSString * const myString = @"foo"; // In .h file, included by multiple .m files

defines an myString variable locally in each "translation unit" (roughly speaking: in each .m source file)
that includes the header file. All string objects have the same contents "foo",
but it may be different objects so that the value of myString (the pointer to the string object)
may be different in each unit.

Your second variant

extern NSString * const myString; // In .h file, included by multiple .m files
NSString * const myString = @"foo"; // In one .m file only

defines a single variable myString which is visible "globally".

Example: In one class you send a notification with myString as user object.
In another class, this notification is received and the user object compared to myString.

In your first variant, the comparison must be done with isEqualToString: because
the sending and the receiving class may have different pointers (both pointing to a
NSString object with the contents "foo"). Therefore comparing with == may fail.

In your second variant, there is only one myString variable, so you can compare with ==.

So the second variant is safer in the sense that the "shared string" is the same object in each translation unit.

How is static const different from extern const?

The static is unnecessary, as const are implicitly static.

No, that's not true.

static when used at file scope (i.e. outside any method or function) means that the variable is visible only within that file.

extern means that the variable is defined in some other file.

const means that the variable cannot be modified.

Consider strings. Often, you'll have an implementation file (name ends in .m) that defines some constant string pointer:

NSString *const SomeString = @"some string";

You might want to use that same constant from other files. If so, you could add a declaration to the header (name ends in .h) file that explains to the compiler that the variable is defined elsewhere:

extern NSString *const SomeString;

and that would let you use SomeString in any file that imports the header file. On the other hand, you might decide that you definitely do not want the constant used outside the implementation file. In that case, you could declare it static (in the implementation file again):

static NSString *const SomeString = @"some string";

and that would prevent its use from outside the file.

Calling NSString * const x is actually declaring a constant and immutable value in x. I cannot change the value, but may be able to change what x points to.

Right, it declares the pointer x to be constant -- you can't change it. You also can't change the value that it points to if it's actually a NSString because an instance NSString isn't mutable.

This const has internal linkage, meaning the the value is defined right away (presumable at compile time).

I'll take the 5th on that -- I'm not sure exactly how the compiler deals with constant strings. I think it's safe to use that as a mental model, though; the string will in any case be defined before your code ever gets to use it.

Why do I need using extern static to define a constant in header file in Objective-C?

You should not use extern static. You should only use extern.

File.h

extern const int MyGlobalConstant; // NOTE: Not static

File.m

const int MyGlobalConstant = 12345; // NOTE: This is not static either

This creates a memory location in File.m which other files that import File.h can reference.

In contrast,

File.h

static const int MyGlobalConstant = 12345;

This creates a separate and distinct memory location in every .m file which includes File.h.

The difference is important. In the first example, you have 1 MyGlobalConstant. In the second example, you will have tens if not hundreds of separate MyGlobalConstants all with the same value.

It's more than just a waste of space. I can cause problems debugging and problems for a profiler.

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]

Const vs Static Const

It depends on your system, and on how you use the variable. For static variables:

Case 1: You never use the variable, and the compiler silently discards it. This cannot happen with extern variables.

Case 2: You use the variable, but you never take its address. The compiler converts use of the variable to immediate operands, just as if it were a #define or enum. The compiler can still convert extern static to immediate operands, but it must still find an address for it anyway.

Case 3: You use the variable and take its address, the compiler is forced to find a place to put it in the object code, exactly as if it were extern.

As for "data" versus "program" memory, well, that is very specific to the system you are using. On my Linux x64/ELF system, it will probably get put in the .rodata section, which goes in the same segment as code (.text), but a different segment from read-write data sections (.bss, .data). My system appears not to create a separate segment for read-only non-executable data.

Addendum: Note that the behavior is different in C++. In C++, a const variable has internal linkage by default, so static const is redundant and extern const is necessary to get a constant with external linkage.

C++ semantics of `static const` vs `const`

At file scope, no difference in C++. const makes internal linkage the default, and all global variables have static lifetime. But the first variant has the same behavior in C, so that may be a good reason to use it.

Within a function, the second version can be computed from parameters. In C or C++ it doesn't have to be a compile-time constant like some other languages require.

Within a class, basically the same thing as for functions. An instance const value can be computed in the ctor-initializer-list. A static const is set during startup initialization and remains unchanged for the rest of the program. (Note: the code for static members looks a little different because declaration and initialization are separated.)

Remember, in C++, const means read-only, not constant. If you have a pointer-to-const then other parts of the program may change the value while you're not looking. If the variable was defined with const, then no one can change it after initialization but initialization can still be arbitrarily complex.

How do I define static constants using other extern constants in its definition?

In C, variables declared at file scope may only be initialized with a constant expression, which loosely speaking means a compile time constant. A variable declared as const is not considered a constant expression, so it cannot be used to initialize file scope variables.

The way around this is to use #define macros for these values instead. A macro does direct token substitution so that they may be used where a constant expression is required.

So your header file would contain this:

#define G 6.67408E-11
#define metre 10
#define window_width 500
#define window_height 500
#define seconds_per_frame (1.0f/60.0f)
#define frames_per_second 60.0f

And consts.c would not be necessary.

static const vs const declaration performance difference on uC

You have some misconceptions here, since a MCU is not a PC. Everything in memory in a MCU will persist for as long as the MCU has power. Programs do not end or return resources to a hosting OS.

"Tasks" on a MCU means you have a RTOS. They use their own stack and that's a topic of it's own, quite unrelated to your question. It is normal that all tasks on a RTOS execute forever, rather than getting allocated/deallocated in run-time like processes in a PC.

static versus automatic on local scope does mean different RAM memory use, but not necessarily more/less memory use. Local variables get pushed/popped on the stack as the program executes. static ones sit on their designated address.


Where as without static, it will be loaded into memory when accessed.

Only if the array you are loading into is declared locally. That is:

void func (void)
{
int my_local_array[] = {1,2,3};
...
}

Here my_local_array will load the values from flash to RAM during execution of that function only. This means two things:

  • The actual copy down from flash to RAM is slow. First of all, copying something is always slow, regardless of the situation. But in the specific case of copying from RAM to flash, it might be extra slow, depending on MCU.

    It will be extra slow on high end MCUs with flash wait states that fail to utilize data cache for the copy. It will be extra slow on weird Harvard architecture MCUs that can't address data directly. Etc.

    So naturally if you do this copy down each time a function is called, instead of just once, your program will turn much slower.

  • Large local objects lead to a need for higher stack size. The stack must be large enough to deal with the worst-case scenario. If you have large local objects, the stack size will need to be set much higher to prevent stack overflows. Meaning this can actually lead to less effective memory use.

So it isn't trivial to tell if you save or lose memory by making an object local.

General good practice design in embedded systems programming is to not allocate large objects on the stack, as they make stack handling much more detailed and the potential for stack overflow increases. Such objects should be declared as static, at file scope. Particularly if speed is important.


static const float array vs const float array

Another misconception here. Making something const in MCU system, while at the same time placing it at file scope ("global"), most likely means that the variable will end up in flash ROM, not in RAM. Regardless of static.

This is most of the time preferred, since in general RAM is a more valuable resource than flash. The role static plays here is merely good program design, as it limits access to the variable to the local translation unit, rather than cluttering up the global namespace.


In lut.h

You should never define variables in header files.

It is bad from a program design point-of-view, as you expose the variable all over the place ("spaghetti programming") and it is bad from a linker point of view, if multiple source files include the same header file - which is extremely likely.

Correctly designed programs places the variable in the .c file and limits access by declaring it static. Access from the outside, if needed, is done through setters/getters.


he uC has limited spiflash

What is "spiflash"? An external serial flash memory accessed through SPI? Then none of this makes sense, since such flash memory isn't memory-mapped and typically the compiler can't utilize it. Access to such memories has to be carried out by your application, manually.



Related Topics



Leave a reply



Submit