Have a look at this tiny snippet of C code or C++ code on godbolt...
void b( char const *c);
void a(void)
{
char const z[] = {0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf, 0xa};
b(z);
}
void c(void)
{
static char const z[] = {0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf, 0xa};
b(z);
}
Earlier versions of gcc compiles both a() and c() to two instructions, load address of z, jump to b.
All modern compilers I tried "pessimise" a() to "make stack frame, copy z onto stack, call b, tear down stack frame, but leave c() as the two instruction simple version.
In effect nothing has changed, in practice modern compilers are now slower for this use case.....
Anybody have any idea why?
The difference is that const char * is a pointer to a const char , while char * const is a constant pointer to a char . The first, the value being pointed to can't be changed but the pointer can be. The second, the value being pointed at can change but the pointer can't (similar to a reference).
C. NOTE: There is no difference between const char *p and char const *p as both are pointer to a const char and position of '*'(asterik) is also same.
If you don't have the choice, using const char* gives a guarantee to the user that you won't change his data especially if it was a string literal where modifying one is undefined behavior. Show activity on this post. By using const you're promising your user that you won't change the string being passed in.
In C programming language, *p represents the value stored in a pointer and p represents the address of the value, is referred as a pointer. const char* and char const* says that the pointer can point to a constant char and value of char pointed by this pointer cannot be changed.
C++ has the following rule:
Unless an object is a bit-field or a subobject of zero size, the address of that object is the address of the first byte it occupies. Two objects with overlapping lifetimes that are not bit-fields may have the same address if one is nested within the other, or if at least one is a subobject of zero size and they are of different types; otherwise, they have distinct addresses and occupy disjoint bytes of storage.
Now, check out this code:
#include <stdio.h> void c(); void b(const char *a) { static const char *p = 0; if (!p) { p = a; c(); } else { if (a==p) { printf("problem!\n"); } } } void c() { const char a[] = { 0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf }; b(a); } int main() { c(); }
Here, c
is called recursively once, so according to the rule, the array a
should have different addresses in each recursion level. b
stores a
at the first invocation, and at the second invocation, it checks whether it is the same or not. With a conforming compiler, it should not print "problem!". But actually, with an old compiler (GCC 4.1, clang 6.0), it prints "problem!", so these compilers violate the standard.
A compiler is allowed to make a
static only in the case that it can be proven that this change is not observable:
Under the “as-if” rule an implementation is allowed to store two objects at the same machine address or not store an object at all if the program cannot observe the difference
I expect the answer to be that the compiler does what you in your code specify should happen - there must be a function-local array of automatic storage that is not shared with other threads, that is to be passed into other functions. Previously the compiler could use the as-if rule to remove that and put it elsewhere as the language didn't have threads as a thing that existed in its model, but since threads are now present it has to ensure it does not accidentally cause false sharing with others. It could've probably made it thread-local, but that's worse than just function local.
Note that GCC never did the optimization, but Clang stopped doing so after 6.0.0. It might even be a Clang bug to have used this optimization.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With