Does every variable have a storage class in C?
Asked Answered
P

3

6

Naively one could think it has, because usually auto is assumed when a storage class keyword is not supplied.

Still, for file-scoped variables putting an auto in front of them gives an error.

#include <stdio.h>

auto int x;

int main(void){
  x = 7;
  printf("x = %d", x);
}

Clang complains:

3:10: error: illegal storage class on file-scoped variable
auto int x;

Declaring x without any storage class keyword and it compiles:

#include <stdio.h>

int x;

int main(void){
  x = 7;
  printf("x = %d", x);
}

Now I wonder what storage class x has in the example above? Does it have a name?

Plowman answered 12/2, 2019 at 20:28 Comment(3)
It's static as any global (note that the storage class is static, but the static keyword has some additional meanings.)Aerator
@EugeneSh. so outside of a function body static doesn't have anything to do with the storage class static … confusing… why is the storage class then called that way and not something like “permanent”?Plowman
Using auto is only valid inside a function; ditto with register. You can't use auto with a variable defined outside a function, any more than you can use register with such a variable.Advocate
O
10

The keywords auto, static, extern, register, and _Thread_local are referred to in the standard as storage class specifiers, but "objects" (this is the standardese term for what we usually call "variables") don't have storage classes. Instead they have linkage (external, internal, none) and storage duration (static, automatic, thread). In addition, any declaration of an object may or may not be a definition. The storage class specifiers, together with the scope at which the object is declared, and whether or not it has an initializer (int foo vs int foo = 3), control these properties. It's easiest to show how this works with a table:

sc-specifier scope initialized   linkage    storage duration    is a definition
------------ ----- -----------   -------    ----------------    ---------------
auto         file  no            [constraint violation]
auto         file  yes           [constraint violation]
auto         block no            none       automatic           yes
auto         block yes           none       automatic           yes

none         file  no            external   static              yes
none         file  yes           external   static              yes
none         block no            none       automatic           yes
none         block yes           none       automatic           yes

static       file  no            internal   static              yes
static       file  yes           internal   static              yes
static       block no            none       static              yes
static       block yes           none       static              yes

extern       file  no            external   static              no
extern       file  yes           external   static              yes
extern       block no            external   static              no
extern       block yes           external   static              yes

The term "storage class specifier" is intentionally different from the terms "storage duration" and "linkage" to remind you that the specifiers don't give you independent control over the storage duration and linkage.

The language doesn't give you independent control over storage duration, linkage, and being a definition because the combinations that are unavailable don't make sense. Automatic storage duration only makes sense for variables declared at block scope, not being a definition only makes sense for variables with external linkage (because only they could be defined in another file), and so on.

I left register and _Thread_local out of the table because they are special. register is just like auto except that it also means you're not allowed to take the address of the object. _Thread_local makes the storage duration of the variable be "thread" and doesn't change the linkage; it can be used by itself or with extern or static, but it's a constraint violation to combine it with "auto". I'm not sure what it does if you use it at block scope by itself.

Ogg answered 12/2, 2019 at 20:57 Comment(8)
One wonders how hard it would be to get that table included in the next revision of the C standard itself....Nagano
So register no longer exists? It isn't a big problem; it behaves the same as auto (not allowed at file scope, has no linkage, has automatic duration, is a definition).Advocate
@JonathanLeffler it behaves the same as auto Off the top of my head, I don't think you can take the address of an object defined with the register keyword, so it's not quite the same. Of course, that makes any removal even more significant.Nagano
@AndrewHenle: valid difference, but not representable in the table above.Advocate
Aheh. I totally forgot about register.Ogg
@Ogg Just like most compiler authors ;)Shirline
In the line none file no external static yes – is it really a definition when using GCC’s or Clang’s -fcommon-Option (which is true by default)? Why doesn't it give a multiple definition error then?Plowman
@wolf-revo-cats It's still a definition, it's just a (generalization of) a "tentative definition" when -fcommon is in use. If it were not a definition, you would get a link error if you only had common symbols for some variable, like you do when you only have extern declarations for some variable.Ogg
S
5

From the C Standard § 6.2.4 paragraph 3:

An object whose identifier is declared without the storage-class specifier _Thread_local, and either with external or internal linkage or with the storage-class specifier static, has static storage duration. Its lifetime is the entire execution of the program and its stored value is initialized only once, prior to program startup.

Emphasis mine. Back reference § 6.2.2 paragraph 5:

If the declaration of an identifier for a function has no storage-class specifier, its linkage is determined exactly as if it were declared with the storage-class specifier extern. If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external.

Emphasis mine again.

So, global variables have static storage duration by default. Even without the standard to guarantee that, it's the only type of storage duration that makes sense for global variables.

Shirline answered 12/2, 2019 at 20:34 Comment(0)
M
3

Does every variable have a storage class in C?

Yes, though the standard actually uses the term "storage duration" for this. It's the same thing, and also the standard somewhat inconsistently uses the term "storage class specifier" for the keywords auto, static, etc..

Naively one could think it has, because usually auto is assumed when a storage class keyword is not supplied.

No. Absolutely not. The default is extern for functions and for anything declared at file scope. Only identifiers for objects declared at block scope default to auto.

Still, for file-scoped variables putting an auto in front of them gives an error.

As it should. The standard explicitly specifies that

The storage-class specifiers auto and register shall not appear in the declaration specifiers in an external declaration.

[C11, paragraph 6.9/2]

Declaring x without any storage class keyword and it compiles [....]

Of course.

Now I wonder what storage class x has in the example above? Does it have a name?

Its storage class is that corresponding to the keyword extern. As I already said, this is the default for file-scope declarations. But although the standard uses the terminology "storage class specifier", again, it doesn't use the "storage class" as a standalone concept. It speaks instead of storage duration. All variables with external or internal linkage have static storage duration, meaning they exist for the entire life of the program.

Macassar answered 12/2, 2019 at 20:42 Comment(7)
Is it really extern? … if I have another file and define xthere, too it will give an error if compiled with the flag -fno-common. It only works if it's defined once with extern, the other time without it.Plowman
@wolf-revo-cats In the terms of the standard, int x; at file scope defines an object with static storage duration and external linkage. extern int x; declares but does not define an object with static storage duration and external linkage.Ogg
Yes, @wolf-revo-cats, though I have simplified a bit. An object declared at file scope without any storage-class specifier has the same storage duration and linkage as one declared anywhere with the storage-class specifier extern. But explicitly declaring an object extern also has some significance with respect to whether that declaration also serves as a definition of the object. And there must be exactly one definition for any external identifier that is in fact accessed, and never more than one.Macassar
@JohnBollinger sorry if I sound pedantic, but if you put an extern in front of the file-scoped variable wherever it occurs, you'll get an error, too. And even if there isn't an explicit assignment somewhere. You need it once without an externPlowman
Or with an initializer, @wolf-revo-cats. And that in no way contradicts anything I already said.Macassar
@wolf-revo-cats For example extern int foo = 42; will define foo as wellShirline
@GovindParmar yes, that works. Gives a warning, but indeed it works.Plowman

© 2022 - 2024 — McMap. All rights reserved.