views:

374

answers:

4
+2  Q: 

C Prototype scope

I learnt that

the type specifier that declares the identifier in the list of parameter declarations in a function prototype (not part of a function definition), the identifier has function prototype scope, which terminates at the end of the function declarator.

Please see the C program mentioned below.

void fn (struct st {int a;} a, struct st b) ;

struct st obj ;

Compilers promptly issues an error as 'obj' size is unknown (or) struct st is not a 'type'. That's right! the declaration of the structure 'struct st' ends at the prototype declaration.

I believe prototype had this limit because we can use some variable names in the prototype declarations too. These names may conflict with the variables in the same scope (as that of function prototype). Like below.

void fn (int a) ;
int a ;

So, to allow the above declarations the scope of prototype is limited. (Correct me if I am wrong)

But, for a prototype declaration, parameter variable name are of no use. So, why is it being 'narrowly scoped'? What is the significance of having the parameter variable name? What is the language designer's (or) specification's thought on this?

+4  A: 

The parameter names can help document the use of the parameters.

Consider a memory setting function:

void mem_set(void *, int, int);

void mem_set(void *buffer, int value, int nbytes);

Which is easier to understand?


The structure type declaration as written is local to the function prototype. As you probably know (now, if not before), you need to define the type outside the scope of the prototype in order to use it successfully. That is, you must write:

struct st {int a;};
void fn(struct st a, struct st b);

You say:

I believe prototype had this limit because we can use some variable names in the prototype declarations too. These names may conflict with the variables in the same scope (as that of function prototype). Like below.

void fn(int a);
int a;

So, to allow the above declarations the scope of prototype is limited. (Correct me if I am wrong)

There's a possibility that GCC with '-Wshadow' would warn about the parameter 'a' shadowing the global variable 'a' - it would certainly do so in the function definition, and might do so in the function declaration. But that is not a mandatory warning; the code as written is legal C - albeit slightly dubious because of the shadowing.


There is a protracted discussion in the comments about "why does C restrict (prevent) you from declaring a type in a parameter list", with the sub-text "because C++ does allow you to do it":

Comments

Being allowed of /**/, it should be the programmer's responsibility (as per coding practices) to add proper comments about the use of the language there. I believe there has to be 'something' other than providing assistance to comments. – Ganesh Gopalasubramanian

OK - believe away. Compatibility with what C++ did was the rest of the reason, and the argument names were added there to promote readability. See Stroustrup 'Design and Evolution of C++'. Note that the names of the parameters in the prototype are not part of the interface - see the discussion on providing arguments by name instead of position. – Jonathan Leffler

I believe the question the OP is asking is "what's the point of having function prototype scope at all?". You answer, unfortunately, does not shed any light on it. Frankly, I have no idea either. If they simply wanted to limit the scope of named parameter declarations (in a non-defining declaration) as OP guesses, they could've done it without introducing a scope (as it is done in C++ for example). – AndreyT

@AndreyT: in a function definition, the arguments are local to the body of the function (it is no longer possible to hide an argument by a local variable in the outermost block of the body of the function). The prototype logically declares a type inside the function, and therefore should be scoped as defined - and hence, a type defined only in the function prototype cannot be used outside the function, and a function that cannot be called is of little benefit to anyone. – Jonathan Leffler

@Jonathan Leffler: You seem to be explaining why the parameter names were allowed ("compatibility with C++" - OK). What I'd like to know is the rationale for introducing the "function prototype scope". Why did they see the need to introduce such a scope? C++ doesn't do it that way. There's no function prototype scope in C++. – AndreyT

@AndreyT Yeh! We both are drowning in the same boat :) – Ganesh Gopalasubramanian

Counter-example

This demonstrates that C++ "does do it that way".

#include <cstdio>
using namespace std;

extern void x(struct c {int y;} b);

void x(struct c b)
{
    printf("b.y = %d\n", b.y);
}

int main()
{
    struct c a;
    a.y = 0;
    x(a);
    return(0);
}

This code does not compile with G++ (4.0.1 on MacOS X 10.5.8). It complains:

$ g++ -o xx xx.cpp
xx.cpp:4: error: types may not be defined in parameter types
$

The error occurs at default levels of warning/error - as well as at pedantic levels.

So it seems fair and accurate to say "C behaves as C++ does" in this context. Can you demonstrate with a counter-counter-example how you can define a type in a function prototype in C++, specifying which C++ compiler and platform allows it?

Jonathan Leffler
Being allowed of /**/, It should be the programmer's responsibility (as per coding practices) to add proper comments about the use of the language there. I believe there has to be 'something' other than providing assistance to comments.
Ganesh Gopalasubramanian
I believe the question the OP is asking is "what's the point of having function prototype scope at all?". You answer, unfortunately, does not shed any light on it. Frankly, I have no idea either. If they simply wanted to limit the scope of named parameter declarations (in a non-defining declaration) as OP guesses, they could've done it without introducing a scope (as it is done in C++ for example).
AndreyT
OK - believe away. Compatibility with what C++ did was the rest of the reason, and the argument names were added there to promote readability. See Stroustrup 'Design and Evolution of C++'. Note that the names of the parameters in the prototype are not part of the interface - see the discussion on providing arguments by name instead of position.
Jonathan Leffler
@AndreyT BTW whats that OP? I am new to SO :)
Ganesh Gopalasubramanian
@AndreyT: in a function definition, the arguments are local to the body of the function (it is no longer possible to hide an argument by a local variable in the outermost block of the body of the function). The prototype logically declares a type inside the function, and therefore should be scoped as defined - and hence, a type defined only in the function prototype cannot be used outside the function, and a function that cannot be called is of little benefit to anyone.
Jonathan Leffler
@Ganesh: OP is an abbreviation for 'Original Poster', meaning the person who asked the question.
Jonathan Leffler
@Jonathan Leffler Take my bow for that OP thing!
Ganesh Gopalasubramanian
@ganesh: see also http://www.acronymfinder.com/OP.html
Jonathan Leffler
@Jonathan Leffler: You seem to be explaining why the parameter names were allowed ("compatibility with C++" - OK). What I'd like to know is the rationale for introducing the "function prototype scope". Why did they see the need to introduce such a *scope*? C++ doesn't do it that way. There's no function prototype scope in C++.
AndreyT
@AndreyT Yeh! We both are drowning in the same boat :)
Ganesh Gopalasubramanian
I referred the rationale document and it says "The function prototype syntax was adapted from C++".
Ganesh Gopalasubramanian
@Jonathan Leffler: In your addition you took it too far with the parameters. Again, the question was not about the ability to *define* types in parameter lists (which is indeed illegal in C++), but about the rationale behind making prototype a *scope*. Try just *declaring* a type there (a non-defining declaration) - and it will "work" in C++, but not in C. All because of that scope thing. Again, the problem I'm having with this is that in C the prototype scope is "useless", since you can never access whatever you declare there again.
AndreyT
@AndreyT You're right. It is indeed 'useless'. But, now at least we know how it came into existence.
Ganesh Gopalasubramanian
@Jonathan Leffler: An example would be a plain `void foo(struct S *);`. In C++ this introduces an incomplete type `S` into the global namespace - it declares `S`, which you can complete (define) later. In C this will declare a local `S` (local to prototype), which will become lost forever after the prototype ends.
AndreyT
OK - thank you for explaining the problem. I understand now - which I didn't previously. I don't have an explanation of why the C rules are different from the C++ rules.
Jonathan Leffler
A: 

Two - let's make that three - obvious reasons for having parameter names in function prototypes:

  • Names indicate the purpose of each parameter.
  • Names let you refer to the parameters more easily in API documentation (e.g. in Doxygen).
  • You can more easily generate the prototypes (cut and paste, or automate) from the definitions.
Dipstick
+2  A: 

I'd echo what chrisharris and Jonathan Leffler said. Having identifiers scoped to the prototype is useful. More useful than not having them. Additionally, it lets the prototypes follow the same grammar as function declarations, which is probably nice from a language design point of view as well as from an implementor's point of view.

I don't think this is what you're proposing, but just for completeness, making those identifiers have scope outside of the prototype would make very little sense and potentially cause problems.

The fact that you can declare something in a prototype that is useless outside the prototype is a downside (I guess), but it's pretty harmless. The fix is "don't do that". I'm unaware of any hidden problems with letting you declare something you can't use outside the prototype.

If we're going to fix that problem, we should also fix the fact that C lets you declare something like:

struct {
    int obj;
};

Which declares a less than useful struct type.

Michael Burr
+1  A: 

One possible use of prototype scope is declaring a function which requires a pointer of type void * to be passed, for which passing other types results in a diagnostic message. If you prototype the function as:

int f(void *);

then passing any pointer type is valid. But if you prototype the function as:

int f(struct dummy *);

then there is no way to declare compatible pointers to pass for the function, and the function essentially requires a void * argument.

Whether this has any real-world usefulness, and whether it's beautiful or hideously ugly, are topics I'll leave for another day.

R..