Increment operator on pointer of array errors? - c++

I'm trying something very simple, well supposed to be simple but it somehow is messing with me...
I am trying to understand the effect of ++ on arrays when treated as pointers and pointers when treated as arrays.
So,
int main()
{
int a[4] = { 1, 4, 7, 9 };
*a = 3;
*(a+1) = 4;
*++a = 4; //compiler error
}
1: So at *(a+1)=4 we set a[1]=4; //Happy
But when *++a = 4;, I'd expect pointer a to be incremented one since ++ is precedent to * and then * kicks in and we make it equal to 4. But this code just does not work... Why is that?
Another problem:
int main()
{
int* p = (int *)malloc(8);
*p = 5;
printf("%d", p[0]);
*++p = 9; //now this works!
printf("%d", p[1]); //garbage
printf("%d", p[0]); //prints 9
}
2: Now *++p = 9; works fine but it's not really behaving like an array. How are two different? This is just incrementing p, and making it equal to 9. If I print p[0], it now prints 9 and I see that though can't access it via p[0] anymore, *(p-1) shows 5 is still there. So indexing a pointer with [0], where exactly does it point to? What has changed?
Thanks a lot all experts!

The array names is not modifiable lvalue so operation ++ is not applied hence ++a that try to modify a is compilation time error (where a is array name).
Note *(a + 1) and *a++ are not same, a + 1 is a valid instruction as it just add 1 but doesn't modify a itself, Whereas ++a (that is equvilent to a = a + 1) try to modify a hence error.
Note 'array names' are not pointer. Pointers are variable but array names are not. Of-course when you assign array name to a pointer then in most expressions array names decays into address of first element. e.g.
int *p = a;
Note p points to first element of array (a[0]).
Read some exceptions where array name not decaying into a pointer to first element?
An expression a[i] is equivalent to *(a + i), where a can be either a pointer or an array name. Hence in your second example p[i] is valid expression.
Additionally, *++p is valid because because p is a pointer (a variable) in second code example.

int a[4] = { 1, 4, 7, 9 };
int *pa=a;
There is one difference between an array name and a pointer that must be kept in mind. A pointer is a variable, sopa=a and pa++ are legal. But an array name is not a
variable; constructions like a=pa and a++ are illegal
int* p = (int *)malloc(8);
Don't cast result of malloc()
Use index with pointer
p[1]=9; // p[1]==*(p+1)

Related

Why ++(*p) changes the pointer value?

If I have this code:
int A[5] = { 2, 1, 3, 55 };
int *p = A;
cout << ++(*p);
the result is 3 and the value of the first position of A is 3 also, why?
I mean, by hierarchy of operators () is more hierarchical than ++, then we would need operate *p first:
++(*p) => ++(2) => 3
with any change in A vector?
*p is not just "2", it's an lvalue, i.e. this "2" has a well-defined location.
The value at this location is modified by the ++ operator - by definition of the ++ operator.
If you don't want to modify the value, use + 1 instead: *p + 1.
In C/C++, lvalue is a value with a defined location in memory. This value can be changed - by an assignment, incremented, decrement.
For example,
int x = 0;
x = 1; // ok, x is an lvalue, assignment changes the value from 0 to 1
int *p = &x;
*p = 2; // ok, *p is an lvalue, assignment changes the value from 1 to 2
In contrast, an rvalue is a value without a defined location - for example, a result of an arithmetic operation. This value can't be assigned, incremented or decremented (it doesn't mean it can't be used in a larger expression).
For example,
int x = 0, y = 1;
(x + y) = 3; // compilation error, (x + y) is an rvalue
2++; // compilation error, 2 is an rvalue
Here's a pretty simple article explaining lvalues / rvalues: https://eli.thegreenplace.net/2011/12/15/understanding-lvalues-and-rvalues-in-c-and-c
The ++(*p) is the same as ++p[0] and ++A[0] All change the first element of the array.
Why ++(*p) changes the pointer value
It does not. The pointer value is value kept in the p. It is called "address" or "reference". It does not change.
It's worth noting the overuse of '*' in C++ which usually confuses novices.
Here:
int *p = A;
'*' means you are declaring p as a variable that can store a memory address (a pointer), and assigning the address of the first position of A (it's the same as &(A[0]) ).
Here:
++(*p)
' *' means you are looking into 'p' content (derreferencing the address of 'p' which is the same as the address of 'A[0]'). Then the '++' increments the value which reflects both.
Just to confirm, after this assignment (int *p = A;) print the values of 'p', 'A' and '&A[0]'.

Can pointers be used for accessing any arbitrary area in memory?

I am extremely new to C++ and pointers.
#include <iostream>
using namespace std;
int main() {
char* c = "my name jeff";
int i = 0;
while (true) {
cout << *(c + i++);
}
return 0;
}
The output is "my name jeff" followed by a lot of random characters, occasionally making sense. Am I actually accessing memory in my computer ? Why can initialize a pointer from a string ?
From https://eel.is/c++draft/expr#add-4
When an expression J that has integral type is added to or subtracted from an expression P of pointer type, the result has the type of P.
(4.1) If P evaluates to a null pointer value and J evaluates to 0, the result is a null pointer value.
(4.2)
Otherwise, if P points to an array element i of an array object x with n elements ([dcl.array]),77 the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) array element
i
+
j
of x if
0
≤
i
+
j
≤
n
and the expression P - J points to the (possibly-hypothetical) array element
i
−
j
of x if
0
≤
i
−
j
≤
n
.
(4.3)
Otherwise, the behavior is undefined.
So, with "my name jeff" (which is of type const char[13] (with the extra '\0')), once you do c[14] you have UB.
First, a note about this line right here:
char* c = "my name jeff";
In modern C++, this should be a const instead:
const char* c = "my name jeff";
With that out of the way, the answer to your question is yes and no.
Yes in the sense that if you have some sort of pointer, C++ doesn't care what you put in it.
char * pointer = nullptr;
char a, b, c;
pointer = &a;
pointer = &b;
pointer = &c;
The pointer doesn't care what address is stored in it. It is arbitrary in that sense. As long as it is the same type, you're fine.
You can even do pointer arithmetic like this:
*(pointer + 1) = 1;
*(pointer + 2) = 2;
*(pointer + i) = i;
In most cases, this will usually at least compile and probably run. In theory, you could use this type of arithmetic to access any given address and see what data is stored there.
However, in practice, the answer is a big no. That is because accessing unallocated memory is undefined behavior in C++. If you're unaware, undefined behavior allows anything to happen, including crashing or printing weird characters.
So, if you have some array like this:
int arr[4] = {1,2,3,4};
int * pointer = arr;
std::cout << *(pointer + 7);
This is undefined behavior because only pointer + 3 is guaranteed to have allocated memory in it.
So, in short: Yes, you can theoretically reach any address you want with pointer arithmetic, but you can only access memory that has been allocated safely. So in practice, not really.

Int *t; and this t[*t] - Type definition

I'm working on updating a code of a server project since days.
I found a line that I cannot understand (which was commented)
First, I get :
int *t;
Then I got this (commented):
t[*t];
What's the type of this "t[*t]"
The type is an int lvalue. We have two parts:
*t // this is an int
t[ some int ] // this is standard array indexing
Or for a simple example:
int array[] = {1, 2, 3, 4};
int* t = array;
With that setup:
t[*t] == t[1] == 2
Or:
t[*t] = 7;
// now array[] holds {1, 7, 3, 4}
The type is int
*t is equivalent to t[0] as such your expression is equivalent to the follow:
t[*t] == t[t[0]] == t[offset] (if you consider offset = t[0])
By definition of the language, given that t is a pointer, the expression t[*t] is completely equivalent to the expression *(t + *t).
To analyze the type of that expression, let's look at it step for step, from the inside out. I'll replace each expression whole type was identified with «type».
Since t is of type int*, we have *(«int*» + *«int*»).
Dereferencing an int* gives an lvalue of type int. Therefore we get *(«int*» + «int»).
Now adding an integer to a pointer gives, again, a pointer of the same type, so the expression reduces to *«int*».
But that is, again, just the dereferencing of a pointer to int, and therefore the final type of the expression is lvalue int.
Sometimes code is estimated by the number of typed symbols. So I would write a more confusing code but with more characters like:)
( t + *t )[*t];
Relative to your example
*t is some integer stored in t that is *tis equivalent to t[0]. Thus in expression t[*t] there is used the pointer arithmetic
t + *t
or
t + t[0]
that gives some new pointer that then is dereferenced
*( t + *t )
The operator[] of int* always returns an int.
Since *t simply returns the first element of t, t[*t] is the element of tpointed to by the first element of t.
Example:
int *t;
t=new int[5];
for(int i=0;i<5;++i){
t[i]=i+1;
} // t now points to {1,2,3,4,5}
t[*t]; // <--- What does this evaluate to?
In this case t[*t] will be 2.
Because *t is 1, t[1] is the second element in the array, which is 2.

Behavior of 2D arrays

I have created a 2D array, and tried to print certain values as shown below:
int a[2][2] = { {1, 2},
{3, 4}};
printf("%d %d\n", *(a+1)[0], ((int *)a+1)[0]);
The output is:
3 2
I understand why 3 is the first output (a+1 points to the second row, and we print its 0th element.
My question is regarding the second output, i.e., 2. My guess is that due to typecasting a as int *, the 2D array is treated like a 1D array, and thus a+1 acts as pointer to the 2nd element, and so we get the output as 2.
Are my assumptions correct or is there some other logic behind this?
Also, originally what is the type of a when treated as pointer int (*)[2] or int **?
When you wrote expression
(int *)a
then logically the original array can be considered as a one-dimensional array the following way
int a[4] = { 1, 2, 3, 4 };
So expression a points to the first element equal to 1 of this imaginary array. Expression ( a + 1 ) points to the second element of the imaginary array equal to 2 and expression ( a + 1 )[0] returns reference to this element that is you get 2.
Are my assumptions correct or is there some other logic behind this?
Yes.
*(a+1)[0] is equivalent to a[1][0].
((int *)a+1)[0] is equivalent to a[0][1].
Explanation:
a decays to pointer to first element of 2D array, i.e to the first row. *a dereferences that row which is an array of 2 int. Therefore *a can be treated as an array name of first row which further decay to pointer to its first element, i.e 1. *a + 1 will give the pointer to second element. Dereferencing *a + 1 will give 1. So:
((int *)a+1)[0] == *( ((int *)a+1 )+ 0)
== *( ((int *)a + 0) + 1)
== a[0][1]
Note that a, *a, &a, &a[0] and &a[0][0] all have the same address value although they are of different types. After decay, a is of type int (*)[2]. Casting it to int * just makes the address value to type int * and the arithmetic (int *)a+1 gives the address of second element.
Also, originally what is the type of a when treated as pointer (int (*)[2] or int **?
It becomes of type pointer to array of 2 int, i.e int (*)[2]
A 2D-array is essentially a single-dimensional array with some additional compiler's knowledge.
When you cast a to int*, you remove this knowledge, and it's treated like a normal single-dimensional array (which in your case looks in memory like 1 2 3 4).
The key thing to recognize here is that the a there holds the value of the address where the first row is located at. Since the whole array starts from the same location as that, the whole array also has the same address value; same for the very first element.
In C terms:
&a == &a[0];
&a == &a[0][0];
&a[0] == &a[0][0];
// all of these hold true, evaluate into 1
// cast them if you want, looks ugly, but whatever...
&a == (int (*)[2][2]) &a[0];
&a == (int (*)[2][2]) &a[0][0];
&a[0] == (int (*)[2]) &a[0][0];
For this reason, when you cast the a to int *, it simply becomes 1-to-1 equivalent to &a[0][0] both by the means of type and the value. If you were to apply those operations to &a[0][0]:
(&a[0][0] + 1)[0];
(a[0] + 1)[0];
*(a[0] + 1);
a[0][1];
As for the type of a when treated as a pointer, although I am not certain, should be int (*)[2].

Pointer to array dilemma

I have a rather simple question about arrays and pointer to arrays.
consider this code fragment..
int (*ptr)[3]; //A pointer to an array of 3 ints
int arr1[3] = {2,4,6,};
ptr = &arr1; //ptr now points to arr1
//3 different ways to express the same address
cout << &arr1 << "\t" << arr1 << "\t" << &arr1[0] << endl;
Now if:
&arr1 == arr1 == &arr1[0]..
why is this code not correct:
ptr = arr1;
or
ptr = &arr1[0];
This has been driving me crazy...so please any explanation would be appreciated. Also please not that this is not an homework question, just something I'm trying to get a grips on.
In
ptr = arr1;
arr1 is converted to an int*, so you're trying to assign from an incompatible pointer type. &arr1[0] is directly an int*, without conversion, so again incompatible.
&arr1 == arr1 == &arr1[0]
is wrong, since the entities have different types. They only point to the same address, so when printing out, they give the same result.
In most contexts, an expression with an array type is implicitly converted to a pointer to the first element of such array, as explained by 6.3.2.1p3:
Except when it is the operand of the sizeof operator, the _Alignof 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.
Thus the right-hand side of your assignment
ptr = arr1;
is implicitly converted to an incompatible pointer type (int* vs. int (*)[3]), and can't be stored to the pointer variable without a cast.
This isn't really an exception to any rule, as you need to use the unary & operator with other types, too:
T val, *ptr;
ptr = &val;
Below programme will help you to better understand difference between
pointer_to_first_member_of_array, pointer_to_1D_array, pointer_to_2D_array.
Please carefully look at the programme, execute it and see output.
#include<stdio.h>
int priv_element = 88;
int array[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int next_element = 99;
main (int argc, char *argv[])
{
int *ptr_to_first_element = &array[0][0];
int (*ptr_to_1d_arry)[5] = &array[0];
int (*ptr_to_2d_arry)[2][5] = &array;
printf ("Print first num of first array of 2-Dim array: %d\n",
*ptr_to_first_element);
ptr_to_first_element += 5;
printf ("Print first num of second array of 2-Dim array: %d\n",
*ptr_to_first_element);
printf ("Print first num of first array of 2-Dim array: %d\n",
(*ptr_to_1d_arry)[0]);
ptr_to_1d_arry++;
printf ("Print first num of second array of 2-Dim array: %d\n",
(*ptr_to_1d_arry)[0]);
printf ("Print first num of first array of 2-Dim array: %d\n",
(*ptr_to_2d_arry)[0][0]);
ptr_to_2d_arry++;
printf
("Now you increased to point end of 2d-array space. So next to it is next_element on data-seg: %d\n",
(*ptr_to_2d_arry)[0][0]);
}
When you printed the various expressions, it showed you that their values were the same. However, they do not have the same types.
C and C++ include type features to reduce human mistakes and to make it easier to write complicated code. Suppose you had an address in some pointer p and C/C++ allowed you to do either:
float *f = p;
or:
int *i = p;
This would be a mistake, because, generally, whatever bits are in the memory at p do not represent both a useful int and a useful float. By enforcing rules about types, the language prevents a programmer from making a mistake here; the pointer p can only be assigned to another pointer of a compatible type, unless the programmer explicitly overrides the rules with a cast.
Similarly, your ptr is a pointer to an array of three int. At first, it might seem like your arr1 is also an array of three int, so you should be able to assign ptr = arr1;. This is wrong because ptr is merely a pointer, but arr1 is an entire array object. You cannot put an entire array into a pointer; you need to put a pointer to the array into the pointer. To get a pointer to the array, you use the & operator: ptr = &arr1;.
Another thing that is confusing here is that C/C++ includes an automatic shortcut: It converts an array to a pointer to the first element of the array. When arr1 appears in most contexts, it is changed automatically to &arr1[0]. There is not a huge philosophical reason for this; it is just a convenience for the ways we often use arrays. So ptr = arr1; would be equivalent to ptr = &arr1[0];, which is also not allowed. In this form, you can see that arr1 has become a pointer to an int, so you cannot assign it to a pointer to an array of int. Even though the pointer has the value you want, it is the wrong type.
When an array appears as the operand of & or sizeof or _Alignof, this automatic conversion does not occur. So &arr1 results in the address of the array.
A string literal that is used in an initialization such as char a[] = "abc"; is treated specially and is not automatically converted as described above.