Kapteyn's Star said:
One small question. If fread() takes a void pointer then how does it know
that what I actually pass in an integer array? Will it figure it out from
the sizeof argument or is some magic going on??
There's no magic. fread() *doesn't* know that you've passed it an
integer array (and in fact you haven't; see below).
The first argument to fread() is normally the address of an array, or
of the first element of an array, which will be converted to void* (a
raw address). This tell fread() where in memory your object is, but
nothing else about it.
The second and third arguments are the size in bytes of each element
of your array, and the number of elements you want to read into. This
tells fread() how much data to read; it's the second and third
arguments multiplied together to yield the size in bytes of the array.
(So why not just use a single size argument? Because fread() reads
whole elements at a time. If you ask it to read 10 4-byte elements
from a file that only has 15 remaining byte, it will only set the
first 3 4-byte elements of your array; it won't touch the 4th element
of your array.)
And of course the fourth specifies the stream you want to read from.
I see what you say. But sizeof(type) is understandable when i look at it
but sizeof expression means i have to figure out the expresson and find
out to which type it belongs.
Not necessarily; the *compiler* has to figure that out. Typically the
expression is the name of an object. You usually need the size of
that object, whatever type it happens to be. And you don't
necessarily have to know the type of the object to understand why you
need its size.
some_type arr[N];
fread(arr, sizeof arr[0], N, some_file);
To know that the fread call's arguments are correct, you don't need to
know the type of arr, you just need to know that it's an N-element
array of *something*.
And as for "sizeof" without parentheses looking funny, think of it
this way. "sizeof" is an operator, not a function. It happens to be
the only operator in C whose symbol is a keyword (which looks like an
identifier, which looks like it could be a function name) rather than
one or more punctuation marks. Operators don't require parentheses on
their operands (you can write -x, you don't have to write -(x)). The
sizeof operator is no different; you can write sizeof x; you don't
have to write sizeof(x). Though you can add parentheses if you like,
and in some cases it might be necessary for disambiguation.
It's similar in this way to the return statement. You can write
"return x;" rather than "return(x);", because "return" isn't a
function. (It's not an operator either; it's a special form of
statement.) You can write "return(x);" if you really want to, but the
parentheses are just part of an ordinary parenthesized expression, not
part of the syntax of the return statement.
*But* there's another form of the "sizeof" operator, one whose operand
is a type name rather than an expression. This form of "sizeof" is
what's weird and exceptional. The type name is enclosed in
parentheses, but the parentheses aren't the same as the parentheses
you see on a function call; they're specified by an entirely different
syntax rule. And they're necessary to avoid ambiguity. You can think
of ``sizeof ( type-name )'' as a special distinct form of expression
-- and one that you really don't need to use very often. (The use of
parentheses on a cast is a similar case of surrounding a type name
used in an expression with parentheses to avoid ambiguity.)
I think i can understand... so arrays are not copied on the stack when we
give them as args to a function but a pointer to it is passed instead.
Almost, but not quite. I think it's time to bring out (drum roll)
The Rule.
You *can't* give an array as an argument to a function. You can try,
but it's just not possible in C. (Well, you can wrap the array in a
struct and pass the struct, but we'll ignore that; you can't do it
directly.)
Let's forget about functions, parameters, and arguments for a moment,
and just talk about arrays and pointers.
When you have an expression of array type (such as the name of an
array object), that expression is, *in most contexts*, immmediately
and implicitly converted to a pointer to (equivalently: the address
of) the first element of the array. The only exceptions to this are:
(1) when the array expression is the operand of a "sizeof" operator
(so "sizeof arr" gives you the size of the entire array, not the size
of a pointer); (2) when the array expression is the operand of a unary
"&" (so "&arr" gives you the address of the array, not of its first
element); and (3) when the array expression is a string literal used
in an initializer for an array object. The last case is slightly
obscure; it means that this:
char s[] = "hello";
initializes the array s with { 'h', 'e', 'l', 'l', 'o' '\0' }, not
with a pointer value.
So, when you call a function like:
given:
int arr[5];
func(arr);
the expression "arr" is of array type, and its context isn't one of
the three exceptions, so it's implicitly converted to the address of
arr[0], of type int*. This conversion has nothing to do with the fact
that it's a function argument; the same thing would happen in any
context other than the Big Three.
Here's an interesting point: The rules for multidimensional arrays are
a direct consequence of this. The standard needn't have mentioned
multidimensional arrays at all. They're implied by the rules for
one-dimensional arrays, and for array-to-pointer conversion. Given:
int array_2d[10][20] = { ... };
int elem = array_2d[5][6];
Figuring out how array_2d[5][6] resolves to an element of the 2d array
given the rules I've described is an interesting exercise.
Another rule (which, IMHO, just causes confusion) is that in a
parameter declaration, an array declaration is really a pointer
declaration. This isn't a run-time conversion, it's a compile-time
re-interpretation. So this:
void func(int arr[]);
really *means* this:
void func(int *arr);
The "[]" form can be used to document the fact that you expect to be
passing (the address of the first element of) an array to the
function. But I prefer the "*" form, since it says what it really
means.
An excellent resource for this kind of thing is section 6 of the
comp.lang.c FAQ said:
in func() *arr is the same as arr[0] and &arr == &arr[0]?
Assuming func's parameter was declared as "int *arr" or "int arr[]",
then yes, *arr is the same as arr[0]. But &arr and &arr[0] are not
the same thing. Remember that, inside func, arr is a pointer object
(a parameter); &arr is the address of that pointer object. &arr[0];
is the address of the first (zeroth?) element of the array to whose
first element arr points.
arr == &arr[0] is correct, though.
And remember that the address of an array and the address of its first
element are two different things. They both refer to the same
location in memory, but they're of different types.
[snip]