I just tried this code:
int i = 33;
int * pi = &i;
cout << "i: " << *pi << endl;
cout << "i: " << pi[0] << endl;
Both lines return the same thing.
Essentially, if I get index zero of any pointer, I'll get the value of the correct type at the location of the pointer. Isn't that the same thing as dereferencing?
Every time a pointer is dereferenced in C++, wouldn't getting index zero also work? I'm not suggesting anyone should actually do that, but I think it would work. Wouldn't it?
Ignoring overloaded operators, there's one case there is a difference, and that's array rvalues post-DR1213:
using arr = int[2];
arr&& f();
int&& b = *f(); // error, *f() is an lvalue, doesn't bind to int&&
int&& c = f()[0]; // OK, subscript applied to array rvalue results in an xvalue
I don't know of any compiler that implements that resolution, though. But it should be implemented eventually.
Assuming no operator overloading, they are nearly the same.
[C] 6.5.2.1 Array subscripting:
E1[E2] is identical to (*((E1)+(E2)))
[C++] 5.2.1 Subscripting:
The expression E1[E2] is identical (by definition) to *((E1)+(E2)) ... , except that in the case of an array operand, the result is an
lvalue if that operand is an lvalue and an xvalue otherwise.
See the great answer of #T.C regarding the last part.
For pointers, they should give the same result.
The only time that they could differ is if you are applying them on a user-defined type that overloads operator*() and operator[](int) differently (or one and not the other, in which case you would get a compile error).
Related
It is said in the C standard that
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 pointer to the initial element of the
array object and is not an lvalue.
So why does the array type expression not decay into a pointer when used as the left operand of an assignment operator, like:
int arrayoften[] = { 10, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
arrayoften = 300; //not valid
I mean, the expression arrayoften is not one of the three exceptions, so shouldn't it theoretically be converted to a pointer?
One way of answering this (as #Rakete1111 noted in a comment) is to say that that an array on the lhs does decay to a pointer. But it's a pointer value (an rvalue), not an lvalue, so you can't assign to it.
What's the difference between an rvalue and an lvalue? An rvalue is anything you can compute, that has a value. An lvalue is a value that additionally has a location (that the compiler knows), such that it can be stored into as the left-hand side of an assignment operator. (The term "rvalue" is therefore a backformation; it's something that can appear on the right-hand side.)
We can understand this better with an example: Suppose I have
int a[10], b[10];
int *p1 = a, *p2 = b;
p1 = p2 + 1;
Now, the type p1 is "pointer to int", and the type of the expression p2 + 1 is "pointer to int", so I can make that assignment, no problem.
But what if I said
p1 + 1 = p2; /* WRONG */
The types on the left and right-hand sides are still both "pointer to int", but now the lhs is p1 + 1, which is an rvalue, but not an lvalue. It has no location, it's just a computed value, I can't assign to it.
So, going back to your example, saying
a = p1;
is sort of the same thing. By the language in the C standard you quoted, the expression is converted to
&a[0] = p1;
Again, the types match, but &a1[0] is not an lvalue, so you can't assign to it.
You can tell this is what's going on (or what might be going on) by looking at the compiler error messages: for one compiler I tried, the error was "illegal lhs of assignment operator", which I believe indicates that everything was fine until the compiler discovered that the lhs was not an lvalue. Indeed, I believe that the error message from Ritchie's original C compiler was simply "not an lvalue". (I should boot up my '11 and see.)
Now, with all of this said, it's only one interpretation. That there are others is suggested by the error messages of other compilers:
"incompatible types in assignment"
"incompatible types when assigning to type ‘int[10]’ from type ‘int’"
"array type 'int [10]' is not assignable"
Compiler 1 is an older version of gcc, compiler 2 is a newer version of gcc, and compiler 3 is clang. It looks like these compilers are (for whatever reason) not blindly converting the array on the lhs to a pointer. Perhaps this is so that they can give a better error message, perhaps it's for some other reason, although it does seem to suggest that these compilers are adding another exception -- not explicitly listed in the Standard -- to the list of cases where arrays are not converted to pointers.
[As an aside, though, I'm not sure what rule these newer compilers are using to cause the lhs to remain an array. It can't be, "don't convert arrays to pointers on the left-hand side of an assignment operator", because there are plenty of valid expressions -- such as *(a + 1) = 10 -- where that conversion is fine.]
One more point this whole discussion brings up is that the deep intertwinedness of the arrays-decay-to-pointers rule is that it makes it very difficult to contemplate a future in which there's an extension to C that adds proper array assignment. Any compiler that wants to make
int a[10], b[10];
a = b;
work, first has to figure out how not to convert it into
a = &b[0];
or
&a[0] = &b[0];
I want to know what is the different between *&aPtr and &*aPtr if replaced * & and & * ?
int a;
int *aptr;
a = 7;
aptr=&a;
cout << &* aPtr<< *&aPtr<< endl;
They have the same value, but *&aPtr is an lvalue that refers to aPtr whereas &*aPtr is a prvalue that has the same value as aPtr.
If the types are primitives (integers, characters, booleans etc.), then they will yield the same value.
A difference may occure if the operators & and * are overloaded for specific class. in this case, depending on the implementation alone - there might be a difference.
One other thing: a corner case can occure if T* t actually points to null:
int* i = nullptr;
*&i; //ok, first takes the address of i, then dereference it, yielding a null pointer again
&*i //wrong, dereference a null pointer, yielding undefined behavior
These unary operators & and * group right to left.
So in this expression
&*aPtr
at first operator * is applied and you get lvalue of a after that operator & is applied and you get rvalue of pointer to a.
Its value is the same as the initial value of aPtr. However you may not write for example
&*aPtr = &a;
while you may write
aPtr = &a;
In this expression
*&aPtr
at first operator & is applied that yields the address of variable aPtr itself. After that operator * is applied and you get again aPtr.
The difference between this expression and the above expression is that you may write
*&aPtr = &a;
because expression *&aPtr yields lvalue of aPtr.
I just noticed I can use [] on a pointer and it works, but I was wondering, if this is good to use it.
int a[]={1,2,3,4};
int *p=&a[1];
std::cout << p[0]; // returns 2;
std::cout << p[-1]; // returns 1;
std::cout << p[1]; // returns 3;
I always learned, you have to use it like this:
std::cout << *(p-1);
std::cout << *(p+1);
But is it okay to use the operator [] on a pointer ?
In C/C++, given a pointer p and integral value k, p[k] is evaluated as *(p+k). Either form is fine to use as long as p+k points to valid memory.
If you have access to the C99 Standard, see section 6.5.2.1 Array subscripting, Paragraph 2. It says:
A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object. The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the
initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero).
If you have access to the C++11 standard, see section 5.2.1 Subscripting, Paragraph 1. It says:
A postfix expression followed by an expression in square brackets is a postfix expression. One of the expressions shall have the type “pointer to T” and the other shall have unscoped enumeration or integral type.
The result is an lvalue of type “T.” The type “T” shall be a completely-defined object type. The expression E1[E2] is identical (by definition) to *((E1)+(E2))
In addition to great answer by R Sahu you can also do
std::cout << p[-1]; // returns 1;
std::cout << p[1]; // returns 3;
std::cout << 1[p]; // returns 3;
std::cout << (-1)[p]; // returns 1;
See : Ideone
For pointer p and integer value k :
p[k] and k[p] both are evaluating to *(p+k). So, both are same.
But following one is not same
std::cout << -1[p]; // returns -3;
std::cout << (-1)[p]; // returns 1;
We all know that dereferencing an null pointer or a pointer to unallocated memory invokes undefined behaviour.
But what is the rule when used within an expression passed to sizeof?
For example:
int *ptr = 0;
int size = sizeof(*ptr);
Is this also undefined?
In most cases, you will find that sizeof(*x) does not actually evaluate *x at all. And, since it's the evaluation (de-referencing) of a pointer that invokes undefined behaviour, you'll find it's mostly okay. The C11 standard has this to say in 6.5.3.4. The sizeof operator /2 (my emphasis in all these quotes):
The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. The size is determined from the type of the operand. The result is an integer. If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.
This is identical wording to the same section in C99. C89 had slightly different wording because, of course, there were no VLAs at that point. From 3.3.3.4. The sizeof operator:
The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. The size is determined from the type of the operand, which is not itself evaluated. The result is an integer constant.
So, in C, for all non-VLAs, no dereferencing takes place and the statement is well defined. If the type of *x is a VLA, that's considered an execution-phase sizeof, something that needs to be worked out while the code is running - all others can be calculated at compile time. If x itself is the VLA, it's the same as the other cases, no evaluation takes place when using *x as an argument to sizeof().
C++ has (as expected, since it's a different language) slightly different rules, as shown in the various iterations of the standard:
First, C++03 5.3.3. Sizeof /1:
The sizeof operator yields the number of bytes in the object representation of its operand. The operand is either an expression, which is not evaluated, or a parenthesized type-id.
In, C++11 5.3.3. Sizeof /1, you'll find slightly different wording but the same effect:
The sizeof operator yields the number of bytes in the object representation of its operand. The operand is either an expression, which is an unevaluated operand (Clause 5), or a parenthesized type-id.
C++11 5. Expressions /7 (the above mentioned clause 5) defines the term "unevaluated operand" as perhaps one of the most useless, redundant phrases I've read for a while, but I don't know what was going through the mind of the ISO people when they wrote it:
In some contexts ([some references to sections detailing those contexts - pax]), unevaluated operands appear. An unevaluated operand is not evaluated.
C++14/17 have the same wording as C++11 but not necessarily in the same sections, as stuff was added before the relevant parts. They're in 5.3.3. Sizeof /1 and 5. Expressions /8 for C++14 and 8.3.3. Sizeof /1 and 8. Expressions /8 for C++17.
So, in C++, evaluation of *x in sizeof(*x) never takes place, so it's well defined, provided you follow all the other rules like providing a complete type, for example. But, the bottom line is that no dereferencing is done, which means it does not cause a problem.
You can actually see this non-evaluation in the following program:
#include <iostream>
#include <cmath>
int main() {
int x = 42;
std::cout << x << '\n';
std::cout << sizeof(x = 6) << '\n';
std::cout << sizeof(x++) << '\n';
std::cout << sizeof(x = 15 * x * x + 7 * x - 12) << '\n';
std::cout << sizeof(x += sqrt(4.0)) << '\n';
std::cout << x << '\n';
}
You might think that the final line would output something vastly different to 42 (774, based on my rough calculations) because x has been changed quite a bit. But that is not actually the case since it's only the type of the expression in sizeof that matters here, and the type boils down to whatever type x is.
What you do see (other than the possibility of different pointer sizes on lines other than the first and last) is:
42
4
4
4
4
42
No. sizeof is an operator, and works on types, not the actual value (which is not evaluated).
To remind you that it's an operator, I suggest you get in the habit of omitting the brackets where practical.
int* ptr = 0;
size_t size = sizeof *ptr;
size = sizeof (int); /* brackets still required when naming a type */
The answer may well be different for C, where sizeof is not necessarily a compile-time construct, but in C++ the expression provided to sizeof is never evaluated. As such, there is never a possibility for undefined behavior to exhibit itself. By similar logic, you can also "call" functions that are never defined [because the function is never actually called, no definition is necessary], a fact that is frequently used in SFINAE rules.
sizeof and decltype do not evaluate their operands, computing types only.
sizeof(*ptr) is the same as sizeof(int) in this case.
Since sizeof does not evaluate its operand (except in the case of variable length arrays if you're using C99 or later), in the expression sizeof (*ptr), ptr is not evaluated, therefore it is not dereferenced. The sizeof operator only needs to determine the type of the expression *ptr to get the appropriate size.
For example:
int x[100];
void *p;
x[0] = 0x12345678;
x[1] = 0xfacecafe;
x[3] = 0xdeadbeef;
p = x;
((int *) p) ++ ;
printf("The value = 0x%08x", *(int*)p);
Compiling the above generates an lvalue required error on the line with the ++ operator.
The cast creates a temporary pointer of type int *. You can't increment a temporary as it doesn't denote a place to store the result.
In C and C++ standardese, (int *)p is an rvalue, which roughly means an expression that can only occur on the right-hand side of an assignment.
p on the other hand is an lvalue, which means it can validly appear on the left-hand side of an assignment. Only lvalues can be incremented.
The expression ((int *) p) treats the pointer stored inside the variable p is a pointer to int. If you want to treat the variable itself as a pointer to int variable (and then increment it), use a reference cast:
((int *&) p) ++ ;
Thanks to larsmans for pointing to the right direction.
I took the liberty of digging deeper into this. So for future reference, according to sections 6.5.2.4 and 6.5.4 of the C99 standard (http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf):
6.5.2.4 Postfix increment and decrement operators
Constraints
The operand of the postfix increment
or decrement operator shall have
qualified or unqualified real or
pointer type and shall be a modifiable
lvalue....
6.5.4 Cast operators
..
..
[Footnote] 89) A cast
does not yield an lvalue. Thus, a cast
to a qualified type has the same
effect as a cast to the unqualified
version of the type.
Note: This only applies to C. C++ may handle casts differently.
You can get the intended result with
p = (int*)p + 1;
Using the increment operator on a dereferenced pointer to p, which is an lvalue, also works:
(*(int**)&p)++;
However, the latter is not portable, since (void*)p might not have the same representation as (int*)p.
Rvalue expression ((int *) p) creates and temporary of type int* on which operator ++ cannot be applied.
++ requires an lvalue as its operand.
As #FredOverflow mentions lvalues and rvalues have very little to do with assignment.
Arrays are lvalues still they cannot be assigned to because they are non-modifiable.
std::string("Prasoon") is an rvalue expression still it can occur on the left side of assignment operator because we are allowed to call member functions( operator = in this case) on temporaries.