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












11















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?










share|improve this question

























  • What's the target compiler / system?

    – dbush
    11 hours ago











  • 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.
    11 hours ago






  • 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
    11 hours ago






  • 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
    11 hours ago








  • 1





    @PSkocik Why auto can't be included but register can (well, static would not make sense)?

    – Eugene Sh.
    10 hours ago
















11















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?










share|improve this question

























  • What's the target compiler / system?

    – dbush
    11 hours ago











  • 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.
    11 hours ago






  • 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
    11 hours ago






  • 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
    11 hours ago








  • 1





    @PSkocik Why auto can't be included but register can (well, static would not make sense)?

    – Eugene Sh.
    10 hours ago














11












11








11








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?










share|improve this question
















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 5 hours ago









John Conde

185k80372423




185k80372423










asked 11 hours ago









Carl Eric CodereCarl Eric Codere

965




965













  • What's the target compiler / system?

    – dbush
    11 hours ago











  • 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.
    11 hours ago






  • 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
    11 hours ago






  • 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
    11 hours ago








  • 1





    @PSkocik Why auto can't be included but register can (well, static would not make sense)?

    – Eugene Sh.
    10 hours ago



















  • What's the target compiler / system?

    – dbush
    11 hours ago











  • 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.
    11 hours ago






  • 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
    11 hours ago






  • 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
    11 hours ago








  • 1





    @PSkocik Why auto can't be included but register can (well, static would not make sense)?

    – Eugene Sh.
    10 hours ago

















What's the target compiler / system?

– dbush
11 hours ago





What's the target compiler / system?

– dbush
11 hours ago













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.
11 hours ago





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.
11 hours ago




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
11 hours ago





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
11 hours ago




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
11 hours ago







@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
11 hours ago






1




1





@PSkocik Why auto can't be included but register can (well, static would not make sense)?

– Eugene Sh.
10 hours ago





@PSkocik Why auto can't be included but register can (well, static would not make sense)?

– Eugene Sh.
10 hours ago












5 Answers
5






active

oldest

votes


















12














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.







share|improve this answer


























  • You are right, @GovindParmar. I have reworded that section of the answer.

    – John Bollinger
    4 hours ago



















2














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).






share|improve this answer

































    2














    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 it is using registers or not each way. In gcc, this is the S option; for example:



    gcc myfile.c -S -o myfile.s





    share|improve this answer

































      2














      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.






      share|improve this answer

































        0














        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.






        share|improve this answer

























          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
          });


          }
          });














          draft saved

          draft discarded


















          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









          12














          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.







          share|improve this answer


























          • You are right, @GovindParmar. I have reworded that section of the answer.

            – John Bollinger
            4 hours ago
















          12














          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.







          share|improve this answer


























          • You are right, @GovindParmar. I have reworded that section of the answer.

            – John Bollinger
            4 hours ago














          12












          12








          12







          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.







          share|improve this answer















          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.








          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited 4 hours ago

























          answered 10 hours ago









          John BollingerJohn Bollinger

          81.8k74277




          81.8k74277













          • You are right, @GovindParmar. I have reworded that section of the answer.

            – John Bollinger
            4 hours ago



















          • You are right, @GovindParmar. I have reworded that section of the answer.

            – John Bollinger
            4 hours ago

















          You are right, @GovindParmar. I have reworded that section of the answer.

          – John Bollinger
          4 hours ago





          You are right, @GovindParmar. I have reworded that section of the answer.

          – John Bollinger
          4 hours ago













          2














          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).






          share|improve this answer






























            2














            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).






            share|improve this answer




























              2












              2








              2







              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).






              share|improve this answer















              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).







              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited 10 hours ago

























              answered 11 hours ago









              PSkocikPSkocik

              33.5k65474




              33.5k65474























                  2














                  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 it is using registers or not each way. In gcc, this is the S option; for example:



                  gcc myfile.c -S -o myfile.s





                  share|improve this answer






























                    2














                    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 it is using registers or not each way. In gcc, this is the S option; for example:



                    gcc myfile.c -S -o myfile.s





                    share|improve this answer




























                      2












                      2








                      2







                      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 it is using registers or not each way. In gcc, this is the S option; for example:



                      gcc myfile.c -S -o myfile.s





                      share|improve this answer















                      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 it is using registers or not each way. In gcc, this is the S option; for example:



                      gcc myfile.c -S -o myfile.s






                      share|improve this answer














                      share|improve this answer



                      share|improve this answer








                      edited 8 hours ago









                      Cody Gray

                      193k35378465




                      193k35378465










                      answered 11 hours ago









                      JohnHJohnH

                      2,308718




                      2,308718























                          2














                          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.






                          share|improve this answer






























                            2














                            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.






                            share|improve this answer




























                              2












                              2








                              2







                              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.






                              share|improve this answer















                              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.







                              share|improve this answer














                              share|improve this answer



                              share|improve this answer








                              edited 4 hours ago









                              Antti Haapala

                              82.9k16159198




                              82.9k16159198










                              answered 11 hours ago









                              Govind ParmarGovind Parmar

                              10.4k53359




                              10.4k53359























                                  0














                                  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.






                                  share|improve this answer






























                                    0














                                    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.






                                    share|improve this answer




























                                      0












                                      0








                                      0







                                      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.






                                      share|improve this answer















                                      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.







                                      share|improve this answer














                                      share|improve this answer



                                      share|improve this answer








                                      edited 10 hours ago

























                                      answered 11 hours ago









                                      Frankie_CFrankie_C

                                      3,5701723




                                      3,5701723






























                                          draft saved

                                          draft discarded




















































                                          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.




                                          draft saved


                                          draft discarded














                                          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





















































                                          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







                                          Popular posts from this blog

                                          If I really need a card on my start hand, how many mulligans make sense? [duplicate]

                                          Alcedinidae

                                          Can an atomic nucleus contain both particles and antiparticles? [duplicate]