I don't know how everyone else was able to type their answers quicker
that me, but my answer is not completely redundant with theirs, so I'll
send it anyway.
What's exactly is happening in the following example?
/*******************/
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a;
a = puts;
"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’’." (6.3.2.1p4)
"Any pointer type may be converted to an integer type. Except as
previously specified, the result is implementation-defined. If the
result cannot be represented in the integer type, the behavior is
undefined. The result need not be in the range of values of any integer
type." (6.3.2.3p6)
While that conversion is allowed, it is not one the implementation is
required to perform implicitly. You have to request it explicitly,
otherwise you run afoul of the constraints that apply to simple
assignments (6.5.16.1p1). A conforming implementation of C is required
to generate a diagnostic message - if yours didn't complain about that
line, you need to either increase the warning levels, or change to use a
better compiler. If, after generating the message, your compiler chooses
to accept your code anyway, and if you choose to execute the compiled
program, the behavior is undefined.
Even if you had inserted an explicit cast to 'int', you've given us no
particular reason to expect that the implementation-defined result of
converting a pointer to puts to an integer results in a value that can
be represented as an 'int', so the behavior of your code might be
undefined for that reason. too. Either way, there's no restrictions on
how your program is allowed to behave.
printf("%d\n", a); /* outputs 4201472 here */
a = &puts;
The implicit conversion of a function designator into a pointer to the
function, described in 6.3.2.1p4 which I quoted above, is explicitly
described as not occurring when the function designator is the operand
of a unary & operator. Therefore, &puts also results in a pointer to
puts, rather than a pointer to a pointer to puts. This produces the
counter-intuitive result that &puts and puts have the same type and
equivalent values.
printf("%d\n", a); /* outputs 4201472 here */
a = *puts;
In the expression *puts, the conversion of puts to a pointer DOES occur.
The result of applying the unary * operator is itself a function
designator; as such, it too gets implicitly converted into a pointer to
puts. The net result is REALLY counter-intuitive: *puts == &puts.
The C Rationale has this to say about it:
The treatment of function designators can lead to some curious, but valid, syntactic forms.
Given the declarations
int f(), (*pf)();
then all of the following expressions are valid function calls:
(&f)(); f(); (*f)(); (**f)(); (***f)();
pf(); (*pf)(); (**pf)(); (***pf)();
The first expression on each line was discussed in the previous paragraph. The second is
conventional usage. All subsequent expressions take advantage of the implicit conversion of a
function designator to a pointer value, in nearly all expression contexts. The C89 Committee
saw no real harm in allowing these forms; outlawing forms like (*f)(), while still permitting
*a for a[], simply seemed more trouble than it was worth.
Getting back to your message:
printf("%d\n", a); /* outputs 4201472 here */
memcpy(&a, puts, sizeof(int));
This behavior of memcpy() is defined only when both of it's first two
arguments point at objects. The expression "puts" points at a function,
so the behavior of this code is undefined. It could print out 16308783,
or 4201472, or -13.45 or "You don't understand the distinction between
objects and functions in C".
printf("%d\n", a); /* outputs 16308783 here */
memcpy(&a, &puts, sizeof(int));
printf("%d\n", a); /* outputs 16308783 here */
memcpy(&a, *puts, sizeof(int));
printf("%d\n", a); /* outputs 16308783 here */
Because of the implicit conversions of function designators to function
pointers, those memcpy() calls have behavior that is just as undefined
as the first one, for precisely the same reason.
return 0;
}
/************************/
- I was hoping at least one of the memcpy lines was equivalent to at least one of the assignments lines.
- The first 3 (as well as the last 3) being equivalent is kind of contra intuitive to me.
- How can I make a memcpy to &a, which is equivalent to "Make 'a' store the number corresponding to the address 'puts' resides on"?
There need not be any such address. There need not be any such number.
On implementations where such a number does exist, the expression
(intptr_t)puts is the one that is generically most likely to have that
value. On other implementations, that same expression could have
undefined behavior, so this should only be done in code that's meant to
be used only with that particular implementation.
On a implementation where (intptr_t)puts does generate the value you
want, you can't put that value into 'a' directly using memcpy(). You'd
first have to use assignment to put the value into a different object;
then you can use memcpy() to copy it from that other object into 'a':
intptr_t b = (intptr_t)puts; // Warning: possibly undefined
intptr_t a;
memcpy(&a, &b, sizeof a);
Are you sure you want to use memcpy()? Assignment is simpler.