Immutable structs in C
c tricks
Immutability is a popular term to qualify an object as totally constant right after its creation. Simple immutable variables aren't so interesting (excluding may be the immutable strings) but structs and objects are. In some languages all the fields of an object are immutable by default (f.ex. Rust), in other we can declare them as immutable. C#:
class ImmutableType {
public readonly double x;
public Immutable(double _x) { x = _x; }
}
Note the constructor Immutable
— it is the single place where we can change immutable fields in C#.
But in C we also can do something like this using good old const
modifier:
typedef struct {
int f;
const int cf;
} s_t;
int main() {
s_t s = {1,2};
s.f = 3;
s.cf = 4;
return 0;
}
This, as expected, generates an error on s.cf = 4
assignment.
You can even write something like this
int x = 1, y = rand();
s_t s = {x,y};
actually initializing a constant with random value. But not
s = {x,y};
and even not
int x = rand();
int y = rand();
s_t t = {x,y};
s = t;
because it isn’t an initialization! (You of course still can do assignments of pointers to structs.)
But f.ex. in construction function you can use the cast
*(int*)&s.cf = 1;
C is still C, so there is no such a thing you are completely forbidden to do.
Footnote
They say, that immutability makes code faster for many reasons.
The simple reason for this is that the compiler can exploit the knowledge of constant nature of immutable objects. The more complex reason is that the immutability allows us to use non-standard (for C) data structures and algorithms (f.ex. persistent data types) and thus to decrease side effects of our code.
As GC, that in some sense simulates a computer with infinite memory, total immutability simulates a computer with readonly memory, allowing a program to make many assumptions about data.
It's all interesting, but let's compile this:
int n = 0;
s_t s = {1,2};
for (int i = 0; i < 256; i++) {
n += s.f;
n += s.cf;
}
return n;
$ cc --std=c99 -O2 -S -masm=intel const.c -o const.asm
The result will be just
main:
mov eax, 768
ret
Ok] Let's do something not so predicable:
int n = 0;
s_t s = {rand(),rand()};
for (int i = 0; i < rand(); i++) {
n += s.f;
n += s.cf;
}
return n;
Listing:
call rand
mov r12d, eax
call rand
add r12d, eax
jmp .L2
.L3:
add ebp, r12d ; accumulation
add ebx, 1
.L2:
call rand
cmp ebx, eax
jl .L3
mov eax, ebp
Note that compiler treats normal f
and constant field cf
both invariant to the loop. So C compilers are smart enough (there is even special volatile
keyword to make them stupid enough]).
As we see at least in simple cases there're no advantages besides readability and better correctness and possibilities for other high-level things for immutable structs in C. Though may be in more complex cases it can help the compiler?
Let’s make our structure be modifiable on nonpredicable moments by another thread in the program:
pthread_t tid;
s_t s = {1,2};
void* thread(void *arg) {
while(1) {
s.f = rand();
*(int*)&s.cf = rand(); // dirty hack
}
return NULL;
}
int main() {
pthread_create(&tid, NULL, &thread, NULL);
int n = 0;
for (int i = 0; i < rand(); i++) {
n += s.f;
n += s.cf;
}
return n;
}
The main loop compiles to
.L8:
add ebx, DWORD PTR s[rip]
add ebp, 1
add ebx, DWORD PTR s[rip+4] ; reloading of "constant"!
.L7:
call rand
cmp ebp, eax
jl .L8
This means that the compiler is ready to all the dirty hacks we can do in C and not trusts us and our const
modifiers]
See also:
- More about
const
andvolatile
here - Why String is immutable in Java?
shitpoet@gmail.com