18

I know the title seems quite stupid, but I think it's worth asking.

Take this declaration(or definition, maybe) for example:

_Thread_local long volatile static int _Atomic const long unsigned x = 10;

I used to consider long long as a type, but if it's a type name, how can so many qualifiers be inserted into it?

So I consulted N1570 with this question, only to be more confused. It mentions some terms such as "type-specifier" and "type-qualifier", and I can't find long long in "type specifiers", but isn't long long a primitive type in C? There are so many books telling me so!


Clarifying not duplicate:

Yes, I saw an existing question deals with long int long, but this question have something to do with qualifiers, and is in C.

Community
  • 1
  • 1
nalzok
  • 14,965
  • 21
  • 72
  • 139
  • You can't have `const` and `volatile` at the same time; it's a contradiction in terms. – Kevin Feb 15 '16 at 03:32
  • 3
    That is a pretty amazing variable declaration. Can someone explain how this is parsed? Is the order of qualifiers important? Can you say `long int long unsigned ` instead of `unsigned long long int`? – Thilo Feb 15 '16 at 03:32
  • Where did you find this? – Thilo Feb 15 '16 at 03:33
  • @Kevin Hmm...Maybe, but when compiling, clang doesn't give me any warning about this. – nalzok Feb 15 '16 at 03:34
  • 8
    @Kevin That's not true. It's legal for a variable to be both `const` and `volatile`, read [C FAQ](http://c-faq.com/~scs/cclass/int/sx4ga.html). Specifically, `volatile` is NOT the opposite of `const`. – Yu Hao Feb 15 '16 at 03:35
  • 1
    @YuHao: It's legal for a *pointer* to be `const` and `volatile` (or more specifically, for it to point at something with both), but I don't think it's legal for a *variable* to have both. Certainly it isn't *meaningful*. – Kevin Feb 15 '16 at 03:36
  • @Thilo Sorry, I forget the source. But this isn't what the original author written. I've changed several qualifier to demonstrate my question better. – nalzok Feb 15 '16 at 03:37
  • 13
    @Kevin: Note that the C11 standard includes: _EXAMPLE 1 An object declared `extern const volatile int real_time_clock;` may be modifiable by hardware, but cannot be assigned to, incremented, or decremented._ – Jonathan Leffler Feb 15 '16 at 03:55
  • 1
    @JonathanLeffler: Fair enough; you can have a lot of weirdness when you start getting into embedded situations or variables mapped to hardware. But it is not something you will just invent *ex nihilo*. – Kevin Feb 15 '16 at 03:57
  • 1
    Legit or not, it is awful. Also the ordering is deprecated, see 6.11.5 – too honest for this site Feb 15 '16 at 04:18
  • is this used in production? – dynamic Feb 15 '16 at 09:46
  • @dynamic Someone might be killed by his colleagues if he dare to write such code! – nalzok Feb 15 '16 at 09:48

1 Answers1

17

If you read the right bits of the standard carefully enough, you find that the monster declaration in the question is valid, even if implausible.

The 'right bits' includes:

6.2.5 Types

There are five standard signed integer types, designated as signed char, short int, int, long int, and long long int. (These and other types may be designated in several additional ways, as described in 6.7.2.)

For each of the signed integer types, there is a corresponding (but different) unsigned integer type (designated with the keyword unsigned) that uses the same amount of storage (including sign information) and has the same alignment requirements.

6.7.2 Type specifiers

At least one type specifier shall be given in the declaration specifiers in each declaration, and in the specifier-qualifier list in each struct declaration and type name. Each list of type specifiers shall be one of the following multisets (delimited by commas, when there is more than one multiset per item); the type specifiers may occur in any order, possibly intermixed with the other declaration specifiers.

  • long long, signed long long, long long int, or signed long long int
  • unsigned long long, or unsigned long long int

Other declaration specifiers include storage classes (static and _Thread_local in the example), and type qualifiers (volatile and _Atomic).

6.7 Declarations

6.7 Declarations

Syntax

declaration:
     declaration-specifiers init-declarator-listopt ;
     static_assert-declaration

declaration-specifiers:
     storage-class-specifier
     declaration-specifiersopt
     type-specifier declaration-specifiersopt
     type-qualifier declaration-specifiersopt
     function-specifier declaration-specifiersopt
     alignment-specifier declaration-specifiersopt

Also, as noted by Olaf in a comment:

6.11.5 Storage-class specifiers

The placement of a storage-class specifier other than at the beginning of the declaration specifiers in a declaration is an obsolescent feature.

It is also eccentric to split up the integer type keywords (the type specifier). A more orthodox version of the declaration would be:

static _Thread_local _Atomic const volatile unsigned long long int x = 10;

(or it might drop the int).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 2
    Translating slightly from spec-ese: the "type" part of a declaration is a list of words, in almost arbitrary order; what matters is how many times each word appears (that's the "multiset" part). If `long` appears twice, `int` appears once or not at all, and the rest is valid (no appearance of other type names, no clashing or invalid specifiers, etc.) you have a `long long int` (signed if `signed` or nothing appeared, unsigned if `unsigned` appeared). Even if a bunch of other stuff is in between. – hobbs Feb 15 '16 at 07:12