Let's look at what the ISO C standard actually says (I'll refer to the 2011 edition, a draft of which is available as N1570.
A declaration of an identifier for an object with storage-class
specifier register suggests that access to the object be as fast as
possible. The extent to which such suggestions are effective is
Note that it says nothing about storing the object in a CPU register.
Elsewhere, the standard says that an external declaration may not use the
register keyword; it can only be used for variables declared inside the body of a function.
Attempting to take the address of a
register-qualified object is not allowed. (That means that declaring an array with
register, though it's legal, is not particularly useful; you have to take its address to be able to do indexing).
Apart from that, a compiler is free to ignore the
register keyword, treating it as equivalent to
auto. You can apply the
register keyword to any variable you like (as long as it's defined locally), regardless of its type or size -- but the only thing the compiler is obligated to do is to complain if you try to take its address.
A compiler is also free to store anything it likes in CPU registers, whether you use the
register keyword or not, as long as the behavior is the same as if the variable were stored in memory. This includes storing a variable in a register for its entire lifetime (if the code happens not to try to compute its address), or retrieving its value from a register where the compiler knows it happens to be stored rather than re-loading from memory.
The common wisdom these days is that compilers are better than humans at deciding which variables should be placed in registers for greater speed, so the
register keyword is of limited usefulness. In the Old Days, when systems were so small and slow that compilers couldn't afford to do that kind of analysis and dinosaurs roamed the Earth,
register could be very useful. With modern compilers, not so much.