Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confusing results from 'sizeof' operator

I recently tried this code and was a little confused. See the following declarations:

 static   st;
 auto     au;
 register reg;
 volatile vl;
 const    cn;

They all are allocating memory of 4 bytes (on 32 bit GCC). But when i try to print (using printf function) their sizes, they are not working and giving errors.

  sizeof(const)      // worked and printed 4
  sizeof(volatile)   // worked and printed 4

  sizeof(auto)       // error: expected expression before ‘auto’
  sizeof(static)     // error: expected expression before ‘static’
  sizeof(register)   // error: expected expression before ‘register’

My doubt is auto, static, register keywords also allocating memory of 4 bytes(on 32 bit arch).

But why these are giving errors unlike const and volatile?

like image 360
gangadhars Avatar asked Oct 31 '13 15:10

gangadhars


4 Answers

In C prior to the 1999 standard, an unspecified type would default to int in many contexts.

C99 dropped that rule, and omitting the type is now illegal (strictly speaking, it's a constraint violation, requiring a diagnostic -- which could be a non-fatal warning). In any case, omitting the int type has always been a bad idea. (It goes back to C's predecessor languages BCPL and B, which where largely typeless.)

static   st;
auto     au;
register reg;
volatile vl;
const    cn;

These declarations are all legal in C90 (and all the variables are of type int), but they're invalid in C99.

sizeof(const)      
sizeof(volatile)

Somewhat to my surprise, these are actually legal in C90 (but not in C99). const or volatile by itself is a type name, equivalent to const int and volatile int, respectively. Syntactically, const and volatile are type qualifiers.

sizeof(auto)
sizeof(static)
sizeof(register)

The distinction is that this:

const int x = 42;

defines x to be an object of type const int, while this:

static int x = 42;

defines x to be a static object of type int (static isn't part of the type).

These are all syntax errors, because auto, static, and register are not type names. Those keywords are storage-class specifiers.

This explains why the first two sizeof expressions seem to work, and the others do not. But that's not particularly useful to know, because if you specify the type int (which you always should), it doesn't matter that sizeof(const) happens to be valid (in C90, not in C99).

The bottom line is that you should always specify the type in any declaration. And though you can legally write sizeof (const int), it's guaranteed to be the same as sizeof (int), so there's not much point in using const in that context.

like image 118
Keith Thompson Avatar answered Oct 26 '22 18:10

Keith Thompson


Prior to C99 if you did not specify a type then int would be implied which is what is happening in your code. It looks like in practice even in C99 mode gcc and clang will just produce warnings. This is a case where compiler warnings are your friend, I tried this in clang -Wall:

printf( "%zu\n", sizeof(const) ) ;  

and it warns me:

warning: type specifier missing, defaults to 'int' [-Wimplicit-int]

All the declarations here:

static   st;
auto     au;
register reg;
volatile vl;
const    cn;

also have an implied int type.

We can see that C99 removed the implicit int assumption:

a declaration that lacks a type specifier no longer has int implicitly assumed. The C standards committee decided that it was of more value for compilers to diagnose inadvertent omission of the type specifier than to silently process legacy code that relied on implicit int. In practice, compilers are likely to display a warning, then assume int and continue translating the program.

If we look at the draft C99 standard Forward section paragraph 5 includes the following:

[...]Major changes from the previous edition include:

and has the following bullet:

— remove implicit int

Update

So why does sizeof not like storage class specifiers like static and auto but is okay with type qualifiers like const and volatile, the behavior seems inconsistent with how the declarations work and should the implicit int assumption still work?

Well if we look at the grammar for sizeof in the draft standard section 6.5.3 it is as follows:

sizeof unary-expression
sizeof ( type-name )

So neither a type qualifier nor a storage class specifiers is an expression but a type qualifier is a type-name, if we look at section 6.7.6 the grammar for type-name is as follows:

type-name:
  specifier-qualifier-list abstract-declaratoropt

and 6.7.2.1 gives us the grammar for specifier-qualifier-list which is as follows:

specifier-qualifier-list:
  type-specifier specifier-qualifier-listopt
  type-qualifier specifier-qualifier-listopt      <- Bingo allows type qualifier

So we can see that sizeof just does not accept storage class specifiers even if the type is explicitly specified to int, so even the following is an error:

printf( "%zu\n", sizeof(static int) ) ;

and clang tells us:

error: expected expression
   printf( "%zu\n", sizeof(static int) ) ;
                           ^

and we can further see that type names won't work with sizeof without ():

printf( "%zu\n", sizeof  int ) ;

produces an error:

error: expected expression

but unary expressions work with () as I explained previously here.

like image 31
Shafik Yaghmour Avatar answered Oct 26 '22 17:10

Shafik Yaghmour


The auto, static, register keywords don't identify any type, but modify the way a variable of that type is stored or accessed.

So:

sizeof(auto)       // error: expected expression before ‘auto’
sizeof(static)     // error: expected expression before ‘static’
sizeof(register)   // error: expected expression before ‘register’

make no sense, because you're not requesting the size of any type. Instead:

sizeof(const)      // worked and printed 4
sizeof(volatile)   // worked and printed 4

These identify types: volatile int and const int. So you can use sizeof on them.

Notice that when you're declaring your variables, the compiler is assuming int as their underlying type. Most compilers (GCC, Clang) will emit warnings if you're relying on this behaviour.

like image 27
peppe Avatar answered Oct 26 '22 16:10

peppe


extern, static, auto, register are called storage-class-specifier, while const, restrict, volatile are called type-qualifier.

For type-qualifiers, when used without type-specifier, int is implicitly specified in C89.

C89 §3.5.2 Type specifiers

int, signed, signed int, or no type specifiers

These types listed are the same with each other. While no type specifiers has been removed in C99 in the same section:

C99 §6.7.2 Type specifiers

int, signed, or signed int

like image 22
Yu Hao Avatar answered Oct 26 '22 16:10

Yu Hao