C function definition/prototype [closed] - c

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
I'm new to learning the C programming language and this question is confusing for me because I thought the compiler will have an error.
Assuming a function is called before its definition/prototype occurs, which one of the following is false concerning its arguments?
A. All char types are converted to type int.
B. All short types are converted to type int.
C. All float types are converted to type double.
D. All types other than char, short, and float are passed unaltered.
E. Each argument is converted to the type of its corresponding formal parameter.

If you have a function call in C89 or C90 (same thing; one is ANSI C, t'other is ISO C), then:
The return type is assumed to be int.
Type char (and signed char and unsigned char) and short (and unsigned short) are converted to int (or, exceptionally, unsigned int if it was unsigned short and sizeof(unsigned short) == sizeof(unsigned int)), and float is converted to double.
Note that pointers are not changed.
The function is assumed not to be a varargs function. You must have a prototype in scope to call a varargs function such as printf() or scanf() correctly.
So, the answer is E.
The standard (current version, ISO/IEC 9899:2011) says:
6.5.2.2 Function calls
¶6 If the expression that denotes the called function has a type that does not include a
prototype, the integer 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 equal the number of parameters, the
behavior is undefined. If the function is defined with a type that includes a prototype, and
either the prototype ends with an ellipsis (, ...) or the types of the arguments after
promotion are not compatible with the types of the 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, except for the following cases:
one promoted type is a signed integer type, the other promoted type is the
corresponding unsigned integer type, and the value is representable in both types;
both types are pointers to qualified or unqualified versions of a character type or
void.
Note that getting it 'wrong' leads to undefined behaviour, and undefined behaviour should be avoided at all costs. The program could do anything — including erasing all the files on your computer — and that is acceptable according to the standard. Actually, programs seldom do that (and even more seldom cause 'demons to fly out of your nose', leading to the phrase 'nasal demons' (search for it), but that would also be acceptable in the face of undefined behaviour).
You should make it a policy that you never call a function without a prototype in scope if at all possible. All new code should meet that criterion; if you are unlucky enough to have to maintain ancient code that doesn't, you may have to go with the flow, but aim to get to the point where all your code can be compiled with options such as -Wmissing-prototypes -Wstrict-prototypes -Wold-style-declaration -Wold-style-definition -Werror in effect. Those are GCC options that report on problems with function prototypes and function definitions.

Thanks everyone!
I found the answer.
+All subintegers (char and short) are converted to type int or unsigned.
+All type float are converted to type double.
+All other types are unaltered.
So yeah the answer is E.
Thanks for the moral support!

See what happens when a function call is made if
1. Compiler has encountered a prototype prior to yhe call:
The value of each argument converted implicitly to the type of the corresponding parameters as if by assignment.
2. The compiler has not encountered a prototype prior to the call:
The compiler will perform default argument promotions: (1) float arguments are converted to double. (2) the integral promotions are performed, causing char and short arguments to be converted to int (In C99, integer promotions are performed).
So the answer is
E. Each argument is converted to the type of its corresponding formal parameter.
But keep in mind that relying on default promotions is dangerous. Consider this example:
#include <stdio.h>
int main(void)
{
double x = 3.0;
printf("Square: %d\n", square(x));
return 0;
}
int square(int n)
{
return n*n;
}
At the time square is called, the compiler hasn't seen a prototype yet, so it doesn't know that square expects an argument of type int. Instead, the compiler performs the default argument promotions on x, with no effect. Since it expecting an argument of type int but has been given a double value instead, the effect of calling square is undefined.

Related

Does C compiler automatically cast literals to parameter types when passing to functions?

I have a doubt regarding the casting of types in C. Is it safe to pass literals to function parameters(expecting different types but compatible types) without explicitly casting? For example here is an example code:
#include<stdio.h>
int sum(int a, int b){
return a + b ;
}
int main(void){
int x = sum(6U,5U) ; /* Does the compiler automatically casts unsigned to int?
or do I need to explicitly cast it sum( (int) 6U, (int) 5U) ?*/
printf("%d \n", x);
return 0;
}
The code runs fine but I wanted to know if this legal or can lead to undefined behaviours (assuming no overflows) ?
If an argument in a function call corresponds to a parameter with a declared type, the argument is converted to the parameter type, per C 2018 6.5.2.2 7.
If the argument does not correspond to a parameter with a declared type, the default argument promotions are performed. This occurs when the argument corresponds to the ... part of a function prototype, per C 2018 6.5.2.2 7, or when called function does not have a prototype, per 6.5.2.2 6.
The default argument promotions consist of the integer promotions (which largely promote integer types narrower than [technically of lower rank] int to int) and promote float to double.
Note that the applicable function type for these conversions is the type of the expression used to call the function. A function may be defined in one place with a prototype, such as int foo(char *a) { ... }, but declared in another place without a prototype, such as int foo();. The declaration visible at the function call is what determines which conversions will be performed. This is particularly so when a function is called via a pointer.
Note that these conversions are not casts. A cast is a specific operator in source code, a type name in parentheses, for which a conversion is performed. The cast, such as (int) in source code, is different from the operation, the same way a + in source code is different from the addition performed for it.

Can I call a function taking a long parameter with an int argument?

Is this code undefined behavior?
extern long f(long x);
long g(int x)
{
return f(x);
}
According to the C11 standard, in 6.5.2.2 §6:
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, the behavior is undefined.
In the example above, the function f is defined with a type that includes a prototype and the type of the argument x is int while the type of the parameter x is long. According to 6.2.7 §1:
Two types have compatible type if their types are the same.
Therefore, long and int are not compatible, so the behavior is undefined, right?
However, in 6.5.2.2 §7:
If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, taking the type of each parameter to be the unqualified version of its declared type.
If I correctly understand this paragraph, it means the argument x which is of type int is implicitly converted to long when the function is called. According to 6.3.1.3 §1:
When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.
As int has a lower rank than long, every int variable can be represented by a long variable. Therefore, the argument x can be converted into a long. Therefore, this is not undefined behavior.
Which interpretation of the standard is right? Is my code undefined behavior or not?
You provided irrelevant quotes relative to your code snippet. According to the same section (6.5.2.2 Function calls)
2 If the expression that denotes the called function has a type that
includes a prototype, the number of arguments shall agree with the
number of parameters. Each argument shall have a type such that its
value may be assigned to an object with the unqualified version of the
type of its corresponding parameter.
The function f has a prototype that is visible in the call expression
extern long f(long x);
and this assignment
int argument;
long parameter;
parameter = argument
is correct.
As for this quote
6 If the expression that denotes the called function has a type that
does not include a prototype, the integer 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 equal the number of parameters, the
behavior is undefined. If the function is defined with a type that
includes a prototype, and either the prototype ends with an ellipsis
(, ...) or the types of the arguments after promotion are not
compatible with the types of the 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, except for the following cases:
Then it means the following. The function calling expression does not see the function prototype. So the default argument promotions are performed. But somewhere else the function is defined with a function prototype and the promoted arguments are not compatible with function parameters. In this case you will have undefined behavior.
Here is a demonstrative program with undefined behavior related to a function call. The compiler can issue an error message.
#include <stdio.h>
void f();
int main(void)
{
short x = 10;
f( x );
return 0;
}
void f( char *s )
{
printf( "s = %s\n", s );
}
or
#include <stdio.h>
#include <limits.h>
void f();
int main(void)
{
unsigned int x = UINT_MAX;
f( x );
return 0;
}
void f( int x )
{
printf( "x = %hd\n", x );
}
For example in the last program the argument x in the call expression
f( x );
is promoted to the type unsigned int. But according to the function definition the function expects an argument of the type signed int and the passed value can not be stored in the type signed int. So the behavior is undefined.
But your original example of a function call is not related to this quote.
The part "the arguments after promotion" is confusing, it refers to the default argument promotions defined earlier on in that same paragraph. Which doesn't apply here, since those rules are only used when there is no prototype or when we have variadic functions.
So "arguments after promotion are not compatible with the types of the parameters" applies to cases where you don't have a prototype, apply the default argument promotions (integer promotion in case of integers) and if the types are not compatible then, there is undefind behavior.
But since you have a prototype, forget about default argument promotion, instead continue to read the next part, C17 6.5.2.2/7 emphasis mine:
If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, taking the type of each parameter to be the unqualified version of its declared type.
Then we go read what's said about "as if by assignment", C17 6.5.16 emphasis mine:
the left operand has atomic, qualified, or unqualified arithmetic type, and the right has arithmetic type;
Both int and long are arithmetic types (and there are no qualifiers), this is a valid form of assignment. Further down in the same chapter:
The type of an assignment expression is the type the left operand would have
after lvalue conversion.
So basically the code passing the parameter is equivalent to simple assignment:
int x;
long y;
y = x;
If we let the standard send us further on this merry chase, next look up lvalue conversion, C17 6.3.2.1:
...an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue); this is called lvalue conversion.
And then the actual conversion for integer types, C17 6.3.1.3:
When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.
Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or
subtracting one more than the maximum value that can be represented in the new type
until the value is in the range of the new type.
Otherwise, the new type is signed and the value cannot be represented in it; either the
result is implementation-defined or an implementation-defined signal is raised.
A long can always hold the value of an int, so the first sentence is the conversion that applies in this case.
Code is correct.
IMO, the first interpretation does not a apply.
It is actually referring to calling a function without a prototype that is defined with a prototype:
long g(int x)
{
return f(x);
}
// other translation unit
long f(long x) {
return 0;
}
The code is defined only if f is called with single argument of type compatible with long.
In a typical C implementation where compilation units are processed independently, and the linker combines the separately-compiled object files and performs address relocation without attempting other optimization, there will be a specification, which today would commonly be called the Application Binary Interface, which describes among other things where/how code which calls a function should store the arguments, and where/how a function should expect to find arguments stored by its caller.
If a function attempts to retrieve arguments in a manner inconsistent with how its caller had stored them, the results are not likely to be meaningful. On the other hand, many platform ABIs describe behavior in terms of storage formats, rather than C data types. Thus, on e.g. a 32-bit ARM implementation where both int and long are 32-bit data types, a function that expects an int would be called in exactly the same fashion as one that expects a long or an int32_t. An ARM implementation that processes the code in different compilation units independently would thus not need to care if one compilation unit uses type int and another uses long, provided that both types have the same representation.
In general, one should make parameter/argument types match when practical, even on implementations that wouldn't care about such things, because it will make it easier for people to read the code and know what it's doing. In some situations, however, one may have different compilation units that expect to be given pointers to functions whose arguments are of different types with matching representations. In such situations, if one is using an implementation that processes compilation units separately, it may be possible to pass the address of a single function to code in both compilation units. Unfortunately, there is no means by which a programmer can indicate when a function call needs to be handled in a fashion consistent with the ABI, without regard for whether the Standard would define its behavior, and some aggressive optimizers make no attempt to meaningfully process constructs whose behavior would be defined by the ABI but not the Standard.

I want default argument promotion's example

At C standard 6.5.2.2. paragraph 6
If the expression that denotes the called function has a type that does not include a prototype, the
integer promotions are performed on each argument, and arguments that have type float are
promoted to double. These are called the default argument promotions.
"the expression that denotes the called function has a type that does not include a prototype" What can this be described as code?
char a = 1;
printf("%d", a); /*"the expression that denotes the called function" is printf, and `a`
doesn't have prototype(char). but a can't be replaced as `char a` originally. ...? */
This is my thought that can be applicable to above standard. Is this right?
For historical reasons, functions in C can be declared the old way, without a prototype, as:
type function()
or the new way, with a prototype, as:
type function(type parameter)
type function(type parameter, type parameter)
type function(type parameter, type parameter, type parameter)
… and so on
Thus, a function declaration in which the types of the parameters are given is said to have a prototype. This affects preparation of the arguments when calling a function. With a prototype, the arguments are converted to the declared types of the parameters. Without a prototype, the arguments are converted to default types, using rules called the default argument promotions. (Also, if a function is just being declared, not defined, the names of the parameters can be omitted, leaving a prototype that just lists types, as in type function(type, type, type).)
Because of the old grammar, to declare a function with a prototype that says it has no parameters, you need to explicitly say void: type function(void). (This is different in C++, which does not have to support the old grammar. In C++, type function() is a prototype with no parameters.)
Additionally, a prototype can specify that there are variable arguments by putting ,... after one or more regular parameters:
type function(type parameter,...)
In this case, the first arguments are converted to the parameter types, but the arguments that correspond to the ... are converted using the default argument promotions. printf is declared this way.
The default argument promotions are:
Integers narrower (technically, of lesser rank) than int are promoted to int if it can represent all the values of the source type or unsigned int otherwise.
float arguments are converted to double.
There is also some finickiness about bit-fields in the default argument promotions, which I cannot say has ever arisen in code for me.
History
In the old C grammar, a function would be defined with:
type function(name0, name1, name2)
type name0;
type name1;
type name2;
and it would be declared without a prototype, as with type function(). This means the caller did not know the actual types of the parameters. But you could not just pass a char value for a char argument or a short value for a short argument, because C, in trying to be flexible so it could work on many types of computers, had rules about char and short values being promoted to int in expressions. Additionally, character constants like 'X' have type int, not char, so, if somebody called a function with foo('X'), the compiler would not know if foo really wanted just a char rather than an int. So the default argument promotions were made to match the integer promotions.
Later versions of C fixed this by providing a way to declare the argument types in declarations visible to the caller, so the arguments always match the parameters (and the compiler has more information it can use to provide error messages). But the old grammar still has to be supported so that old code can be compiled.
More
The phrase in the C standard, “the expression that denotes the called function,” is used because you can call functions through pointers, not just their names. For example, we can write:
int (*FunctionPointer)() = (int (*)()) printf;
and then call printf using FunctionPointer("Hello, world.\n");. Then “the expression that denotes the called function” is FunctionPointer, and it does not have a prototype even though printf does. (There is no good reason to do this with the printf function, but some esoteric code may do some unsavory things.)
You can initially declare a function without a prototype:
int foo();
and later add a prototype:
int foo(float x, char *y);
The compiler will merge the declarations, and the resulting foo will have a prototype.
The expression that denotes the called function is printf, right.
And if you don't provide a prototype for this function, for example by not including <stdio.h> and not declaring of printf() yourself, the compiler recognizes the function as prototype-less.
In your case the first argument is a const char *, and the second argument is an int.
So, it is not a that has no prototype, but printf().
The value of a will be promoted to int.
The assumed prototype will be int printf(); and takes any number and any type of parameters.

char type in va_arg

I have the following function which writes passed arguments to a binary file.
void writeFile(FILE *fp, const int numOfChars, ...)
{
va_list ap;
va_start(ap, numOfChars);
for(int i = 0; i < numOfChars; i++)
{
const char c = va_arg(ap, char);
putc(c, fp);
}
va_end(ap);
}
Upon compiling, I get the following warning from the compiler
warning: second argument to 'va_arg' is of promotable type 'char'; this va_arg
has undefined behavior because arguments will be promoted to 'int' [- Wvarargs]
Now as I understand it, C wants to promote char type to int. Why does C want to do this? Second, is the best solution to cast the int back to a char?
Now as I understand it, C wants to promote char type to int. Why does C want to do this?
Because that's what the standard says. If you pass an integral value with conversion rank smaller than that of int (e. g. char, bool or short) to a function taking a variable number of arguments, it will be converted to int. Presumably the reason for this has its roots in performance, where it was (and in fact, often it still is nowadays) better to pass values aligned to a machine word boundary.
Second, is the best solution to cast the int back to a char?
Yes, but you don't really need a cast even, an implicit conversion will do:
char ch = va_arg(ap, int);
Variadic functions are treated specially.
For a non-variadic function, the prototype (declaration) specifies the types of all the parameters. Parameters can be of any (non-array, non-function) type -- including types narrower than int.
For a variadic function, the compiler doesn't know the types of the parameters corresponding to the , .... For historical reasons, and to make the compiler's job easier, any corresponding arguments of types narrower than int are promoted to int or to unsigned int, and any arguments of type float are promoted to double. (This is why printf uses the same format specifiers for either float or double arguments.)
So a variadic function can't receive arguments of type char. You can call such a function with a char argument, but it will be promoted to int.
(In early versions of C, before prototypes were introduced, all functions behaved this way. Even C11 permits non-prototype declarations, in which narrow arguments are promoted to int, unsigned int, or double. But given the existence of prototypes, there's really no reason to write code that depends on such promotions -- except for the special case of variadic functions.)
Because of that, there's no point in having va_arg() accept char as the type argument.
But the language doesn't forbid such an invocation of va_arg(); in fact the section of the standard describing <stdarg.h> doesn't mention argument promotion. The rule is stated in the section on function calls, N1570 6.5.2.2 paragraph 7:
If the expression that denotes the called function has a type that
does include a prototype, the arguments are implicitly converted, as
if by assignment, to the types of the corresponding parameters, taking
the type of each parameter to be the unqualified version of its
declared type. The ellipsis notation in a function prototype
declarator causes argument type conversion to stop after the last
declared parameter. The default argument promotions are performed on
trailing arguments.
And the description of the va_arg() macro, 7.16.1.1, says (emphasis added):
If there is no actual next argument, or if type is not compatible with
the type of the actual next argument (as promoted according to the
default argument promotions), the behavior is undefined, except for
the following cases:
[SNIP]
The "default argument promotions" convert narrow arguments to int, unsigned int, or double. (An argument of an unsigned integer type whose maximum value exceeds INT_MAX will be promoted to unsigned int. It's theoretically possible for char to behave this way, but only in a very unusual implementation.)
Second, is the best solution to cast the int back to a char?
No, not in this case. Casts are rarely necessary; in most cases, implicit conversions can do the same job. In this particular case:
const char c = va_arg(ap, char);
putc(c, fp);
the first argument to putc is already of type int, so this is better written as:
const int c = va_arg(ap, int);
putc(c, fp);
putc internally converts its int argument value to unsigned char and writes it to fp.

C compiler warning error

A simple program
#include<stdio.h>
int main() {
char b='a';
printf("%s \n", b);
return 0;
}
Output:
test.c: In function ‘main’:
test.c:4:1: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
printf("%s \n", b);
^
But the second arg is char not int.
Is this a wrong warning by the compiler, or something else I am missing?
The compiler is certainly correct to warn about the call. printf with a "%s" specifier requires an argument of type char* (which must point to a string), and the argument you pass is not of that type.
As for why the warning message refers to int rather than char, it's because printf is a variadic function. The first parameter, the format string, is declared to be of type const char*, but the following arguments are specified only as , .... In this special case, arguments of integer types narrower than int are promoted to int or to unsigned int. So even though the expression b is of type char, the actual argument that's passed to printf is of type int; specifically, it's the result of converting the value of b from char to int.
The warning is accurate, though the reasons are modestly obscure.
The declaration of printf() is:
int printf(const char *restrict format, ...);
The const and restrict keywords aren't important for this discussion. What is important is the ellipsis, .... When arguments are passed to a function with a variable argument list (a variadic function), they undergo 'default argument conversions'. Integer types shorter than int (short and char in their various forms) are promoted to int, and float values are promoted to double. Thus, the value of b is converted to int by the rules for calling a variadic functions.
The "%s" format expects to be given a pointer to a null-terminated character string. The variable b is a single char, not a string. Consequently, the compiler is correctly warning you that you will not get good results from running the program.
In this context, using a format such as %c (to print a character) or %d (to print a decimal integer) is probably best:
printf("%c\n", b);
As a general rule, at this stage in your C programming career, you should assume the compiler is right and that you're wrong. Remember, the C compiler knows a lot more about C than you do. This isn't to say that there are never bugs in compilers. However, the chances of you finding one are slim. Until you know enough about C (maybe in five to ten years time), then you should assume the compiler is right, you're wrong, and work out (a) what the compiler means and (b) how to fix it.
It was not always thus. Thirty years ago, bad compilers existed because the best were not all that much better. It was possible to find bugs in those compilers. There has, however, been a serious winnowing and few incompetent compilers are left on the market. One area where you can sometimes find compilers with surprising limitations (occasionally tantamount to bugs) is in specialized embedded systems for obscure chips. However, in mainstream o/s for desktops and servers (and tablets and smart phones), you're unlikely to come across a seriously defective compiler.
ISO/IEC 9899:2011 §6.5.2.2 Function calls
¶6 If the expression that denotes the called function has a type that does not include a
prototype, the integer 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 equal the number of parameters, the
behavior is undefined. If the function is defined with a type that includes a prototype, and
either the prototype ends with an ellipsis (, ...) or the types of the arguments after
promotion are not compatible with the types of the 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, except for the following cases:
one promoted type is a signed integer type, the other promoted type is the
corresponding unsigned integer type, and the value is representable in both types;
both types are pointers to qualified or unqualified versions of a character type or
void.
The 'integer promotions' are defined in §6.3.1.8 Usual arithmetic conversions. They're more complex than I want to go through here.
a char is really just an 8 bit number, so a single char alone is no different than an int, except that it can only store smaller numbers. In your printf statement you have
%s
but a single char is not considered a string, just a number, so you should use
%c or %d
for you printf statement. If you had an array of chars, then you would use %s
Try this instead:
printf("%c \n", b);
The compiler is casting the char to int on the fly, so the error msg makes sense (at least to someone used to interpreting C compiler messages).

Resources