简体   繁体   中英

Why is there now a difference between “{static const char a[]={…}” and “{const char a[]={…}”?

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?

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM