What is happening with these arrays in C pointer notation?
I'm a bit confused about pointer arrays and I just wanna make sure I'm right.
When I write int *arr
it is just a pointer to an int variable, not an array yet. It is only that I initialize it (say with malloc) that it becomes an array. Am I right so far?
Also I have another question: were given (in school) a little function that is supposed to return an array of grades, with the first cell being the average. The function was deliberately wrong: what they did was to set
int *grades = getAllGrades();
And than they have decreased the pointer by one for the average 'cell'
*(grades - 1) = getAverage();
return *(grades - 1)
I know this is wrong because the returned value is not an array, I just don't know how to explain it. When I set a pointer, how does the machine/compiler know if I want just a pointer or an array?
(If I'm not clear its because I'm trying to ask about something that is still vague for me, my 开发者_开发问答apologizes)
how does the machine/compiler know if I want just a pointer or an array?
It doesn't, and it never will. Suppose you
int *a = malloc(3 * sizeof(int));
You just allsocated 12 bytes (assuming int is 4). But malloc only sees 12. Is that 1 big object or lots of little ones? It doesn't know. The only one who actually knows is you ;)
Now about your particular example,
int *grades = getAllGrades();
At this point, as you said, there's nothing to say whether grades
points to an array. But you know it points to an array, and that's what's important. Or, maybe you know it doesn't point to an array. The key is you have to know what getAllGrades
does, to know if it's returning an array or a pointer to 1 thing.
*(grades - 1) = getAverage();
return *(grades - 1)
This is not necessarily wrong, but it does look kind of sketch. If it is an array, you would expect grades[0]
== *(grade + 0)
to be the first element, so grades[-1]
== *(grades - 1)
looks like it would be before the first element. Again, it's not necessarily wrong; maybe in getAllGrades
they did:
int* getAllGrades() {
int *grades = malloc(sizeof(int) * 10);
return grades + 1;
}
ie they scooched the start up by 1. It's been known to happen (look in Numerical Recipes in C) but it's kind of odd.
Arrays are not pointers. Pointers are not arrays.
Perhaps it would be clearer to say that array objects are not pointer objects, and vice versa.
When you declare int *arr
, arr
is a pointer object. That's all it is; it cannot be, and never will be, an array.
When you execute arr = malloc(10 * sizeof *arr);
, (if malloc()
doesn't fail, which you should always check), arr
now points to an int object. That object happens to be the first element of a 10-element array of int (the one created by the malloc
call). Note that there is such a thing as a pointer to an array, but this isn't it.
Arrays, in a very real sense, are not first-class types in C. You can create and manipulate array objects as you can with any other type of objects, but you'll rarely deal with array values directly. Instead, you'll deal with the elements of an array object indirectly, via pointers to those elements. And in the case of the arr
declaration above, you can perform arithmetic on the pointer to the first element to obtain pointers to the other elements (and you have to have some other mechanism to remember how many elements there are).
Any expression of array type, in most contexts, is implicitly converted to a pointer to the array's first element (the exceptions are: the operand of a unary &
operator, the operand of the sizeof
operator, and a string literal in an initializer used to initialize an array (sub)object). That's the rule that makes it seem as if arrays and pointers are interchangeable.
The array indexing operator []
is actually defined to work on pointers, not arrays. a[b]
is simply another way of writing *((a)+(b))
. If a
happens to be the name of an array object, it's first converted to a pointer, as I describe above.
I highly recommend reading section 6 of the comp.lang.c FAQ. (The link is to the front page, not directly to section 6, because I like to encourage people to browse the whole thing.)
I mentioned that there are array pointers. Given int foo[10];
, &foo[0]
is a pointer to an int, but &foo
is a pointer to the entire array. Both point to the same location in memory, but they're of different types, and they behave quite differently under pointer arithmetic.
When I write
int *arr
it is just a pointer to an int variable, not an array yet. It is only that I initialize it (say with malloc) that it becomes an array. Am I right so far?
Well, yes and no :). arr
is a pointer to (or the address of) some block of memory. Until arr
is initialized it probably points to an invalid, or non-sense memory address. So it may be confusing to think of arr
as a pointer to an int variable. For example, before the malloc, you can't store an integer in the location that it is pointing to.
Also, it may be easier to understand if you say that after the malloc
, arr
points to an array, it does not "become" an array. Before the malloc
, arr
points to some random non-sense location.
When I set a pointer, how does the machine/compiler know if I want just a pointer or an array?
If you set a pointer (e.g. arr = <something>
) you are just changing where the pointer points. That may be what you want. If don't want to change where arr
points but you want to change the values stored in the memory where it is pointing you have to do it one element at a time (e.g. with a for loop that iterates over each element in the array).
You are right, the only difference between arrays and pointers is convention. Here's a picture of what memory must look like when the getAllGrades()
function returns:
| secret malloc() stuff |
+-----------------------+
| average value |
+-----------------------+ ----\
grades* points here ---> | grade at index 0 | | by convention
+-----------------------+ | this stuff is
| grade at index 1 | | called grades[]
+-----------------------+ |
| grade at index 2 | |
+-----------------------+ .
| ... | .
Now, there is no difference between an array and a pointer. So, when the compiler sees *(grades - 1)
it first subtracts 1
from the grades pointer. This is special pointer arithmetic so it knows to go one whole int
block upwards, and points at the average value. Then it can operate on this value, for example to set the average with *(grades - 1) = getAverage()
.
An aside on array indexing: Array indexing gets compiled exactly like pointer arithmetic. For example, grades[2]
gets compiled down to *(grades + 2)
which does pointer arithmetic to move down 2 blocks to the memory address marked "grade at index 2" in my picture. This means you could change *(grades - 1) = getAverage()
to grades[-1] = getAverage()
and it would work exactly the same.
If you wanted to experiment you could do (-1)[grades]
which compiles down to *(-1 + grades)
and works as well, but that's stupid so don't do that :)
精彩评论