There are ways of coding coding in C which makes those optimizations unnecessary.
The basic idea is: don't load your block of code with lots of pointer dereferences. Load the values you need into local variables. Don't proliferate common expressions which dereference the same pointer to get at the same value. Consolidate the assignments through pointers. Don't do this in three places: (*ptr)++. Have that value in a local variable var, and do var++ in three places, then assign it *ptr = var.
Even without no-strict aliasing, a compiler can still assume that local variables whose addresses are not taken are not the targets of any pointers.
In C (99 or later), you have restrict also. restrict is independent of strict aliasing, because it's not based on type.
Furthermore, speaking of restrict, aliasing between like typed objects matters for optimization. It's good and well to optimize based on the idea that a double * cannot be aiming at an object whose declared type is long. But it's insufficient, because code that is manipulating double * pointers is likely working with objects of type double which could be the targets of those pointers. So without some combination of tight coding and possibly using restrict, you will end up with those load-hit-stores in all sorts of code.
This code for removing from a linked list:
node->prev->next = node->next;
node->next->prev = node->prev;
still isn't as good as this:
node *next = node->next;
node *prev = node->prev;
next->prev = prev;
prev->next = next;
The problem is that the common expressions can't be eliminated based on aliasing, and the aliasing is between like types, so strict aliasing doesn't help.
With gcc (x86) I get:
movl (%eax), %edx
movl 4(%eax), %ecx
movl %ecx, 4(%edx)
movl 4(%eax), %eax
movl %eax, (%eax)
versus:
movl (%eax), %edx
movl 4(%eax), %eax
movl %eax, 4(%edx)
movl %edx, (%eax)
We shaved off an instruction through tighter coding, and IMHO (in this case) improved the readability also.
That was gcc 7 on Ubuntu. The result is exactly the same like what I saw under gcc 2.7.x a quarter century ago.
How about gcc 11, x86-64? I should probably be using godbolt, but anyway, also five instructions down to four:
movq (%rdi), %rax
movq 8(%rdi), %rdx
movq %rdx, 8(%rax)
movq 8(%rdi), %rax
movq %rax, (%rax)
vs:
movq 8(%rdi), %rax
movq (%rdi), %rdx
movq %rax, 8(%rdx)
movq %rdx, (%rax)
(I think in this particular case the compiler could do a better job because even if the assignment "node->prev->next = node->next" clobbers the value of "node->next" due to the nodes being aliases, the assignment can only clobber it with the value that node->next already has! The compiler doesn't analyze it that far though.)
C was designed from the start as a language which the programmer does the optimizing, and regardless of the advancements in compilers, that has not been entirely eliminated.
How you write C still makes a difference, even at the microscopic level of individual statements and expressions, not just the level of overall program organization and use of algorithms.
If you write tight code, you can turn off strict aliasing optimizations globally and it won't matter. But you don't have to do that globally. You may be able to confine your type punning hack in its own source file, and just turn it off for that file. (Or may be even on a finer granularity if you have such compiler support.)