C function pointer behavior, array/array pointer behavior - c

Consider the following code:
#include <stdio.h>
int ret_five() {
return 5;
}
int main() {
int x[5] = {1,2,3,4,5};
int (*p)();
p = &ret_five;
printf("%d\n", p()); // 1
p = ret_five;
printf("%d\n", p()); // 2
printf("%d\n", sizeof ret_five); // 3
printf("%d\n", sizeof &ret_five); // 4
printf("%d\n", (*p)()); // 5
printf("%d\n", (****p)()); // 6
printf("%p\n", p); // 7 // edited: replaced %d with %p
printf("%p\n", *p); // 8 // same here and in (8), (10)
printf("%p\n", **p); // 9
printf("%p\n", *******p); // 10
printf("%p\n", x); // 11
printf("%p\n", &x); // 12
return 0;
}
My questions are:
Lines (1) and (2) print the same result. Do ret_five and &ret_five have the same data type? It seems like no, because lines (3) and (4) print different results.
From a syntactical point of view, it seems to me that line (5) should be the right way to call the function that p points to, but of course lines (1) and (2) print 5 just fine. Is there a technical reason for this, or was it a design decision made because the calls in (1) and (2) look cleaner? Or something else?
Line (5) makes perfect sense to me (because p is a function pointer, its dereferenced value is the function, we call the function, it returns 5, we print 5). I was very surprised to find that (6) prints 5 as well! Why is this?
Similarly, lines (7)--(10) all print the same value, namely &ret_five. Why does (10) work?
Lines (11) and (12) print the same value, namely the address where the first element of x lives in memory. Line (12) makes sense, but I don't quite understand exactly what is technically happening in line (11). Does x automatically get cast or interpreted as an int* in this context?
To get the location in memory where x is stored, I typically do &x[0], but it seems like &x works just fine as well, and because x is an array and not a pointer, it seems like in fact &x may be the more canonical way of getting this memory address. Is there a reason to prefer one to the other?
In general, are there best-practices in the above situations? For example, if p = ret_five; and p = &ret_five really do the exact same thing, is there a reason to prefer one to the other?
And, if the two assignments in question 7 really do the exact same thing, why, in a language that is otherwise so rigid, was this laxity built-in?

Do ret_five and &ret_five have the same data type?
ret_five is a function designator and &ret_five is a function pointer. In an expression ret_five is converted to a function pointer whose value and type are the same asret_five.
printf("%d\n", sizeof ret_five); // 3
printf("%d\n", sizeof &ret_five); // 4
sizeof &ret_five is correct. And it yields the size of a function pointer of type int (*)().
sizeof ret_five is invalid C code and it is accepted in gcc as a GNU extension.
printf("%d\n", p); // 7
printf("%d\n", *p); // 8
printf("%d\n", **p); // 9
printf("%d\n", *******p); // 10
If p is a function pointer, p, *p or *****p are equivalent in C.
printf("%p\n", x); // 11
printf("%p\n", &x); // 12
x is an array of 5 int elements. In an expression (except in a few exceptions like if it is the operand of the &operator), it is converted to a pointer to its first element (type of x after conversion is int *).
&x is a pointer to an array of 5 int elements (type of &x is int (*)[5].

A function designator is an expression that has function type. Except when it is the
operand of the sizeof 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«.
ret_five and &ret_five both evaluate to the same function pointers. sizeof ret_five is a constraint violation and your compiler should output a diagnostic. So, ret_five is a function designator that is in all (but two (see above)) situations converted to a pointer to said function, *ret_five is again a function designator, which is AGAIN converted to a pointer to said function if you use it in any context except the two above, so **ret_five is again a function designator, and so on. Printing such a pointer with %d is undefined behavior since %d is for ints.
p = ret_five is correct in modern C. Using &ret_five instead is old fashioned, 1980s C.
Except when it is the operand of the sizeof operator or the unary & operator, or is a
string literal used to initialize an array, an expression that has type »array of type« is
converted to an expression with type »pointer to type« that points to the initial element of
the array object and is not an lvalue.
x and &x have the same numerical value (they are pointers to x's first element) but different types. x evaluates to a pointer to int, but &x evaluates to a pointer to an array of five ints.

When using %p specifier you need to cast the argument to void * as it expects void * type argument.
7.21.6 Formatted input/output functions:
p The argument shall be a pointer to void. The value of the pointer is
converted to a sequence of printing characters, in an implementation-defined
manner.
printf("%p\n", (void *) &x);
Lines (11) and (12) print the same value, namely the address where the first element of x lives in memory. Line (12) makes sense, but I don't quite understand exactly what is technically happening in line (11). Does x automatically get cast or interpreted as an int* in this context?
Yes it will. Array name x decays to the pointer to first element of arrayx. It will give you the location of first element and having type int *. &x is the address of entire array x and it will always print the starting address of the array and is of type int (*)[5]. Since address of first element of the array is same as the staring address of array x that's why you are getting the same value.
To get the location in memory where x is stored, I typically do &x[0], but it seems like &x works just fine as well, and because x is an array and not a pointer, it seems like in fact &x may be the more canonical way of getting this memory address. Is there a reason to prefer one to the other?
Answer is similar to previous one. &x[0] is the address of first element while &x is the address of entire array.

Related

in C, how come the address of array, &array, &array[0] are the same?

int main(){
long a[]={6,9,10};
printf("%p\n",a);
printf("%p\n",&a);
printf("%p\n",&a[0]);
return 0;
}
the result is:
0x7ff9c94a10
0x7ff9c94a10
0x7ff9c94a10
this result really baffles me a lot!
how come it stores different values at the same memory locaton, at 0x7ff9c94a10 exists there the address itself and value 6 simultaneously
A picture should help - we’ll assume a 64-bit long:
+––––––+
0x7ff9c94a10 | 0x06 | a[0]
+––––––+
0x7ff9c94a18 | 0x09 | a[1]
+–––—––+
0x7ff9c94a20 | 0x0a | a[2]
+–––––—+
There is no object a separate from the array elements themselves, so the address of the array (&a, type long (*)[3]) is the same as the address of its first element (&a[0], type long *).
Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" will be converted, or "decay", to an expression of type "pointer to T" and the value of the expression is the address of the first element of the array, so the expression a is effectively the same as &a[0] and also has type long *.
a is a synonym (by definition) for &a[0] as K&R gently explain in "Arrays and Pointers" chapter. &a is not mentioned, but can be regarded as intermediate step.
You have to concentrate on the identifier, here a. All the compiler needs is the address where a begins and what type its elements are. The 2nd element is bound to be at the base address plus one element size.
&a and &a[0] are rather expressions to evaluate than values to store. Also &a[23] is calculated, not stored: a + 23 * elemsize.
printf("%p\n", a );
printf("%p\n", a+1 ); // address of next element, not byte
printf("%p\n", &(a[1]) ); // parens not needed
The 2nd and 3rd line print an address 8 bytes (long size) above the array start.
at 0x7ff9c94a10 exists there the address itself
Maybe that address really exists at the same spot. like we all somehow do. But it holds a long "6".
To fill an array with its addresses you can:
void *a[3];
for (int i = 0; i < 3; i++)
a[i] = &a[i];
for (int i = 0; i < 3; i++) {
printf("%p\n", a[i]);
printf("%p\n", &a[i]);
}
prints:
0x7ffe0918cbe0
0x7ffe0918cbe0
0x7ffe0918cbe8
0x7ffe0918cbe8
0x7ffe0918cbf0
0x7ffe0918cbf0
It takes 8 bytes to store an address/pointer. long and void * have same size here.
printf("%p\n", a+i); also works. No need to take reference of a derefernced element: &a[i].
One basic array-and-pointer rule is the E1[E2] is *(E1 + E2). E1 has to designate an array, and E2 and integer, so
a[i]
*(a + i)
are the same. As are
&(a[i])
&a[i]
a + 1
The idea of an array starting at a defined address is simple. But to talk about it...natural language can live with double senses:
"Have you seen number 12 today?" (the guest)
"Did you clean number 12?" (not the guest, the room)
bafflement
Somehow. But while after void **b = a; you can use the pointer b just like a (only that it has its own new &b) a = ... is not possible (assignment to the naked array).
New Example
Your long type was good for really storing pointers to something, but it made the above example complex. Here a simple int array, so nobody gets tempted to store addresses. It shows what an array and a pointer share and what not:
int main(){
int a[3] = {10,20,30};
int *b;
b = a;
for (int i = 0; i < 3; i++) {
printf("%p\n", &a[i]);
printf("%p\n", &b[i]);
printf("%p %d\n", a+i, a[i]);
printf("%p %d\n", b+i, b[i]);
}
printf("%p\n", a);
printf("%p b = a\n", b);
printf("%p\n", &a);
printf("%p <-- different, &b != &a\n", &b);
return 0;
}
This gives:
0x7fff72f0ed8c
0x7fff72f0ed8c
0x7fff72f0ed8c 10
0x7fff72f0ed8c 10
0x7fff72f0ed90
0x7fff72f0ed90
0x7fff72f0ed90 20
0x7fff72f0ed90 20
0x7fff72f0ed94
0x7fff72f0ed94
0x7fff72f0ed94 30
0x7fff72f0ed94 30
0x7fff72f0ed8c
0x7fff72f0ed8c b = a (same as top index zero)
0x7fff72f0ed8c
0x7fff72f0ed80 <-- different, &b != &a
The difference (12 bytes, "0" for a "c") is not dramatic, but that does not matter. b has a life besides the array elements, a not.
Along with that goes the fact that the pointer b can be assigned to (as done with b = a), but the array a not.
How come ?
the addresses of array, &array, &array[0]
This is actually very unclear. The address of array is &array (somehow), but the address of &array is neither &&array (errror about a label 'array') nor &(&array) (error: lvalue required).
Well,
array
is, by definition, the address of the first element, this is the same as:
&array[0]
so this explains why both have the same address.
The second expression is the address of the array. Normally, (I don't remember at this moment if that is enforced by the standard or not) an array is stored by putting its elements one after the other, starting on the first one... so it is normal that the array address is the same as the address of the first element. But if you check the types of the pointed to objects, you will see the differences between these expressions: The type of
*array
is the type of an element of the array, while the type of
*&array
is the whole array type.
So, let's assume you have
char array[10];
then
array
is of type
char *
and
*array
is of type char, while
&array
is of type array of 10 chars, or
char [10]
How to verify this? Just run the following program:
#include <stdio.h>
/* print the expression and it's size */
#define P(T) printf("sizeof("#T") == %zu\n", sizeof(T))
int main()
{
char array[10];
P( array );
P( *array );
P( &array );
P( *&array );
P( array[0] );
P( &array[0] );
P( *&array[0] );
}
When run (on my system) it produces:
$ a.out
sizeof(array) == 10 <-- the size of the array
sizeof(*array) == 1 <-- the size of the pointed to first element (as by def)
sizeof(&array) == 8 <-- size of a pointer to the array.
sizeof(*&array) == 10 <-- size of the pointed to object (the array)
sizeof(array[0]) == 1 <-- size of the first element.
sizeof(&array[0]) == 8 <-- size of a pointer to the first element (as above)
sizeof(*&array[0]) == 1 <-- size of the pointed to first element.
$ _
(the texts on the right are the explanations on the numbers)
The reason of the error on && (this belongs to one of the answers given, that for some reason has chained the & into two applications of it and got an error about a label operator) is that GCC has an unary operator && for a GCC extension to the language. There's no && unary operator in C, and you'd get an error if you resume to -pedantic, about a syntax error when trying to use the logical and operator (&&) without a left operand. Anyway, if you tried to separate the operands as in
& & array
then you would get another error because the subexpression &array doesn't denote an lvalue (has no representation in memory) and so, cannot be applied the & left unary operator (you cannot get an address of a value).
Your last paragraph is interesting: You say
this result really baffles me a lot! how come it stores different values at the same memory locaton, at 0x7ff9c94a10 exists there the address itself and value 6 simultaneously
they are not different values. The problem here is that two of the three different expressions represent different things, but in the same place, because the array starts in memory at the same point as it's first element.
When you are at home, you, and your house have the same postal address, but you and your house are different things, right? This is exactly what is happening here: &array (is the address of your house, the array) and &array[0] (is your address, which is the same as the postal addres of the house because you are in the house). The problem is that you have three expressions and don't get that the expression array means, by how it was defined, literally A POINTER TO THE FIRST ELEMENT and this makes it equivalent to &array[0] (like an abreviation). So we end having three expressions with the same value, two of them being the same thing (exactly) and the other meaning something different. Probably after you read this paragraph, you'll need to read again the output of the simple program above and you'll see where are the differences (obviously, your house is bigger than you)

Array of Pointers and address spacing

#include<stdio.h>
int main()
{
int a[4]={1,2,3,4};
int *p[4]={a,a+1,a+2,a+3};
printf("%u %u %u\n",p,(p+1),(p+2));
}
And the output is:
937449104 937449112 937449120
On line 3:
This will store the addresses of a[] and its address spacing is 4 values apart as expected.
On line 6:
But when I print the addresses of the elements in p, shouldn't even their address differ by 4 since they are ints as well.
But the output gives us address spacing of 8.
Note that p is an array of int* (pointers), not an array of integers. So on a 64-bit system, it's perfectly normal for pointers to have a spacing of 8, or more precisely, sizeof(int*).
int *p[4]={a,a+1,a+2,a+3};
^
printf("%u %u %u\n",p,(p+1),(p+2));
^ ^ ^
When you write p+1 (use p in pointer arithmetics), the array p decays to a pointer, so the type for p+1 is int**, which should be a pointer to a pointer to int. You'll observe 4 (or sizeof(int)) if you dereference p, getting its content:
printf("%u %u %u\n",*p,*(p+1),*(p+2));
^ ^ ^
which is equivalent to:
printf("%u %u %u\n",a,(a+1),(a+2));
^ ^ ^
By the way, your compiler should have warned you about wrong format specifier, so this is the correct statement:
printf("%p %p %p\n",*p,*(p+1),*(p+2));
printf("%p %p %p\n",,a,(a+1),(a+2));
^ ^ ^
Note that %p is the correct specifier if you want to print the address of pointers.
The increment of a pointer in pointer arithmetic is dictated by what the pointer points to. Here in p+1 p will convert ("decay") into pointer to the first element of the array, which (decayed pointer) is of type int**, because the first element being of type int*. Now when we write p+1 it will move by what p points to — which means move by sizeof(int*). And in your system sizeof(int*)=8. (You have likely to have 64 bit system — the sizeof resulting in 8 byte or 64 bit).
At this point it should be clear why it gave the spacing of 8 in the second case.
Check what happens in first case. a converts ("decays") into pointer to the first element, which (decayed pointer) is of type int*, the first element being of type int. So here a+1 will move by sizeof what it points to which is size of the int variable or sizeof(int).
The printed values will differ by 4 in a system where sizeof(int*) == 4 which is certainly not the case in your system. Understanding the declaration here solves the half of the doubt you have — both are distinct in that, the first one is an array of 4 int but the second is an array 4 int*. When in doubt about declarations, you can check with cdecl.
The correct way to print pointers is to use %p format specifier with explicit void* casting. Example: printf("%p\n",(void*)p); Also, you can check the size of int variable and int* variables like this (and doing this will make you aware of the size of the int and int* in your system).
printf("sizeof(int)=%zu sizeof(int*)=%zu\n", sizeof(int), sizeof(int*));
%zu used because sizeof operator returns the results in size_t.
Cutting a long story short — the takeaways will be:
Pointer arithmetic is dependent on the object it points to.
For the same arithmetic operation p+1, the result will be different depending on the type p points to.
Correctly printing pointer variables and size_t values.

Confused on examples of basic functions in C [duplicate]

Is an array's name a pointer in C?
If not, what is the difference between an array's name and a pointer variable?
An array is an array and a pointer is a pointer, but in most cases array names are converted to pointers. A term often used is that they decay to pointers.
Here is an array:
int a[7];
a contains space for seven integers, and you can put a value in one of them with an assignment, like this:
a[3] = 9;
Here is a pointer:
int *p;
p doesn't contain any spaces for integers, but it can point to a space for an integer. We can, for example, set it to point to one of the places in the array a, such as the first one:
p = &a[0];
What can be confusing is that you can also write this:
p = a;
This does not copy the contents of the array a into the pointer p (whatever that would mean). Instead, the array name a is converted to a pointer to its first element. So that assignment does the same as the previous one.
Now you can use p in a similar way to an array:
p[3] = 17;
The reason that this works is that the array dereferencing operator in C, [ ], is defined in terms of pointers. x[y] means: start with the pointer x, step y elements forward after what the pointer points to, and then take whatever is there. Using pointer arithmetic syntax, x[y] can also be written as *(x+y).
For this to work with a normal array, such as our a, the name a in a[3] must first be converted to a pointer (to the first element in a). Then we step 3 elements forward, and take whatever is there. In other words: take the element at position 3 in the array. (Which is the fourth element in the array, since the first one is numbered 0.)
So, in summary, array names in a C program are (in most cases) converted to pointers. One exception is when we use the sizeof operator on an array. If a was converted to a pointer in this context, sizeof a would give the size of a pointer and not of the actual array, which would be rather useless, so in that case a means the array itself.
When an array is used as a value, its name represents the address of the first element.
When an array is not used as a value its name represents the whole array.
int arr[7];
/* arr used as value */
foo(arr);
int x = *(arr + 1); /* same as arr[1] */
/* arr not used as value */
size_t bytes = sizeof arr;
void *q = &arr; /* void pointers are compatible with pointers to any object */
If an expression of array type (such as the array name) appears in a larger expression and it isn't the operand of either the & or sizeof operators, then the type of the array expression is converted from "N-element array of T" to "pointer to T", and the value of the expression is the address of the first element in the array.
In short, the array name is not a pointer, but in most contexts it is treated as though it were a pointer.
Edit
Answering the question in the comment:
If I use sizeof, do i count the size of only the elements of the array? Then the array “head” also takes up space with the information about length and a pointer (and this means that it takes more space, than a normal pointer would)?
When you create an array, the only space that's allocated is the space for the elements themselves; no storage is materialized for a separate pointer or any metadata. Given
char a[10];
what you get in memory is
+---+
a: | | a[0]
+---+
| | a[1]
+---+
| | a[2]
+---+
...
+---+
| | a[9]
+---+
The expression a refers to the entire array, but there's no object a separate from the array elements themselves. Thus, sizeof a gives you the size (in bytes) of the entire array. The expression &a gives you the address of the array, which is the same as the address of the first element. The difference between &a and &a[0] is the type of the result1 - char (*)[10] in the first case and char * in the second.
Where things get weird is when you want to access individual elements - the expression a[i] is defined as the result of *(a + i) - given an address value a, offset i elements (not bytes) from that address and dereference the result.
The problem is that a isn't a pointer or an address - it's the entire array object. Thus, the rule in C that whenever the compiler sees an expression of array type (such as a, which has type char [10]) and that expression isn't the operand of the sizeof or unary & operators, the type of that expression is converted ("decays") to a pointer type (char *), and the value of the expression is the address of the first element of the array. Therefore, the expression a has the same type and value as the expression &a[0] (and by extension, the expression *a has the same type and value as the expression a[0]).
C was derived from an earlier language called B, and in B a was a separate pointer object from the array elements a[0], a[1], etc. Ritchie wanted to keep B's array semantics, but he didn't want to mess with storing the separate pointer object. So he got rid of it. Instead, the compiler will convert array expressions to pointer expressions during translation as necessary.
Remember that I said arrays don't store any metadata about their size. As soon as that array expression "decays" to a pointer, all you have is a pointer to a single element. That element may be the first of a sequence of elements, or it may be a single object. There's no way to know based on the pointer itself.
When you pass an array expression to a function, all the function receives is a pointer to the first element - it has no idea how big the array is (this is why the gets function was such a menace and was eventually removed from the library). For the function to know how many elements the array has, you must either use a sentinel value (such as the 0 terminator in C strings) or you must pass the number of elements as a separate parameter.
Which *may* affect how the address value is interpreted - depends on the machine.
An array declared like this
int a[10];
allocates memory for 10 ints. You can't modify a but you can do pointer arithmetic with a.
A pointer like this allocates memory for just the pointer p:
int *p;
It doesn't allocate any ints. You can modify it:
p = a;
and use array subscripts as you can with a:
p[2] = 5;
a[2] = 5; // same
*(p+2) = 5; // same effect
*(a+2) = 5; // same effect
The array name by itself yields a memory location, so you can treat the array name like a pointer:
int a[7];
a[0] = 1976;
a[1] = 1984;
printf("memory location of a: %p", a);
printf("value at memory location %p is %d", a, *a);
And other nifty stuff you can do to pointer (e.g. adding/substracting an offset), you can also do to an array:
printf("value at memory location %p is %d", a + 1, *(a + 1));
Language-wise, if C didn't expose the array as just some sort of "pointer"(pedantically it's just a memory location. It cannot point to arbitrary location in memory, nor can be controlled by the programmer). We always need to code this:
printf("value at memory location %p is %d", &a[1], a[1]);
I think this example sheds some light on the issue:
#include <stdio.h>
int main()
{
int a[3] = {9, 10, 11};
int **b = &a;
printf("a == &a: %d\n", a == b);
return 0;
}
It compiles fine (with 2 warnings) in gcc 4.9.2, and prints the following:
a == &a: 1
oops :-)
So, the conclusion is no, the array is not a pointer, it is not stored in memory (not even read-only one) as a pointer, even though it looks like it is, since you can obtain its address with the & operator. But - oops - that operator does not work :-)), either way, you've been warned:
p.c: In function ‘main’:
pp.c:6:12: warning: initialization from incompatible pointer type
int **b = &a;
^
p.c:8:28: warning: comparison of distinct pointer types lacks a cast
printf("a == &a: %d\n", a == b);
C++ refuses any such attempts with errors in compile-time.
Edit:
This is what I meant to demonstrate:
#include <stdio.h>
int main()
{
int a[3] = {9, 10, 11};
void *c = a;
void *b = &a;
void *d = &c;
printf("a == &a: %d\n", a == b);
printf("c == &c: %d\n", c == d);
return 0;
}
Even though c and a "point" to the same memory, you can obtain address of the c pointer, but you cannot obtain the address of the a pointer.
The following example provides a concrete difference between an array name and a pointer. Let say that you want to represent a 1D line with some given maximum dimension, you could do it either with an array or a pointer:
typedef struct {
int length;
int line_as_array[1000];
int* line_as_pointer;
} Line;
Now let's look at the behavior of the following code:
void do_something_with_line(Line line) {
line.line_as_pointer[0] = 0;
line.line_as_array[0] = 0;
}
void main() {
Line my_line;
my_line.length = 20;
my_line.line_as_pointer = (int*) calloc(my_line.length, sizeof(int));
my_line.line_as_pointer[0] = 10;
my_line.line_as_array[0] = 10;
do_something_with_line(my_line);
printf("%d %d\n", my_line.line_as_pointer[0], my_line.line_as_array[0]);
};
This code will output:
0 10
That is because in the function call to do_something_with_line the object was copied so:
The pointer line_as_pointer still contains the same address it was pointing to
The array line_as_array was copied to a new address which does not outlive the scope of the function
So while arrays are not given by values when you directly input them to functions, when you encapsulate them in structs they are given by value (i.e. copied) which outlines here a major difference in behavior compared to the implementation using pointers.
The array name behaves like a pointer and points to the first element of the array. Example:
int a[]={1,2,3};
printf("%p\n",a); //result is similar to 0x7fff6fe40bc0
printf("%p\n",&a[0]); //result is similar to 0x7fff6fe40bc0
Both the print statements will give exactly same output for a machine. In my system it gave:
0x7fff6fe40bc0

typecasting a pointer to an int .

I can't understand the output of this program .
What I get of it is , that , first of all , the pointers p, q ,r ,s were pointing towards null .
Then , there has been a typecasting . But how the heck , did the output come as 1 4 4 8 . I might be very wrong in my thoughts . So , please correct me if I am wrong .
int main()
{
int a, b, c, d;
char* p = (char*)0;
int *q = (int *)0;
float* r = (float*)0;
double* s = (double*)0;
a = (int)(p + 1);
b = (int)(q + 1);
c = (int)(r + 1);
d = (int)(s + 1);
printf("%d %d %d %d\n", a, b, c, d);
_getch();
return 0;
}
Pointer arithmetic, in this case adding an integer value to a pointer value, advances the pointer value in units of the type it points to. If you have a pointer to an 8-byte type, adding 1 to that pointer will advance the pointer by 8 bytes.
Pointer arithmetic is valid only if both the original pointer and the result of the addition point to elements of the same array object, or just past the end of it.
The way the C standard describes this is (N1570 6.5.6 paragraph 8):
When an expression that has integer type is added to or subtracted
from a pointer, the result has the type of the pointer operand. If the
pointer operand points to an element of an array object, and the array
is large enough, the result points to an element offset from the
original element such that the difference of the subscripts of the
resulting and original array elements equals the integer expression.
[...]
If both the pointer operand and the result point to elements of the
same array object, or one past the last element of the array object,
the evaluation shall not produce an overflow; otherwise, the behavior
is undefined. If the result points one past the last element of the
array object, it shall not be used as the operand of a unary *
operator that is evaluated.
A pointer just past the end of an array is valid, but you can't dereference it. A single non-array object is treated as a 1-element array.
Your program has undefined behavior. You add 1 to a null pointer. Since the null pointer doesn't point to any object, pointer arithmetic on it is undefined.
But compilers aren't required to detect undefined behavior, and your program will probably treat a null pointer just like any valid pointer value, and perform arithmetic on it in the same way. So if the null pointer points to address 0 (this is not guaranteed, BTW, but it's very common), then adding 1 to it will probably give you a pointer to address N, where N is the size in bytes of the type it points to.
You then convert the resulting pointer to int (which is at best implementation-defined, will lose information if pointers are bigger than int, and may yield a trap representation) and you print the int value. The result, on most systems, will probably show you the sizes of char, int, float, and double, which are commonly 1, 4, 4, and 8 bytes, respectively.
Your program's behavior is undefined, but the way it actually behaves on your system is typical and unsurprising.
Here's a program that doesn't have undefined behavior that illustrates the same point:
#include <stdio.h>
int main(void) {
char c;
int i;
float f;
double d;
char *p = &c;
int *q = &i;
float *r = &f;
double *s = &d;
printf("char: %p --> %p\n", (void*)p, (void*)(p + 1));
printf("int: %p --> %p\n", (void*)q, (void*)(q + 1));
printf("float: %p --> %p\n", (void*)r, (void*)(r + 1));
printf("double: %p --> %p\n", (void*)s, (void*)(s + 1));
return 0;
}
and the output on my system:
char: 0x7fffa67dc84f --> 0x7fffa67dc850
int: 0x7fffa67dc850 --> 0x7fffa67dc854
float: 0x7fffa67dc854 --> 0x7fffa67dc858
double: 0x7fffa67dc858 --> 0x7fffa67dc860
The output is not as clear as your program's output, but if you examine the results closely you can see that adding 1 to a char* advances it by 1 byte, an int* or float* by 4 bytes, and a double* by 8 bytes. (Other than char, which by definition has a size of 1 bytes, these may vary on some systems.)
Note that the output of the "%p" format is implementation-defined, and may or may not reflect the kind of arithmetic relationship you might expect. I've worked on systems (Cray vector computers) where incrementing a char* pointer would actually update a byte offset stored in the high-order 3 bits of the 64-bit word. On such a system, the output of my program (and of yours) would be much more difficult to interpret unless you know the low-level details of how the machine and compiler work.
But for most purposes, you don't need to know those low-level details. What's important is that pointer arithmetic works as it's described in the C standard. Knowing how it's done on the bit level can be useful for debugging (that's pretty much what %p is for), but is not necessary to writing correct code.
Adding 1 to a pointer advances the pointer to the next address appropriate for the pointer's type.
When the (null)pointers+1 are recast to int, you are effectively printing the size of each of the types being pointed to by the pointers.
printf("%d %d %d %d\n", sizeof(char), sizeof(int), sizeof(float), sizeof(double) );
does pretty much the same thing. If you want to increment each pointer by only 1 BYTE, you'll need to cast them to (char *) before incrementing them to let the compiler know
Search for information about pointer arithmetic to learn more.
You're typecasting the pointers to primitive datatypes rather type casting them to pointers themselves and then using * (indirection) operator to indirect to that variable value. For instance, (int)(p + 1); means p; a pointer to constant, is first incremented to next address inside memory (0x1), in this case. and than this 0x1 is typecasted to an int. This totally makes sense.
The output you get is related to the size of each of the relevant types. When you do pointer arithmetic as such, it increases the value of the pointer by the added value times the base type size. This occurs to facilitate proper array access.
Because the size of char, int, float, and double are 1, 4, 4, and 8 respectively on your machine, those are reflected when you add 1 to each of the associated pointers.
Edit:
Removed the alternate code which I thought did not exhibit undefined behavior, which in fact did.

Why the compiler is not showing error on expressions that use arrays like pointers?

I'm new in C programming and currently learning about array and strings. I'm quite confuse in this topic. Coming to my question-
Since an array (for ex- a[]={20,44,4,8}), the name in an expression decays into pointer constant,so whenever if i try to do pointer arithmetic for example- a=a+1 or anything like this the compiler shows error but when the same thing I write in printf() function it is showing the address of the first element rather than showing error. Why?
In an expression for example *(a+1)=2 first (a+1) will be evaluated and then * will dereference it. My question is that if a is a pointer constant then how it can point to any other memory location in an array and how this expression is perfectly legal?
I tried to search about this but couldn't get the accurate result.
Although an array name evaluates to a pointer in some expressions, your a = a+1 assignment tries to assign to an array, which is not allowed.
On the other hand, a+1 expression is allowed, and it evaluates to another pointer. When you pass this value to printf, the function happily prints it. Do not forget to cast the result to void* when you print:
printf("%p\n", (void*)(a+1));
if a is a pointer constant then how it can point to any other memory location in an array and how is *(a+1) expression perfectly legal?
For the same reason that 2+3, a combination of two constants, produces a value that is neither a 2 nor a 3. In your example, a+1 expression does not modify a. Instead, the expression uses it as a "starting point", computes a different value (which happens to be of type pointer), and leaves a unchanged.
The name of the array a is not quite the same as a pointer constant. It merely
acts like a pointer constant in some circumstances. In other circumstances it will
act quite differently; for example, sizeof(a) may have a much larger value
than sizeof(b) where b is truly a pointer.
This code is legal:
int a[] = {20,44,4,8};
int *b;
b = a;
b = b + 1;
because a is enough like a pointer that you can set b to point to the same
address but, unlike a, b really is a pointer and it can be modified.
The last line of code could just as well be:
b = a + 1;
because the right-hand side here is not trying to modify a; it is merely using
the address of the first element of a to compute a new address.
The expression *(a + 1) is effectively another way of writing a[1].
You know what will happen when you write a[1] = 2, right?
It will change what is stored in the second element of a.
(The first element is always a[0] whether you do anything with it or not.)
Storing a new value in a[1] doesn't change the location of the array a.
When array decays in to pointer, the resulting value is a rvalue. It's an value that cannot be assigned to.
So int[4] will become int*const, constant pointer to integer.
Q1:
Types in expression a = a + 1 are:
int[4] = int[4] + int
If we focus on addition first, array decays to pointer:
int[4] = int*const + int
int[4] = int*const // After addition
But now there is a problem:
int*const = int*const
In memory a is an array with 4 ints, and nothing more. There is no place where you could possibly store address with type int*. Compiler will show an error.
Q2:
Types in expression *(a+1)=2 are:
*(int[4] + int) = int
Again, array decays to pointer and addition happens:
*(int*const + int) = int
*(int*const) = int // int* is now equal to &a[1]
Dereferencing int*const is legal. While pointer is constant, value it points to is not:
int = int // Ok, equal types
Types are now perfectly compatible.

Resources