473,386 Members | 1,710 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,386 software developers and data experts.

Confusion in ANSI C's function concepts

hi all ,
It really seems that C never ceases to amaze . All this time
i've been doing C and i thought i was quite adept at it but i was
wrong . So without wasting any more time , here's the confusion .

I read in K&R that ANSI introduced the concept of function
prototyping in C and it was missing there initially ( it borrowed the
concept from C++ ) _but_ it did it make it compulsory to actually
include the function declaration in the program , the reason being -
so that older C code ( the ones missing the declarations ) could
run on newer compilers too . So the situation now is this - if there
is no function declaration corresponding to the function call and the
call does not say anything about the return type then the compiler
should assume a declaration with an int return type . for example ,
main()
{ fun(3) ;
return 0 ;
}
in this case the compiler assumes this declaration - int fun( the
standard says nothing about the parameters so thats implementation
dependent ) ;
and the code compiles without any hitch .

my question is what sort of declaration would the
compiler assume in the following case :
main()
{
void * p ;
p = fun(3) ;
}
in the above case the return type is mentioned as void * .
also what happens in this case :
main()
{
void * p ;
p = malloc(sizeof(int)) ;
return 0 ;
}
the above code also compiles and _executes_ successfully on
gcc .

few more regarding these concepts have cropped up lately in my mind
but i'll ask them as the thread proceeds .
Thanking in anticipation . Vaib .

Sep 24 '08 #1
8 2364
vaib <va************@gmail.comwrites:
my question is what sort of declaration would the
compiler assume in the following case :
main()
{
void * p ;
p = fun(3) ;
}
in the above case the return type is mentioned as void * .
The compiler assumes that the return type is "int". The compiler
always makes this assumption in the absence of a function
declaration.

Also, there is no reason that fun() would have to have a return
type of void * for the above to be valid. Its return type could
be pointer to any object or incomplete type.
--
char a[]="\n .CJacehknorstu";int putchar(int);int main(void){unsigned long b[]
={0x67dffdff,0x9aa9aa6a,0xa77ffda9,0x7da6aa6a,0xa6 7f6aaa,0xaa9aa9f6,0x11f6},*p
=b,i=24;for(;p+=!*p;*p/=4)switch(0[p]&3)case 0:{return 0;for(p--;i--;i--)case+
2:{i++;if(i)break;else default:continue;if(0)case 1:putchar(a[i&15]);break;}}}
Sep 24 '08 #2
vaib <va************@gmail.comwrites:
It really seems that C never ceases to amaze . All this time
i've been doing C and i thought i was quite adept at it but i was
wrong . So without wasting any more time , here's the confusion .

I read in K&R that ANSI introduced the concept of function
prototyping in C and it was missing there initially ( it borrowed the
concept from C++ ) _but_ it did it make it compulsory to actually
include the function declaration in the program , the reason being -
so that older C code ( the ones missing the declarations ) could
run on newer compilers too . So the situation now is this - if there
is no function declaration corresponding to the function call and the
call does not say anything about the return type then the compiler
should assume a declaration with an int return type .
For certain values of "now".

In the C89/C90 standard, which K&R2 describes, the situation is
basically what you've described. In the C99 standard, the "implicit
int" rule has been dropped, so the examples you show are illegal (more
precisely, they're constraint violations requiring diagnostics).
for example ,
main()
{ fun(3) ;
return 0 ;
}
Please use a more traditional code layout, even for short examples
like this.

main()
{
fun(3);
return 0;
}

In C90, the compiler will assume that fun is a function returning int
with a single parameter of type int (the latter because that's what
you passed it).
in this case the compiler assumes this declaration - int fun( the
standard says nothing about the parameters so thats implementation
dependent ) ;
No, it's not implementation dependent; the compiler's assumption about
the parameter types is based on the actual types of the arguments you
pass, after promotion. (For example, short is promoted to int and
float to double, so if you pass a float argument it will assume the
parameter is of type double.)
and the code compiles without any hitch .
Right -- but if you use C99 mode you'll at least get a warning.
You'll also probably get a warning on the declaration of main; use
"int main(void)" rather than "main()".
my question is what sort of declaration would the
compiler assume in the following case :
main()
{
void * p ;
p = fun(3) ;
}
in the above case the return type is mentioned as void * .
main()
{
void *p;
p = fun(3);
}

A C90 compiler will assume that fun takes one argument of type int and
returns a result of type int. In the absence of a visible
declaration, it *always* assumes a return type of int. There is no
implicit conversion from int to void*, so you should get at least a
warning even in C90 mode.
also what happens in this case :
main()
{
void * p ;
p = malloc(sizeof(int)) ;
return 0 ;
}
the above code also compiles and _executes_ successfully on
gcc .
main()
{
void *p;
p = malloc(sizeof(int));
return 0;
}

sizeof yields a result of type size_t, which is an alias (a typedef)
for some predefined unsigned integer type. Since sizeof is an
operator, not a function, the compiler knows this.

Let's assume size_t is unsigned long in the current implementation.
Then a C90 compiler will assume that malloc takes an argument of type
unsigned long (possibly declared as size_t, but that doesn't matter
for these purposes) and returns a result of type int. You then
attempt to assign this int result to an object of type void*, which is
a constraint violation requiring a diagnostic. gcc doesn't complain
about this by default, because with no options specified gcc is not a
conforming C90 compiler. Try invoking it with at least "-ansi
-pedantic", and possibly "-Wall -Wextra" in addition, and you'll get
at least one warning.

The best lesson to learn from this is not how compilers behave when
you write bad code like this, but how to write good code that the
compiler won't complain about. If you use a standard function,
*always* provide a #include directive for the appropriate header. If
you call your own function, *always* provide a prototype for it, not
just an old-style declaration, but a prototype that specifies the
types of the parameters. And if the compiler complains about a type
mismatch, adding a cast is almost never the right solution; more
often, it's like taping over a warning light on your car's dashboard.
few more regarding these concepts have cropped up lately in my mind
but i'll ask them as the thread proceeds .
--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Sep 24 '08 #3
Keith Thompson wrote:
[...]
main()
{
fun(3);
return 0;
}

In C90, the compiler will assume that fun is a function returning int
with a single parameter of type int (the latter because that's what
you passed it).
> in this case the compiler assumes this declaration - int fun( the
standard says nothing about the parameters so thats implementation
dependent ) ;

No, it's not implementation dependent; the compiler's assumption about
the parameter types is based on the actual types of the arguments you
pass, after promotion. (For example, short is promoted to int and
float to double, so if you pass a float argument it will assume the
parameter is of type double.)
[...]
It may be just a wording problem, but this doesn't seem quite right
to me. As I read it, you're saying that `fun(3)' implicitly declares
the function as `int fun(int)'. If that were so, then the fragment

fun(3); /* now we know `int fun(int)' */
fun(); /* ... or do we? */

.... would require a diagnostic, because the second call omits the
argument that the first call's implicit declaration specifies. But
in fact no diagnostic is required, hence the first call does not
actually declare fun's parameters.

Obviously, at least one of the calls must be erroneous -- but a
C90 compiler is not obliged to diagnose either of them.

--
Er*********@sun.com
Sep 24 '08 #4
Eric Sosman <Er*********@sun.comwrites:
Keith Thompson wrote:
>[...]
main()
{
fun(3);
return 0;
}
In C90, the compiler will assume that fun is a function returning int
with a single parameter of type int (the latter because that's what
you passed it).
>> in this case the compiler assumes this declaration - int fun( the
standard says nothing about the parameters so thats implementation
dependent ) ;
No, it's not implementation dependent; the compiler's assumption
about
the parameter types is based on the actual types of the arguments you
pass, after promotion. (For example, short is promoted to int and
float to double, so if you pass a float argument it will assume the
parameter is of type double.)
[...]

It may be just a wording problem, but this doesn't seem quite right
to me. As I read it, you're saying that `fun(3)' implicitly declares
the function as `int fun(int)'. If that were so, then the fragment

fun(3); /* now we know `int fun(int)' */
fun(); /* ... or do we? */

... would require a diagnostic, because the second call omits the
argument that the first call's implicit declaration specifies. But
in fact no diagnostic is required, hence the first call does not
actually declare fun's parameters.

Obviously, at least one of the calls must be erroneous -- but a
C90 compiler is not obliged to diagnose either of them.
That's exactly right. In C90, if you're not using prototypes (or in
pre-ANSI C where you couldn't use prototypes), getting the argument
types right in a function call is entirely up to the programmer. If
you get it wrong, the behavior is undefined; no diagnostic is
required.

A sufficiently clever compiler could warn about the fact that it's
seen two incompatible implicit declarations for the same function, but
it's not required to do so.

A call to a unprototyped function doesn't create an implicit
declaration that remains visible after the call. The compiler
generates code to throw the argument values in the general direction
of the function; if they don't land in the right place, that's the
programmer's problem.

Remember that in pre-ANSI C, there was nothing special about printf;
it was just another function. <varargs.h>, the predecessor of
<stdarg.h>, was available, but it didn't affect the *declaration* of
printf. If the compiler couldn't complain about
printf("hello\n");
printf("x = %d\n", x);
then it would have no basis for complaining about
fun(3);
fun();

(In one of my first C programs, written about 10 years before the ANSI
standard, I made a mistake and passed the wrong number of arguments to
a function. The compiler didn't complain. When I realized what was
going on, I was *horrified*. Most of my previous programming had been
in Pascal.)

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Sep 24 '08 #5
Keith Thompson wrote:
[... concerning `fun(3)' without a declaration ...]
A call to a unprototyped function doesn't create an implicit
declaration that remains visible after the call. [...]
Are you sure of that? I don't have a C90 at hand for study,
and of course in C99 the rules changed.

I argued earlier that `fun(3)' did not magically declare
`int fun(int)', which your earlier post seemed to imply (though
as I said, it might just have been a misreading). However, all
the compilers I've seen have behaved as if a different declaration
did in fact appear out of thin air: `int fun()'. Specifically,

int main(void) {
fun(3);
return 0;
}

double fun(int x) { return x * 0.5; }

.... generates a diagnostic indicating that the definition of fun()
conflicts with a prior declaration. So if this isn't just a matter
of compilers trying to be helpful and informative, it means that
the implicit declaration has "staying power" beyond the immediate
context.

--
Er*********@sun.com
Sep 25 '08 #6
On Thu, 25 Sep 2008 11:15:07 -0400, Eric Sosman wrote:
Keith Thompson wrote:
>[... concerning `fun(3)' without a declaration ...] A call to a
unprototyped function doesn't create an implicit declaration that
remains visible after the call. [...]

Are you sure of that? I don't have a C90 at hand for study,
and of course in C99 the rules changed.

I argued earlier that `fun(3)' did not magically declare
`int fun(int)', which your earlier post seemed to imply (though as I
said, it might just have been a misreading). However, all the compilers
I've seen have behaved as if a different declaration did in fact appear
out of thin air: `int fun()'. Specifically,

int main(void) {
fun(3);
return 0;
}

double fun(int x) { return x * 0.5; }

... generates a diagnostic indicating that the definition of fun()
conflicts with a prior declaration. So if this isn't just a matter of
compilers trying to be helpful and informative, it means that the
implicit declaration has "staying power" beyond the immediate context.
The implicit declaration does remain, but your example doesn't show it.
The declaration of fun has block scope, so it's not visible when the
definition of fun is encountered. Your example is one of compilers trying
to be helpful and informative. But, consider this program:

int main(void) {
int (*fp)();
fp = fun; /* #1 */
{ fun(); }
fp = fun; /* #2 */
fun();
fp = fun; /* #3 */
return 0;
}

the assignments marked #1 and #2 are invalid, while the assignment marked
#3 is valid. #1 is invalid because fun is undeclared, but #2 is invalid
for the same reason. #3, on the other hand, is valid, because the implicit
declaration of function fun remains visible for the rest of the block.
Sep 25 '08 #7
Eric Sosman <Er*********@sun.comwrites:
Keith Thompson wrote:
>[... concerning `fun(3)' without a declaration ...]
A call to a unprototyped function doesn't create an implicit
declaration that remains visible after the call. [...]

Are you sure of that? I don't have a C90 at hand for study,
and of course in C99 the rules changed.

I argued earlier that `fun(3)' did not magically declare
`int fun(int)', which your earlier post seemed to imply (though
as I said, it might just have been a misreading). However, all
the compilers I've seen have behaved as if a different declaration
did in fact appear out of thin air: `int fun()'. Specifically,

int main(void) {
fun(3);
return 0;
}

double fun(int x) { return x * 0.5; }

... generates a diagnostic indicating that the definition of fun()
conflicts with a prior declaration. So if this isn't just a matter
of compilers trying to be helpful and informative, it means that
the implicit declaration has "staying power" beyond the immediate
context.
Assuming a C90-conforming compiler, it's just being helpful; the
diagnostic is not required.

But now that I've actually checked the C90 standard, I see that my
description was a bit off (though I think the effect is about the
same).

C90 6.3.2.2:

If the expression that precedes the parenthesized argument list in
a function call consists solely of an identifier, and if no
declaration is visible for this identifier, the identifier is
implicitly declared exactly as if, in the innermost block
containing the function call, the declaration

extern int identifier ();

appeared.

with a footnote:

That is, an identifier with block scope declared to have external
linkage with type function without parameter information and
returning an int. If in fact it is not defined as having type
``function returning int'', the behavior is undefined.

So there is an implicit declaration, but it's not visible outside the
innermost block containing the call, and it provides no parameter
information.

Later in that same section in C90:

If the expression that denotes the called function has a type that
does not include a prototype, the integral promotions are
performed on each argument and arguments that have type float are
promoted to double. These are called the _default argument
promotions_. If the number of arguments does not agree with the
number of parameters, the behavior is undefined. If the function
is defined with a type that does not include a prototype, and the
types of the arguments after promotion are not compatible with
those of the parameters after promotion, the behavior is
undefined. If the function is defined with a type that includes a
prototype, and the types of the arguments after promotion are not
compatible with the types of the parameters, or if the prototype
ends with an ellipsis (,... ), the behavior is undefined.

Even without implicit declations, non-prototype function declarations
can cause similar problems. For example, this requires no diagnostic,
even in C99, though it's certain to invoke undefined behavior:

int main(void)
{
extern void func();
func();
func(42);
func("hello");
return 0;
}

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Sep 25 '08 #8
On Wed, 24 Sep 2008 15:23:03 -0400, Eric Sosman <Er*********@sun.com>
wrote:
vaib wrote:
<snip much good>
{
void * p ;
p = malloc(sizeof(int)) ;
return 0 ;
}
the above code also compiles and _executes_ successfully on
gcc .

It shouldn't. Since malloc() has not been declared, it is
assumed to return an int and the compiler should object to your
attempt to convert that int to a pointer. A diagnostic is required;
I speculate that you are running gcc in some kind of lenient mode.
Depends on quite what you mean by 'should[n't]'.

A diagnostic is required, but need be no more than a warning. If you
ignore or suppress (or leave suppressed) the warning it's Undefined
Behavior; the compiler can proceed to generate code to do whatever it
likes. On many machines (and implementations) where 'int' and 'void*'
are both a word, and use the same return mechanism, an easy thing to
do is just use the assumed-int-actually-voidptr return value, and it
does work. This is not only permitted, but arguably reasonable.
But it is not required, and (thus) should not be relied on.

And as both you and Keith noted (but I snipped) you should request and
at least think about diagnostics. Even when you aren't forced to.

And nearby on Wed, 24 Sep 2008 12:58:28 -0700, Keith Thompson
<ks***@mib.orgwrote in <ln************@nuthaus.mib.org>:
vaib <va************@gmail.comwrites:
{
fun(3);
return 0;
}

In C90, the compiler will assume that fun is a function returning int
with a single parameter of type int (the latter because that's what
you passed it).
in this case the compiler assumes this declaration - int fun( the
standard says nothing about the parameters so thats implementation
dependent ) ;

No, it's not implementation dependent; the compiler's assumption about
the parameter types is based on the actual types of the arguments you
pass, after promotion. (For example, short is promoted to int and
float to double, so if you pass a float argument it will assume the
parameter is of type double.)
It's not impl _defined_, but it can be impl _dependent_; the default
argument promotions for narrow unsigned integer types depends on the
ranges, (mostly) chosen by the impl, for several of the integer types.

<snip other points>

- formerly david.thompson1 || achar(64) || worldnet.att.net
Oct 6 '08 #9

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

12
by: harry | last post by:
I have an object that's passed in to a function as a parameter i.e public boolean getProjectTitle(ProjectHeader_DTO obj) {...} If I then call a method on this object inside the function i.e...
38
by: Grant Edwards | last post by:
In an interview at http://acmqueue.com/modules.php?name=Content&pa=showpage&pid=273 Alan Kay said something I really liked, and I think it applies equally well to Python as well as the languages...
0
by: Eric Myers | last post by:
Hello folks: (This message is also posted on the help forum at the pexpect sourceforge page, but all indentation in the code got stripped away when I submitted the post.) For some time I've...
22
by: Canonical Latin | last post by:
#include<iostream> int main() { char buff; std::cin.getline(buff,3); std::cin.getline(buff,3); std::cout << buff << endl; } Run at command prompt and input 1234567 what do you get as output?
100
by: Roose | last post by:
Just to make a tangential point here, in case anyone new to C doesn't understand what all these flame wars are about. Shorthand title: "My boss would fire me if I wrote 100% ANSI C code" We...
5
by: celsius | last post by:
Hi all, please forgive me if this already posted many times. i was reading peter van der linden's book expert C programming. on page number 188,he is discussing about implementing finite state...
39
by: Martin Jørgensen | last post by:
Hi, I'm relatively new with C-programming and even though I've read about pointers and arrays many times, it's a topic that is a little confusing to me - at least at this moment: ---- 1)...
47
by: Thierry Chappuis | last post by:
Hi, I'm interested in techniques used to program in an object-oriented way using the C ANSI language. I'm studying the GObject library and Laurent Deniau's OOPC framework published on his web...
11
by: blangela | last post by:
I am teaching a C programming course to C++ programmers and want to come up with list of _KEY_ differences between the two languages. Below is a list I have come up with so far. Have I missed any?...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.