I was reading this excerpt from the GNU C manual:
You use the comma operator, to separate two (ostensibly related) expressions.
Later in the description:
If you want to use the comma operator in a function argument, you need
to put parentheses around it. That’s because commas in a function
argument list have a different meaning: they separate arguments.
Until now, everything is alright. The weird part is:
foo (x, (y=47, x), z); is a function call with just three
arguments. (The second argument is (y=47, x) .)
The question is: how is the parameter pushed on the stack, how do I access it from within the function?
In your case,
foo (x, (y=47, x), z);
is functionally similar as
foo (x, x, z);
As per the property of comma operator, the LHS operand is evaluated and the result is discarded, then the RHS operand is evaluated and that's the result.
For sake of completion, quoting the C11, chapter §6.5.17
The left operand of a comma operator is evaluated as a void expression; there is a
sequence point between its evaluation and that of the right operand. Then the right
operand is evaluated; the result has its type and value.
Point to note: the variable y will be updated, as the LHS operand is evaluated as a void expression, but has no effect on this funcion call. In case, the y is a global variable and used in foo() function, it will see an initial value of 47.
That said, to answer
how is the parameter pushed on the stack
is very very implementation (architecture) dependent. C does not specify any order for function argument passing and some architecture may event not use "stack" for function argument passing, at all!!
Related
It's clear from the C standard that general function calls are expressions from the definition:
An expression is a sequence of operators and operands that specifies computation of a value, or that designates an object or a function, or that generates side effects, or that performs a combination thereof. The value computations of the operands of an operator are sequenced before the value computation of the result of the operator.
(6.5.1)
Since the () are operators and it returns a value, regular function calls are obviously expressions.
But those which don't return a value don't seem to fit with this definition. The function name itself does (as it designates a function), but this isn't a function call.
The standard does clearly say that a function call is an expression, and that it can return void, but this seems to conflict with the definition of an expression. What am I missing?
Calling a function is an expression regardless of the function's return type. C's grammar is orthogonal to its type system. They are independent pieces of the language. Grammatically func(); is an expression statement.
expression_statement
: ';'
| expression ';'
;
postfix_expression
: primary_expression
| postfix_expression '[' expression ']'
| postfix_expression '(' ')'
| postfix_expression '(' argument_expression_list ')'
There are very few things you can do with a void result. You can't assign it to a variable since void variables aren't allowed. If func()'s result is void you can use four operators:
Parentheses: (func())
Comma sequencing: func(), 42
Ternary operator: 42 ? func() : func().
Cast to void: (void) func()
You can also return a void result:
return func();
Finally, in a for(init; condition; increment) loop the three pieces are all expressions. init and increment (but not condition) can be void.
for (func(); 42; func()) { }
Few of these are useful and none are good style, but they're all legal.
Paragraph 1 of clause 6.5 was not completely thought out with regard to void. The C standard is imperfect and has a number of defects. This paragraph should be received as a general description to orient readers and is not a precise mathematical specification of what an expression is.
It is said that:
An expression is a sequence of operators and operands that
specifies computation of a value, or
that designates an object or a function or
that generates side effects
or that performs a combination thereof.
The specifies computation of a value is but one among possibilities. The void function call would be the one "that generates side effects".
Any expression in the expression statement in C is considered a void expression. C11 6.8.3 Expression and null statements p2:
The expression in an expression statement is evaluated as a void expression for its side effects.153)
153) Such as assignments, and function calls which have side effects.
i.e. in the expression statement
a = 5;
a = 5 is a void expression that is evaluated for its side effects only, i.e. the assignment of value 5 into a, not for computation of a value, even though a = 5 could be used for a computation of a value in other contexts. Likewise you can write a; and it is a legal use of an expression "evaluated for its side effects", even though it has none. It does not cease to be an expression there.
The LHS of a comma operator is a void expression. A void expression can be used in ? : - then both branches will be void expressions and the entire expression in itself will be a void expression.
An expression in C can be void.
Such an expression has not a value and then it cannot be assigned to an object.
Moreover, any expression can be cast to void.
I am learning C so I tried the below code and am getting an output of 7,6 instead of 6,7. Why?
#include <stdio.h>
int f1(int);
void main()
{
int b = 5;
printf("%d,%d", f1(b), f1(b));
}
int f1(int b)
{
static int n = 5;
n++;
return n;
}
The order of the evaluation of the function arguments is unspecified in C. (Note there's no undefined behaviour here; the arguments are not allowed to be evaluated concurrently for example.)
Typically the evaluation of the arguments is either from right to left, or from left to right.
As a rule of thumb don't call the same function twice in a function parameter list if that function has side-effects (as it does in your case), or if you pass the same parameter twice which allows something in the calling site to be modified (e.g. passing a pointer).
https://en.cppreference.com/w/c/language/eval_order
Before C11, you must follow Rule (2)
There is a sequence point after evaluation of the first (left) operand and
before evaluation of the second (right) operand of the following binary
operators: && (logical AND), || (logical OR), and , (comma).
Because arguments are considered separated by comma operator before C11. This is not optimal because arguments are pushed right to left on some platform. Thus, C11 adds Rule (12) making it unspecified.
A function call that is not sequenced before or sequenced after another
function call is indeterminately sequenced (CPU instructions that
constitute different function calls cannot be interleaved, even if the
functions are inlined)
Even C99 designated initializers still go back to Rule (2), where earlier (left) initializers are resolved before later (right) initializers relative to the comma operator. That is, until C11 adds Rule (13) making it unspecified.
In initialization list expressions, all evaluations are indeterminately
sequenced
In other words, before Rule (12) and Rule (13), the comma operator from Rule (2) is the specified behavior. Rule (2) leads to inefficient code that cannot be optimized on some platform. There is not enough registers if the number of structure member or function parameter exceed some threshold. That is, "Register Pressure" becomes an issue.
Historically, aggregate type initializers and function arguments falls back to the comma operator. In C11, they specifically add the definition that commas in those aggregate type initializers and function arguments are not "comma operators" so that Rule (12) and Rule (13) makes sense, and that Rule (2) is not applied.
I'm relatively new to C, and found it intriguing that both of the following calls to the function pointer compile and work fine. One with and one without dereferencing the function pointer before calling it.
#include <stdio.h>
#include <stdlib.h>
void func() {
puts("I'm a func");
}
int main(void) {
void (*f)() = func;
f();
(*f)();
return EXIT_SUCCESS;
}
I think I understand that (*f)() is the "official" way to call a function pointer, but why does simply calling f() work? Is that syntactic sugar of recent C versions?
This is a piece of syntactic/semantic sugar that has, AFAIK, worked since the very earliest versions of C. It makes sense if you think of functions as pointers to code.
The only special rule needed to make function pointers work this way is that indirecting a function pointer gives the same pointer back (because you can't manipulate code in standard C anyway): when f is a function pointer, then f == (*f) == (**f), etc.
(Aside: watch out with declaration such as void (*f)(). An empty argument list denotes an old-style, pre-C89 function declaration that matches on the return type only. Prefer void (*f)(void) for type safety.)
A function call expression is always of the form "function pointer", "round parenthesis", "arguments", "round parenthesis". In order for you not to have to spell out (&printf)("Hello World\n") every time1, there is a separate rule by which an expression which denotes a function decays to the respective function pointer.
Since a function pointer can be dereferenced to give an expression that denotes a function again, this will again decay, so you can keep dereferencing and there'll be a lot of decay:
(&f)(); // no decay (decay does not happen when the expression
// is the operand of &)
f(); // one decay
(*f)(); // two decays
(**f)(); // three decays
1) Early Perl has function syntax like that.
The C 2011 standard says in clause 6.3.2.1, paragraph 4:
Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, a function designator with type ‘‘function returning type’’ is converted to an expression that has type ‘‘pointer to function returning type’’.
This means that, if f designates a function, then, in a call such as f(), f is automatically converted to &f, resulting in (&f)(). This is actually the “proper” way to call a function, because the function-call expression requires a pointer to a function.
Now consider what happens in *f. The f is automatically converted to &f, so we have *&f. In this expression, the result of the * operator is a function, f; it just reverse the operation performed by &. So *&f is the same as f. We can repeat this indefinitely: **f is automatically converted to **&f, which is *f, which is automatically converted to *&f, which is f, which is automatically converted to &f.
In C you can call your function like:
f();
(*f)();
(**f)();
(********f)();
(*****************************f)();
all are valid. In C, dereferencing or taking the address of a function just evaluates to a pointer to that function, and dereferencing a function pointer just evaluates back to the function pointer. C is designed in such a way that both function name identifier as well as variable holding function's pointer mean the same: address to CODE memory. And it allows to jump to that memory by using call () syntax either on an identifier or variable.
And the last but but least, standard says that:
C11: 6.3.2.1:
4 A function designator is an expression that has function type. Except when it is the
operand of the sizeof operator, the _Alignof operator,65) or the unary & operator, a
function designator with type ‘‘function returning type’’ is converted to an expression that has type ‘‘pointer to function returning type’’.
With respect to the return statement, the Microsoft Visual Studio documentation says
Syntax:
return expression;
where expression is marked as optional. It continues
The value of expression, if present, is returned to the calling function. If expression is
omitted, the return value of the function is undefined.
This is quite clear, but on the other hand, there is the notion of an empty expression. This makes me confuse. Thinking of the empty expression not as nothing, but as an expression which is empty, I would think that if we have a function
void foo(void)
{
return;
}
then the expression foo() could be used wherever the empty expression is allowed. For example, the behaviour of the code
unsigned int i=0;
for(foo();i<10;i++) printf("%u",i);
would be defined.
I am aware that this is probably of little practical relevance, but I would like to understand why, in this context, the empty expression is not an expression.
It's called void expression. And you can use a void expression in for like you did. The void expression does nothing but its side effect, which is calling the function.
In fact, if the first clause of for is any type of expression, it's evaluated as a void expression:
C99 6.8.5.3 The for statement
... If clause-1 is an expression, it is evaluated as a void expression before the first evaluation of the controlling expression.
and
C99 6.3.2.2 void
The (nonexistent) value of a void expression(an expression that has type void) shall not
be used in any way, and implicit or explicit conversions (except to void) shall not be
applied to such an expression. If an expression of any other type is evaluated as a void
expression, its value or designator is discarded. (A void expression is evaluated for its
side effects.)
I don't know if there actually is such a thing in C as "the empty expression", but if there is, then you are confusing it with the type void, and at the same time you are confusing the act of returning "nothing" from a non-void function (which is illegal) with leaving out the initializer expression of a for loop (which is legal).
Suppose there is a function pointer:
void func(float a1, float a2) {
}
void (*fptr)(float, float) = &func;
Is there any difference between these two lines (both compile and work on my compiler)?
(*fptr)(1,2);
fptr(1,2);
I suppose that the second version is just a shortcut of the first one, but want to ensure myself. And more important is it a standard behavior?
They do the same thing.
The prefix of a function call is always an expression of pointer-to-function type.
An expression of pointer type, such as the name of a declared function. is implicitly converted to a pointer to that function, unless it's the operand of a unary "&" (which yields the function's address anyway) or of sizeof (which is illegal rather than yielding the size of a pointer).
The consequence of this rule is that all of these:
&func
func
*func
**func
are equivalent. They all evaluate to the address of the function, and they can all (if suitably parenthesized) be used as the prefix of a function call.
Yes, it is standard behavior and will work in all C compilers.
Function calls are actually always made through a pointer to function:
6.5.22 "Function calls":
The expression that denotes the called function (footnote 77) shall have type pointer to function returning void or returning an object type other than an array type
As footnote 77 indicates, a 'normal' function identifier is actually converted to a pointer-to-function when a function call is expression is used:
Footnote 77:
Most often, this is the result of converting an identifier that is a function designator.
Interestingly, dereferencing a function pointer (or a function designator converted to a function pointer) yields a function designator for the function, which will be converted back to a function pointer for the function call expression. So you can dereference the function pointer (or a normal function name), but don't have to.
6.5.3.2/4 "Address and indirection operators":
The unary * operator denotes indirection. If the operand points to a function, the result is a function designator
So as far as the standard is concerned, for your examples:
(*fptr)(1,2);
fptr(1,2);
The second isn't really a shorthand - it's what the compiler expects. Actually, the first example is really just a more verbose way of saying the same thing as the second. But some people might prefer the first form as it makes it more clear that a pointer is being used. There should be absolutely no difference in the generated code.
Another consequence of the standard's wording is that you can just as well dereference a normal function name:
func(1,2);
(*func)(1,2); // equivalent to the above
Not that there's really any defensible point to doing that that I can think of.