Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I place the parameter storage class specifier in the function definition or in both the declaration and definition?

I'm working on porting some old K&R code to ANSI C, so I'm writing missing function prototype declarations. A lot of the function definitions have parameters with the register storage class, but I'm not sure if the register storage class specifier can be omitted in the function prototype?

With and without the register storage class specific declaration, the code compiles correctly (I tried GCC, VC++ and Watcom C). I could not find any information in the ISO/ANSI C89 standard on what is the correct way to do - is it OK if I just put the register keyword in the function definition?

int add(register int x, register int y); 

int add(register int x, register int y)
{
  return x+y;
}

This also builds correctly:

int add(int x, int y);

int add(register int x, register int y)
{
   return x+y;
}

I want to make sure that the register storage specifier is really taken into account, according to the standard (my target is to compile using a very old compiler where this storage class specifier is important). Are both OK and it's just a question of coding style, or not?

like image 972
Carl Eric Codere Avatar asked Feb 13 '19 16:02

Carl Eric Codere


People also ask

How are storage class specifiers helpful in refining the declaration of variables?

Storage class specifiers in C language tells the compiler where to store a variable, how to store the variable, what is the initial value of the variable and life time of the variable.

Can a function be declared with a static storage class?

A function declared with the static storage class specifier has internal linkage, which means that it may be called only within the translation unit in which it is defined. The default for a function is external linkage. The only storage class that can be specified for a function parameter is register.

When should we use register storage specifier?

The register storage class specifier indicates to the compiler that the object should be stored in a machine register. The register storage class specifier is typically specified for heavily used variables, such as a loop control variable, in the hopes of enhancing performance by minimizing access time.


1 Answers

The key provision is that every declaration of the function must specify a compatible type for it. That requires compatible return types, and, for declarations such as yours that contain parameter lists, compatible type for each pair of corresponding parameters.

The question then becomes whether storage-class specifiers differentiate types. They do not, though the standard says that indirectly, by omission of storage-class specifiers from its discussion of type derivation. The property specified by a storage-class specifier in an object's declaration is therefore separate from that object's type.

Moreover, C89 specifically says

The storage-class specifier in the declaration specifiers for a parameter declaration, if present, is ignored unless the declared parameter is one of the members of the parameter type list for a function definition.

(emphasis added). A function definition is a declaration accompanied by a function body, as opposed to a forward declaration, so your two codes have identical semantics.

With and without the register storage class specific declaration, the code compiles correctly (I tried gcc, VC++ and Watcom), I could not find any information in the ISO/ANSI C89 standard on what is the correct way to do, or is it ok if i just put the register keyword in the function definition?

Personally, I would be inclined to make each forward declaration identical to the declaration in the corresponding function definition. This is never wrong if the function definition itself is correct.

HOWEVER,

  1. The register keyword is a relic. Compilers are not obligated to make any attempt at all to actually assign register variables to registers, and modern compilers are a lot better than humans at deciding how to assign variables to registers and otherwise to generate fast code anyway. As long as you're converting old code, I would take the opportunity to remove all appearances of the register keyword.

  2. C89 is obsolete. The latest version of the standard is C 2018; C 2011 is widely deployed; and C99 (also, technically, obsolete) is available almost everywhere. Perhaps there is a good reason for you to target C89, but you should strongly consider instead targeting C11 or C18, or at least C99.

like image 70
John Bollinger Avatar answered Sep 28 '22 16:09

John Bollinger