Should I place the parameter storage class specifier in the function definition or in both the declaration...
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?
c declaration definition c89
|
show 5 more comments
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?
c declaration definition c89
What's the target compiler / system?
– dbush
Feb 13 at 16:12
Take a look here: port70.net/~nsz/c/c11/n1570.html#6.7.6.3 The only storage-class specifier that shall occur in a parameter declaration is register.
– Eugene Sh.
Feb 13 at 16:13
1
Storage class is not a factor in determining whether types are compatible. Therefore, the type of a function declared with register should be compatible with the type of a function declared without register, if they are otherwise compatible.
– Eric Postpischil
Feb 13 at 16:25
2
@EugeneSh. It's not an exception. Storage classes are never included in type information.auto int x;
,static int x;
, andregister int x;
all have typeint
.
– PSkocik
Feb 13 at 16:28
1
@PSkocik Whyauto
can't be included butregister
can (well,static
would not make sense)?
– Eugene Sh.
Feb 13 at 16:31
|
show 5 more comments
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?
c declaration definition c89
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?
c declaration definition c89
c declaration definition c89
edited Feb 13 at 21:54
John Conde
185k80372423
185k80372423
asked Feb 13 at 16:01
Carl Eric CodereCarl Eric Codere
1136
1136
What's the target compiler / system?
– dbush
Feb 13 at 16:12
Take a look here: port70.net/~nsz/c/c11/n1570.html#6.7.6.3 The only storage-class specifier that shall occur in a parameter declaration is register.
– Eugene Sh.
Feb 13 at 16:13
1
Storage class is not a factor in determining whether types are compatible. Therefore, the type of a function declared with register should be compatible with the type of a function declared without register, if they are otherwise compatible.
– Eric Postpischil
Feb 13 at 16:25
2
@EugeneSh. It's not an exception. Storage classes are never included in type information.auto int x;
,static int x;
, andregister int x;
all have typeint
.
– PSkocik
Feb 13 at 16:28
1
@PSkocik Whyauto
can't be included butregister
can (well,static
would not make sense)?
– Eugene Sh.
Feb 13 at 16:31
|
show 5 more comments
What's the target compiler / system?
– dbush
Feb 13 at 16:12
Take a look here: port70.net/~nsz/c/c11/n1570.html#6.7.6.3 The only storage-class specifier that shall occur in a parameter declaration is register.
– Eugene Sh.
Feb 13 at 16:13
1
Storage class is not a factor in determining whether types are compatible. Therefore, the type of a function declared with register should be compatible with the type of a function declared without register, if they are otherwise compatible.
– Eric Postpischil
Feb 13 at 16:25
2
@EugeneSh. It's not an exception. Storage classes are never included in type information.auto int x;
,static int x;
, andregister int x;
all have typeint
.
– PSkocik
Feb 13 at 16:28
1
@PSkocik Whyauto
can't be included butregister
can (well,static
would not make sense)?
– Eugene Sh.
Feb 13 at 16:31
What's the target compiler / system?
– dbush
Feb 13 at 16:12
What's the target compiler / system?
– dbush
Feb 13 at 16:12
Take a look here: port70.net/~nsz/c/c11/n1570.html#6.7.6.3 The only storage-class specifier that shall occur in a parameter declaration is register.
– Eugene Sh.
Feb 13 at 16:13
Take a look here: port70.net/~nsz/c/c11/n1570.html#6.7.6.3 The only storage-class specifier that shall occur in a parameter declaration is register.
– Eugene Sh.
Feb 13 at 16:13
1
1
Storage class is not a factor in determining whether types are compatible. Therefore, the type of a function declared with register should be compatible with the type of a function declared without register, if they are otherwise compatible.
– Eric Postpischil
Feb 13 at 16:25
Storage class is not a factor in determining whether types are compatible. Therefore, the type of a function declared with register should be compatible with the type of a function declared without register, if they are otherwise compatible.
– Eric Postpischil
Feb 13 at 16:25
2
2
@EugeneSh. It's not an exception. Storage classes are never included in type information.
auto int x;
, static int x;
, and register int x;
all have type int
.– PSkocik
Feb 13 at 16:28
@EugeneSh. It's not an exception. Storage classes are never included in type information.
auto int x;
, static int x;
, and register int x;
all have type int
.– PSkocik
Feb 13 at 16:28
1
1
@PSkocik Why
auto
can't be included but register
can (well, static
would not make sense)?– Eugene Sh.
Feb 13 at 16:31
@PSkocik Why
auto
can't be included but register
can (well, static
would not make sense)?– Eugene Sh.
Feb 13 at 16:31
|
show 5 more comments
5 Answers
5
active
oldest
votes
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,
The
register
keyword is a relic. Compilers are not obligated to make any attempt at all to actually assignregister
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 theregister
keyword.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.
As replied above, the compiler i am working with is ANSI C89 compatible, and currently the code of it is K&R, so i am trying to "modernize" the code a bit... and then bootstrap it, it does use the register keyword into consideration in its code generator.
– Carl Eric Codere
Feb 14 at 14:33
I think, as with other advice given below,the safe bet is to have the register specifier in both the definition and declaration, and this is what I did, I just wanted to make sure it was a logical choice.
– Carl Eric Codere
Feb 14 at 14:35
@CarlEricCodere, it is important to understand what I said aboutregister
. The point is not that compilers necessarily don't or shouldn't pay attention to it, but rather that if if they do, then it is more likely to cause them to emit worse code than better. Compilers from the 90's and onward are much better at allocating data to registers effectively than 70s-era compilers were. They do not need the programmer's help in that regard, so generally the best case is that using theregister
keyword is a needless redundancy.
– John Bollinger
Feb 14 at 14:43
However, if you do retain theregister
keywords then yes, putting them in both declaration and definition is best and cleanest.
– John Bollinger
Feb 14 at 14:46
add a comment |
Empirically on gcc and clang, the register
storage class on function params
is behaving the same as top-level qualifiers on params: only the ones in the definition (not a previous prototype) count.
(as for top level qualifiers, they're also discarded when type compatibility is considered, i.e., void f(int);
and void f(int const);
are compatible prototypes, but storage classes aren't part of types so type compatibility isn't an issue with them in the first place)
From the point of view of a C programmer, the only observable upshot of register
in C is that the compiler will not let you take the address of the declared object.
When I do:
void f(int A, int register B);
void f(int register A, int B)
{
/*&A;*/ //doesn't compile => A does have register storage here
&B; //compiles => B doesn't have register storage here;
//the register from the previous prototype wasn't considered
}
then &B
compiles, but &A
doesn't, so only the qualifiers in the definition appear to count.
I think that if you do need those register
, your best bet is to use it consistently in both places (the register
in prototypes could theoretically modify how calls are made).
Interesting observation... I guess this is compiler specific in that case.
– Carl Eric Codere
Feb 14 at 14:40
@CarlEricCodere I could be wrong but I don't think the standard has wording about how prototypes with different storage classifiers should be combined with the contradicting definition so testing is all there is left. For top level qualifiers (like the lastconst
intint const* const X
) I think it is prescribed that only those in the definition are used in the body. (They don't play a role in the function's type.) IRC last time I checked,__attributes
(nonstandard extension) and things likeinline
/_Noreturn
tended to accumulate.
– PSkocik
Feb 14 at 14:59
add a comment |
The C89 standard does say this (§ 3.5.4.3 External definitions):
The only storage-class specifier that shall occur in a parameter declaration is
register
.
So, it would appear that while register
is permissible as a function parameter storage class specifier, I still believe that whether or not this is honored really depends on the architecture and calling convention for the function.
Since you mentioned Watcom and C89, I'm going to assume you're targeting x86-16. The typical calling conventions for x86-16 (pascal
, stdcall
, and cdecl
) all require parameters to be pushed on the stack, not in registers, so I doubt that the keyword would actually modify how the parameters are passed to the function at the call site.
Consider, you have the following function definition:
int __stdcall add2(register int x, register int y);
The function goes into the object file as _add2@4
per requirements for stdcall. The @4 indicates how many bytes to remove from the stack at function return. The ret imm16
(return to calling procedure and pop imm16 bytes from stack) instruction is used in this case.
add2
will then have the following ret
at the end:
ret 4
If 4 bytes were not pushed on the stack at the call site (i.e. because the parameters were actually in registers), your program now has a misaligned stack and crashes.
From my understanding, unless i am completely wrong, the storage class specifier in the parameter just means that the variable associated with the parameter should be placed in a register (even after the start of the code executes in the routine), it does not necessarily indicate it will be called using register calling conventions, no?
– Carl Eric Codere
Feb 14 at 14:38
@CarlEricCodere The standard doesn't actually say anything on that topic - only that the keywordregister
is permissible in the parameter list
– Govind Parmar
Feb 14 at 14:41
add a comment |
Since you're using an old compiler for an odd platform, sometimes just looking at what the compiler does is more important than assuming it will comply perfectly with the C spec.
This means you want to run each variant of your example through the compiler, with the compiler option set to generate assembly instead of an executable. Look at the assembly, and see if you can tell if it is using registers or not each way. In gcc, this is the S
option; for example:
gcc myfile.c -S -o myfile.s
add a comment |
From C17 standard, 6.7.1 Storage-class specifiers:
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
implementation-defined.
Imply that the compiler will try, or not depending on compiler, to speed-up parameter access, but doesn't imply any modification to the calling convention (basically no modifications on calling side).
So it should be present in the function definition, but is insignificant in prototype.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54674465%2fshould-i-place-the-parameter-storage-class-specifier-in-the-function-definition%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
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,
The
register
keyword is a relic. Compilers are not obligated to make any attempt at all to actually assignregister
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 theregister
keyword.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.
As replied above, the compiler i am working with is ANSI C89 compatible, and currently the code of it is K&R, so i am trying to "modernize" the code a bit... and then bootstrap it, it does use the register keyword into consideration in its code generator.
– Carl Eric Codere
Feb 14 at 14:33
I think, as with other advice given below,the safe bet is to have the register specifier in both the definition and declaration, and this is what I did, I just wanted to make sure it was a logical choice.
– Carl Eric Codere
Feb 14 at 14:35
@CarlEricCodere, it is important to understand what I said aboutregister
. The point is not that compilers necessarily don't or shouldn't pay attention to it, but rather that if if they do, then it is more likely to cause them to emit worse code than better. Compilers from the 90's and onward are much better at allocating data to registers effectively than 70s-era compilers were. They do not need the programmer's help in that regard, so generally the best case is that using theregister
keyword is a needless redundancy.
– John Bollinger
Feb 14 at 14:43
However, if you do retain theregister
keywords then yes, putting them in both declaration and definition is best and cleanest.
– John Bollinger
Feb 14 at 14:46
add a comment |
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,
The
register
keyword is a relic. Compilers are not obligated to make any attempt at all to actually assignregister
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 theregister
keyword.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.
As replied above, the compiler i am working with is ANSI C89 compatible, and currently the code of it is K&R, so i am trying to "modernize" the code a bit... and then bootstrap it, it does use the register keyword into consideration in its code generator.
– Carl Eric Codere
Feb 14 at 14:33
I think, as with other advice given below,the safe bet is to have the register specifier in both the definition and declaration, and this is what I did, I just wanted to make sure it was a logical choice.
– Carl Eric Codere
Feb 14 at 14:35
@CarlEricCodere, it is important to understand what I said aboutregister
. The point is not that compilers necessarily don't or shouldn't pay attention to it, but rather that if if they do, then it is more likely to cause them to emit worse code than better. Compilers from the 90's and onward are much better at allocating data to registers effectively than 70s-era compilers were. They do not need the programmer's help in that regard, so generally the best case is that using theregister
keyword is a needless redundancy.
– John Bollinger
Feb 14 at 14:43
However, if you do retain theregister
keywords then yes, putting them in both declaration and definition is best and cleanest.
– John Bollinger
Feb 14 at 14:46
add a comment |
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,
The
register
keyword is a relic. Compilers are not obligated to make any attempt at all to actually assignregister
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 theregister
keyword.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.
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,
The
register
keyword is a relic. Compilers are not obligated to make any attempt at all to actually assignregister
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 theregister
keyword.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.
edited Feb 13 at 23:21
answered Feb 13 at 16:38
John BollingerJohn Bollinger
82.1k74277
82.1k74277
As replied above, the compiler i am working with is ANSI C89 compatible, and currently the code of it is K&R, so i am trying to "modernize" the code a bit... and then bootstrap it, it does use the register keyword into consideration in its code generator.
– Carl Eric Codere
Feb 14 at 14:33
I think, as with other advice given below,the safe bet is to have the register specifier in both the definition and declaration, and this is what I did, I just wanted to make sure it was a logical choice.
– Carl Eric Codere
Feb 14 at 14:35
@CarlEricCodere, it is important to understand what I said aboutregister
. The point is not that compilers necessarily don't or shouldn't pay attention to it, but rather that if if they do, then it is more likely to cause them to emit worse code than better. Compilers from the 90's and onward are much better at allocating data to registers effectively than 70s-era compilers were. They do not need the programmer's help in that regard, so generally the best case is that using theregister
keyword is a needless redundancy.
– John Bollinger
Feb 14 at 14:43
However, if you do retain theregister
keywords then yes, putting them in both declaration and definition is best and cleanest.
– John Bollinger
Feb 14 at 14:46
add a comment |
As replied above, the compiler i am working with is ANSI C89 compatible, and currently the code of it is K&R, so i am trying to "modernize" the code a bit... and then bootstrap it, it does use the register keyword into consideration in its code generator.
– Carl Eric Codere
Feb 14 at 14:33
I think, as with other advice given below,the safe bet is to have the register specifier in both the definition and declaration, and this is what I did, I just wanted to make sure it was a logical choice.
– Carl Eric Codere
Feb 14 at 14:35
@CarlEricCodere, it is important to understand what I said aboutregister
. The point is not that compilers necessarily don't or shouldn't pay attention to it, but rather that if if they do, then it is more likely to cause them to emit worse code than better. Compilers from the 90's and onward are much better at allocating data to registers effectively than 70s-era compilers were. They do not need the programmer's help in that regard, so generally the best case is that using theregister
keyword is a needless redundancy.
– John Bollinger
Feb 14 at 14:43
However, if you do retain theregister
keywords then yes, putting them in both declaration and definition is best and cleanest.
– John Bollinger
Feb 14 at 14:46
As replied above, the compiler i am working with is ANSI C89 compatible, and currently the code of it is K&R, so i am trying to "modernize" the code a bit... and then bootstrap it, it does use the register keyword into consideration in its code generator.
– Carl Eric Codere
Feb 14 at 14:33
As replied above, the compiler i am working with is ANSI C89 compatible, and currently the code of it is K&R, so i am trying to "modernize" the code a bit... and then bootstrap it, it does use the register keyword into consideration in its code generator.
– Carl Eric Codere
Feb 14 at 14:33
I think, as with other advice given below,the safe bet is to have the register specifier in both the definition and declaration, and this is what I did, I just wanted to make sure it was a logical choice.
– Carl Eric Codere
Feb 14 at 14:35
I think, as with other advice given below,the safe bet is to have the register specifier in both the definition and declaration, and this is what I did, I just wanted to make sure it was a logical choice.
– Carl Eric Codere
Feb 14 at 14:35
@CarlEricCodere, it is important to understand what I said about
register
. The point is not that compilers necessarily don't or shouldn't pay attention to it, but rather that if if they do, then it is more likely to cause them to emit worse code than better. Compilers from the 90's and onward are much better at allocating data to registers effectively than 70s-era compilers were. They do not need the programmer's help in that regard, so generally the best case is that using the register
keyword is a needless redundancy.– John Bollinger
Feb 14 at 14:43
@CarlEricCodere, it is important to understand what I said about
register
. The point is not that compilers necessarily don't or shouldn't pay attention to it, but rather that if if they do, then it is more likely to cause them to emit worse code than better. Compilers from the 90's and onward are much better at allocating data to registers effectively than 70s-era compilers were. They do not need the programmer's help in that regard, so generally the best case is that using the register
keyword is a needless redundancy.– John Bollinger
Feb 14 at 14:43
However, if you do retain the
register
keywords then yes, putting them in both declaration and definition is best and cleanest.– John Bollinger
Feb 14 at 14:46
However, if you do retain the
register
keywords then yes, putting them in both declaration and definition is best and cleanest.– John Bollinger
Feb 14 at 14:46
add a comment |
Empirically on gcc and clang, the register
storage class on function params
is behaving the same as top-level qualifiers on params: only the ones in the definition (not a previous prototype) count.
(as for top level qualifiers, they're also discarded when type compatibility is considered, i.e., void f(int);
and void f(int const);
are compatible prototypes, but storage classes aren't part of types so type compatibility isn't an issue with them in the first place)
From the point of view of a C programmer, the only observable upshot of register
in C is that the compiler will not let you take the address of the declared object.
When I do:
void f(int A, int register B);
void f(int register A, int B)
{
/*&A;*/ //doesn't compile => A does have register storage here
&B; //compiles => B doesn't have register storage here;
//the register from the previous prototype wasn't considered
}
then &B
compiles, but &A
doesn't, so only the qualifiers in the definition appear to count.
I think that if you do need those register
, your best bet is to use it consistently in both places (the register
in prototypes could theoretically modify how calls are made).
Interesting observation... I guess this is compiler specific in that case.
– Carl Eric Codere
Feb 14 at 14:40
@CarlEricCodere I could be wrong but I don't think the standard has wording about how prototypes with different storage classifiers should be combined with the contradicting definition so testing is all there is left. For top level qualifiers (like the lastconst
intint const* const X
) I think it is prescribed that only those in the definition are used in the body. (They don't play a role in the function's type.) IRC last time I checked,__attributes
(nonstandard extension) and things likeinline
/_Noreturn
tended to accumulate.
– PSkocik
Feb 14 at 14:59
add a comment |
Empirically on gcc and clang, the register
storage class on function params
is behaving the same as top-level qualifiers on params: only the ones in the definition (not a previous prototype) count.
(as for top level qualifiers, they're also discarded when type compatibility is considered, i.e., void f(int);
and void f(int const);
are compatible prototypes, but storage classes aren't part of types so type compatibility isn't an issue with them in the first place)
From the point of view of a C programmer, the only observable upshot of register
in C is that the compiler will not let you take the address of the declared object.
When I do:
void f(int A, int register B);
void f(int register A, int B)
{
/*&A;*/ //doesn't compile => A does have register storage here
&B; //compiles => B doesn't have register storage here;
//the register from the previous prototype wasn't considered
}
then &B
compiles, but &A
doesn't, so only the qualifiers in the definition appear to count.
I think that if you do need those register
, your best bet is to use it consistently in both places (the register
in prototypes could theoretically modify how calls are made).
Interesting observation... I guess this is compiler specific in that case.
– Carl Eric Codere
Feb 14 at 14:40
@CarlEricCodere I could be wrong but I don't think the standard has wording about how prototypes with different storage classifiers should be combined with the contradicting definition so testing is all there is left. For top level qualifiers (like the lastconst
intint const* const X
) I think it is prescribed that only those in the definition are used in the body. (They don't play a role in the function's type.) IRC last time I checked,__attributes
(nonstandard extension) and things likeinline
/_Noreturn
tended to accumulate.
– PSkocik
Feb 14 at 14:59
add a comment |
Empirically on gcc and clang, the register
storage class on function params
is behaving the same as top-level qualifiers on params: only the ones in the definition (not a previous prototype) count.
(as for top level qualifiers, they're also discarded when type compatibility is considered, i.e., void f(int);
and void f(int const);
are compatible prototypes, but storage classes aren't part of types so type compatibility isn't an issue with them in the first place)
From the point of view of a C programmer, the only observable upshot of register
in C is that the compiler will not let you take the address of the declared object.
When I do:
void f(int A, int register B);
void f(int register A, int B)
{
/*&A;*/ //doesn't compile => A does have register storage here
&B; //compiles => B doesn't have register storage here;
//the register from the previous prototype wasn't considered
}
then &B
compiles, but &A
doesn't, so only the qualifiers in the definition appear to count.
I think that if you do need those register
, your best bet is to use it consistently in both places (the register
in prototypes could theoretically modify how calls are made).
Empirically on gcc and clang, the register
storage class on function params
is behaving the same as top-level qualifiers on params: only the ones in the definition (not a previous prototype) count.
(as for top level qualifiers, they're also discarded when type compatibility is considered, i.e., void f(int);
and void f(int const);
are compatible prototypes, but storage classes aren't part of types so type compatibility isn't an issue with them in the first place)
From the point of view of a C programmer, the only observable upshot of register
in C is that the compiler will not let you take the address of the declared object.
When I do:
void f(int A, int register B);
void f(int register A, int B)
{
/*&A;*/ //doesn't compile => A does have register storage here
&B; //compiles => B doesn't have register storage here;
//the register from the previous prototype wasn't considered
}
then &B
compiles, but &A
doesn't, so only the qualifiers in the definition appear to count.
I think that if you do need those register
, your best bet is to use it consistently in both places (the register
in prototypes could theoretically modify how calls are made).
edited Feb 13 at 16:41
answered Feb 13 at 16:22
PSkocikPSkocik
33.8k65475
33.8k65475
Interesting observation... I guess this is compiler specific in that case.
– Carl Eric Codere
Feb 14 at 14:40
@CarlEricCodere I could be wrong but I don't think the standard has wording about how prototypes with different storage classifiers should be combined with the contradicting definition so testing is all there is left. For top level qualifiers (like the lastconst
intint const* const X
) I think it is prescribed that only those in the definition are used in the body. (They don't play a role in the function's type.) IRC last time I checked,__attributes
(nonstandard extension) and things likeinline
/_Noreturn
tended to accumulate.
– PSkocik
Feb 14 at 14:59
add a comment |
Interesting observation... I guess this is compiler specific in that case.
– Carl Eric Codere
Feb 14 at 14:40
@CarlEricCodere I could be wrong but I don't think the standard has wording about how prototypes with different storage classifiers should be combined with the contradicting definition so testing is all there is left. For top level qualifiers (like the lastconst
intint const* const X
) I think it is prescribed that only those in the definition are used in the body. (They don't play a role in the function's type.) IRC last time I checked,__attributes
(nonstandard extension) and things likeinline
/_Noreturn
tended to accumulate.
– PSkocik
Feb 14 at 14:59
Interesting observation... I guess this is compiler specific in that case.
– Carl Eric Codere
Feb 14 at 14:40
Interesting observation... I guess this is compiler specific in that case.
– Carl Eric Codere
Feb 14 at 14:40
@CarlEricCodere I could be wrong but I don't think the standard has wording about how prototypes with different storage classifiers should be combined with the contradicting definition so testing is all there is left. For top level qualifiers (like the last
const
int int const* const X
) I think it is prescribed that only those in the definition are used in the body. (They don't play a role in the function's type.) IRC last time I checked, __attributes
(nonstandard extension) and things like inline
/_Noreturn
tended to accumulate.– PSkocik
Feb 14 at 14:59
@CarlEricCodere I could be wrong but I don't think the standard has wording about how prototypes with different storage classifiers should be combined with the contradicting definition so testing is all there is left. For top level qualifiers (like the last
const
int int const* const X
) I think it is prescribed that only those in the definition are used in the body. (They don't play a role in the function's type.) IRC last time I checked, __attributes
(nonstandard extension) and things like inline
/_Noreturn
tended to accumulate.– PSkocik
Feb 14 at 14:59
add a comment |
The C89 standard does say this (§ 3.5.4.3 External definitions):
The only storage-class specifier that shall occur in a parameter declaration is
register
.
So, it would appear that while register
is permissible as a function parameter storage class specifier, I still believe that whether or not this is honored really depends on the architecture and calling convention for the function.
Since you mentioned Watcom and C89, I'm going to assume you're targeting x86-16. The typical calling conventions for x86-16 (pascal
, stdcall
, and cdecl
) all require parameters to be pushed on the stack, not in registers, so I doubt that the keyword would actually modify how the parameters are passed to the function at the call site.
Consider, you have the following function definition:
int __stdcall add2(register int x, register int y);
The function goes into the object file as _add2@4
per requirements for stdcall. The @4 indicates how many bytes to remove from the stack at function return. The ret imm16
(return to calling procedure and pop imm16 bytes from stack) instruction is used in this case.
add2
will then have the following ret
at the end:
ret 4
If 4 bytes were not pushed on the stack at the call site (i.e. because the parameters were actually in registers), your program now has a misaligned stack and crashes.
From my understanding, unless i am completely wrong, the storage class specifier in the parameter just means that the variable associated with the parameter should be placed in a register (even after the start of the code executes in the routine), it does not necessarily indicate it will be called using register calling conventions, no?
– Carl Eric Codere
Feb 14 at 14:38
@CarlEricCodere The standard doesn't actually say anything on that topic - only that the keywordregister
is permissible in the parameter list
– Govind Parmar
Feb 14 at 14:41
add a comment |
The C89 standard does say this (§ 3.5.4.3 External definitions):
The only storage-class specifier that shall occur in a parameter declaration is
register
.
So, it would appear that while register
is permissible as a function parameter storage class specifier, I still believe that whether or not this is honored really depends on the architecture and calling convention for the function.
Since you mentioned Watcom and C89, I'm going to assume you're targeting x86-16. The typical calling conventions for x86-16 (pascal
, stdcall
, and cdecl
) all require parameters to be pushed on the stack, not in registers, so I doubt that the keyword would actually modify how the parameters are passed to the function at the call site.
Consider, you have the following function definition:
int __stdcall add2(register int x, register int y);
The function goes into the object file as _add2@4
per requirements for stdcall. The @4 indicates how many bytes to remove from the stack at function return. The ret imm16
(return to calling procedure and pop imm16 bytes from stack) instruction is used in this case.
add2
will then have the following ret
at the end:
ret 4
If 4 bytes were not pushed on the stack at the call site (i.e. because the parameters were actually in registers), your program now has a misaligned stack and crashes.
From my understanding, unless i am completely wrong, the storage class specifier in the parameter just means that the variable associated with the parameter should be placed in a register (even after the start of the code executes in the routine), it does not necessarily indicate it will be called using register calling conventions, no?
– Carl Eric Codere
Feb 14 at 14:38
@CarlEricCodere The standard doesn't actually say anything on that topic - only that the keywordregister
is permissible in the parameter list
– Govind Parmar
Feb 14 at 14:41
add a comment |
The C89 standard does say this (§ 3.5.4.3 External definitions):
The only storage-class specifier that shall occur in a parameter declaration is
register
.
So, it would appear that while register
is permissible as a function parameter storage class specifier, I still believe that whether or not this is honored really depends on the architecture and calling convention for the function.
Since you mentioned Watcom and C89, I'm going to assume you're targeting x86-16. The typical calling conventions for x86-16 (pascal
, stdcall
, and cdecl
) all require parameters to be pushed on the stack, not in registers, so I doubt that the keyword would actually modify how the parameters are passed to the function at the call site.
Consider, you have the following function definition:
int __stdcall add2(register int x, register int y);
The function goes into the object file as _add2@4
per requirements for stdcall. The @4 indicates how many bytes to remove from the stack at function return. The ret imm16
(return to calling procedure and pop imm16 bytes from stack) instruction is used in this case.
add2
will then have the following ret
at the end:
ret 4
If 4 bytes were not pushed on the stack at the call site (i.e. because the parameters were actually in registers), your program now has a misaligned stack and crashes.
The C89 standard does say this (§ 3.5.4.3 External definitions):
The only storage-class specifier that shall occur in a parameter declaration is
register
.
So, it would appear that while register
is permissible as a function parameter storage class specifier, I still believe that whether or not this is honored really depends on the architecture and calling convention for the function.
Since you mentioned Watcom and C89, I'm going to assume you're targeting x86-16. The typical calling conventions for x86-16 (pascal
, stdcall
, and cdecl
) all require parameters to be pushed on the stack, not in registers, so I doubt that the keyword would actually modify how the parameters are passed to the function at the call site.
Consider, you have the following function definition:
int __stdcall add2(register int x, register int y);
The function goes into the object file as _add2@4
per requirements for stdcall. The @4 indicates how many bytes to remove from the stack at function return. The ret imm16
(return to calling procedure and pop imm16 bytes from stack) instruction is used in this case.
add2
will then have the following ret
at the end:
ret 4
If 4 bytes were not pushed on the stack at the call site (i.e. because the parameters were actually in registers), your program now has a misaligned stack and crashes.
edited Feb 14 at 14:37
answered Feb 13 at 16:14
Govind ParmarGovind Parmar
11.1k53360
11.1k53360
From my understanding, unless i am completely wrong, the storage class specifier in the parameter just means that the variable associated with the parameter should be placed in a register (even after the start of the code executes in the routine), it does not necessarily indicate it will be called using register calling conventions, no?
– Carl Eric Codere
Feb 14 at 14:38
@CarlEricCodere The standard doesn't actually say anything on that topic - only that the keywordregister
is permissible in the parameter list
– Govind Parmar
Feb 14 at 14:41
add a comment |
From my understanding, unless i am completely wrong, the storage class specifier in the parameter just means that the variable associated with the parameter should be placed in a register (even after the start of the code executes in the routine), it does not necessarily indicate it will be called using register calling conventions, no?
– Carl Eric Codere
Feb 14 at 14:38
@CarlEricCodere The standard doesn't actually say anything on that topic - only that the keywordregister
is permissible in the parameter list
– Govind Parmar
Feb 14 at 14:41
From my understanding, unless i am completely wrong, the storage class specifier in the parameter just means that the variable associated with the parameter should be placed in a register (even after the start of the code executes in the routine), it does not necessarily indicate it will be called using register calling conventions, no?
– Carl Eric Codere
Feb 14 at 14:38
From my understanding, unless i am completely wrong, the storage class specifier in the parameter just means that the variable associated with the parameter should be placed in a register (even after the start of the code executes in the routine), it does not necessarily indicate it will be called using register calling conventions, no?
– Carl Eric Codere
Feb 14 at 14:38
@CarlEricCodere The standard doesn't actually say anything on that topic - only that the keyword
register
is permissible in the parameter list– Govind Parmar
Feb 14 at 14:41
@CarlEricCodere The standard doesn't actually say anything on that topic - only that the keyword
register
is permissible in the parameter list– Govind Parmar
Feb 14 at 14:41
add a comment |
Since you're using an old compiler for an odd platform, sometimes just looking at what the compiler does is more important than assuming it will comply perfectly with the C spec.
This means you want to run each variant of your example through the compiler, with the compiler option set to generate assembly instead of an executable. Look at the assembly, and see if you can tell if it is using registers or not each way. In gcc, this is the S
option; for example:
gcc myfile.c -S -o myfile.s
add a comment |
Since you're using an old compiler for an odd platform, sometimes just looking at what the compiler does is more important than assuming it will comply perfectly with the C spec.
This means you want to run each variant of your example through the compiler, with the compiler option set to generate assembly instead of an executable. Look at the assembly, and see if you can tell if it is using registers or not each way. In gcc, this is the S
option; for example:
gcc myfile.c -S -o myfile.s
add a comment |
Since you're using an old compiler for an odd platform, sometimes just looking at what the compiler does is more important than assuming it will comply perfectly with the C spec.
This means you want to run each variant of your example through the compiler, with the compiler option set to generate assembly instead of an executable. Look at the assembly, and see if you can tell if it is using registers or not each way. In gcc, this is the S
option; for example:
gcc myfile.c -S -o myfile.s
Since you're using an old compiler for an odd platform, sometimes just looking at what the compiler does is more important than assuming it will comply perfectly with the C spec.
This means you want to run each variant of your example through the compiler, with the compiler option set to generate assembly instead of an executable. Look at the assembly, and see if you can tell if it is using registers or not each way. In gcc, this is the S
option; for example:
gcc myfile.c -S -o myfile.s
edited Feb 14 at 18:17
answered Feb 13 at 16:12
JohnHJohnH
2,318718
2,318718
add a comment |
add a comment |
From C17 standard, 6.7.1 Storage-class specifiers:
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
implementation-defined.
Imply that the compiler will try, or not depending on compiler, to speed-up parameter access, but doesn't imply any modification to the calling convention (basically no modifications on calling side).
So it should be present in the function definition, but is insignificant in prototype.
add a comment |
From C17 standard, 6.7.1 Storage-class specifiers:
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
implementation-defined.
Imply that the compiler will try, or not depending on compiler, to speed-up parameter access, but doesn't imply any modification to the calling convention (basically no modifications on calling side).
So it should be present in the function definition, but is insignificant in prototype.
add a comment |
From C17 standard, 6.7.1 Storage-class specifiers:
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
implementation-defined.
Imply that the compiler will try, or not depending on compiler, to speed-up parameter access, but doesn't imply any modification to the calling convention (basically no modifications on calling side).
So it should be present in the function definition, but is insignificant in prototype.
From C17 standard, 6.7.1 Storage-class specifiers:
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
implementation-defined.
Imply that the compiler will try, or not depending on compiler, to speed-up parameter access, but doesn't imply any modification to the calling convention (basically no modifications on calling side).
So it should be present in the function definition, but is insignificant in prototype.
edited Feb 13 at 16:31
answered Feb 13 at 16:25
Frankie_CFrankie_C
3,6051723
3,6051723
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54674465%2fshould-i-place-the-parameter-storage-class-specifier-in-the-function-definition%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
What's the target compiler / system?
– dbush
Feb 13 at 16:12
Take a look here: port70.net/~nsz/c/c11/n1570.html#6.7.6.3 The only storage-class specifier that shall occur in a parameter declaration is register.
– Eugene Sh.
Feb 13 at 16:13
1
Storage class is not a factor in determining whether types are compatible. Therefore, the type of a function declared with register should be compatible with the type of a function declared without register, if they are otherwise compatible.
– Eric Postpischil
Feb 13 at 16:25
2
@EugeneSh. It's not an exception. Storage classes are never included in type information.
auto int x;
,static int x;
, andregister int x;
all have typeint
.– PSkocik
Feb 13 at 16:28
1
@PSkocik Why
auto
can't be included butregister
can (well,static
would not make sense)?– Eugene Sh.
Feb 13 at 16:31