Similarities and differences between arrays and pointers through a practical example
Given the following code:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a[1];
int * b = malloc(sizeof(int));
/* 1 */
scanf("%d", &a);
printf("%d\n", a[0]);
/* 2 */
开发者_Go百科 scanf("%d", &b);
printf("%d\n", b[0]);
return 0;
}
the following warnings are obtained when it is compiled (i686-apple-darwin9-gcc-4.0.1):
array.c: In function 'main':
array.c:9: warning: format '%d' expects type 'int *', but argument 2 has type 'int (*)[0u]'
array.c:14: warning: format '%d' expects type 'int *', but argument 2 has type 'int **'
but, why does an execution-error occur in the second printf, meanwhile it works for the first printf?
Even more, why it is obtained the same output if the first scanf is substituted by scanf("%d", a);?
Thank very much in advance
In most contexts, an expression of array type will be implicitly converted from an "N-element array of T" to "pointer to T" and its value will be set to point to the first element of the array. The exceptions to this rule are when the array is an operand of the &
or sizeof
operators, or if the array is a string literal being used to initialize another array in a declaration.
So how does all that relate to your code?
In the line
scanf("%d", &a);
You are applying the &
operator to the array. This suppresses the implicit conversion from "array of T" to "pointer to T" and returns a value of type "pointer to array of T", or T (*)[N]
(hence your first warning). Now it turns out that the value of a pointer to an array and the value of a pointer to the first element of the array are the same, they just have different types. So assuming that a
is at address 0x0001000:
expression type value note
---------- ---- ----- ----
a int * 0x0001000 implicitly converted to pointer
&a int (*)[N] 0x0001000
&a[0] int * 0x0001000
That's why your first call to scanf()
"works"; you're passing the right pointer value, but the compiler is complaining because the type of the expression doesn't match what the function expects. Had you written
scanf("%d", a);
you would not have received any warnings, since the type of a
will be taken to be int *
, which is what scanf()
expects. Note that this is identical to calling
scanf("%d", &a[0]);
As for b
...
You explicitly declare b
as a pointer to int and assign a block of memory to it. When you apply the &
operator to it, what you get back is the address of the variable b
with type int **
(hence the second warning), not the address that b
points to.
expression type value note
---------- ---- ----- ----
b int * 0x0040000 value contained in b
&b int ** 0x0001000 address of b
For that case, you just pass the undecorated b
:
scanf("%d", b);
The array a is placed on the stack, the address for the first element is the same as the address of a. &a[0] and &a is the same address
The array b is allocated with malloc, the address for the storage is on the heap, whereas the address for the pointer b is on the stack. &b[0] is not the same address as &b.
That's why the first scanf and printf works but not the second.
The C-FAQ explains it in much greater detail.
In the first scanf you pass a reference to an array. In C arrays are pointers to a memory block of the allocated type, in your case int *
and an expression like a[0]
gets translated into *(a + 0)
(which btw gives rise to the funny variant 0[a]
which will actually compile.) This array is allocated on the stack. The second array is allocated on the heap and the stack contains the pointer variable to that array.
In both cases you do not pass a pointer to the first array entry, but to the array and the pointer to the array respectively.
Your first scanf overwrites that what is the array, as it is allocated on the stack, your value ends up (by luck) in the array.
Your second scanf overwrites the pointer to the array, thereby changing the pointer to a memory address that probably does not exist in your data segment. This results in the execution error.
That's normal ...
First, scanf requires a pointer. "a" and "b" already are pointers ! So :
/* 1 */
scanf("%d", a);
printf("%d\n", a[0]);
/* 2 */
scanf("%d", b);
printf("%d\n", b[0]);
Will work.
Normally /* 1 */ shouldn't work. But gcc transforms "&a" by "a" because "&a" doesn't have any sense.
printf("&a = %p\n", &a);
printf("a = %p\n", a);
printf("&b = %p\n", &b);
printf("b = %p\n", b);
&a = 0x7ffff6be67d0
a = 0x7ffff6be67d0
&b = 0x7ffff6be67c8
b = 0xb0b010
You can't take the adress of a. But b is a "normal variable" of type pointer, and thus you can take it's address by "&b".
On /* 2 */ you're putting the value entered by the user in b and thus, *b (or b[0]) will crash unless the user will enter a valid readable memory address.
In your case what is happening is that you are passing both variables a and b with the & operator to the scanf function. What this operator does is "ask" the memory address of the variable and pass that address to the scanf function. But, as both of your variables are pointers, what they have indeed is an memory address, so when you pass &a or &b you are passing the memory of the pointer, not the memory address that it holds.
Example:
int x;
int *ptr;
x = 10;
suppose the memory address of x is 1000. You are storing the number 10 at the memory address 1000. Now you do this:
ptr = &x;
You are storing the address 1000 in the pointer. But 1000, apart being an address, is a number itself, so the pointer, as does x, still needs a memory address to store that information. Suppose the pointer memory location is 1004. Now look the example:
*ptr == 10; //x content
ptr == 1000 //x memory address
&ptr == 1004 // ptr memory address.
So if you want to pass to scanf the variable x, but using the pointer, you need to pass the x address stored in it
scanf("%d", ptr);
Just to ilustrate another example of pointers and vectors
int main
{
int vet[5];
int *ptr;
ptr = vet;
for(int i = 0; i < 5; ++i)
{
scanf("%d", (ptr+i) );
}
}
Here you can read the vector using the pointer. Also, using pointer arithmetics you can iterate over the memory addresses of the vector.
精彩评论