I currently learn C, in the kr book there is one paragraph that confuses me:
"Q Rather more surprising, at least at first sight, is the fact that a reference to
a[i] can also be written as *(a i)
. In evaluating a[i]
, C converts it to
*(a i)
immediately; the two forms are equivalent. Applying the operator &
to
both parts of this equivalence, it follows that &a[i]
and a i
are also identical:
a i
is the address of the i
-th element beyond a
. As the other side of this coin,
if pa
is a pointer, expressions may use it with a subscript; pa[i]
is identical to
*(pa i)
. In short, an array-and-index expression is equivalent to one written
as a pointer and offset."
Taken literally it means that &a[i]
is equal to &(*(a i))
, and also &(*(a i))=a i
. But *x
is the VALUE of x
and can, for example, be negative, so &
should not apply to it. I understand that a i
is a[i]
but it was explained in the book right before the paragraph I cited so I don't get what is this paragraph for or what's even written in it.
While I am at it, am I right in thinking that for say 3 adjacent objects of some type(whether they officially make up an array or not) if the pointer x of the same type points to the first of them the address it contains is the address of the first byte of first object and x 1
is not x 1
in bytes, but x 1*sizeof(the type in question)
?
CodePudding user response:
But
*x
is the VALUE ofx
…
*x
is not the value of x
, nor is it the value of the object that x
points to.
To start, let’s use a simple object such as int y
. This defines y
to be an object, which is a portion of storage (computer memory) that can represent values. After the definition int y;
, y
refers to the object.
Now consider an assignment such as y = y 1
. This assignment takes the value of y
, adds 1, and stores the result in y
. This is strange: In y 1
, the value of y
is used. But in y =
, something is stored in the object y
. So y
is used in two different ways in y = y 1
.
This works because of a rule in the C standard. C 2018 6.3.2.1 2 says:
Except when it is the operand of the
sizeof
operator, the unary&
operator, theoperator, the
--
operator, or the left operand of the.
operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue); this is called lvalue conversion…
In y = y 1
, the first y
is the left operand of an assignment operator. So it is not converted to its value; it continues to refer to the object y
. But the second y
is not the operand of any of the listed operators, so it is converted to its value. (To convert an object to its value, the object’s bytes are loaded from memory, after which they can be interpreted as a value.)
Now we can go back to your *x
. x
is a pointer to an object. So *x
is a reference to that object. If we write *x = *x 1
, the first *x
will mean the object, and the second *x
will be converted to the value of *x
.
If we have the expression &*x
, then the exceptions to the 6.3.2.1 2 rule apply, because it includes the operand of the unary &
operator as an exception. In &*x
, the *x
is not converted to the value in *x
; it remains a reference to the object. And the &
operator gives the address of that object.
While I am at it, am I right in thinking that for say 3 adjacent objects of some type(whether they officially make up an array or not) if the pointer x of the same type points to the first of them the address it contains is the address of the first byte of first object and
x 1
is notx 1
in bytes, butx 1*sizeof(the type in question)
?
If x
points to an object, then x 1
points to the memory location just past the end of the object, and sizeof *x
is the number of bytes in the object. If we convert x
to a pointer to char
, with (char *) x
, that gives us a pointer to the first byte of the object. Then (char *) x sizeof *x
points to the byte just beyond the end of *x
.
So x 1
and (char *) x sizeof *x
point to the same place. However, they have different types, so we cannot directly compare them for equality. If we convert the first one to char *
, they will compare as equal: (char *) (x 1) == (char *) x sizeof *x
will evaluate as true.
CodePudding user response:
But *x is the VALUE of x and can, for example, be negative, so & should not apply to it.
Rather, it is an lvalue meaning it is a value which is stored at an addressable location. In case of &*
then the C standard (C17 6.5.3.2) says:
The unary & operator yields the address of its operand. If the operand has type ‘‘type’’, the result has type ‘‘pointer to type’’. If the operand is the result of a unary
*
operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue.
Meaning that in variable
is equivalent to &*variable
regardless of what type variable
happens to be. And in case of &*(a i)
that's indeed the same as a i
.
As for the quoted parts of the book, "a[i]
can also be written as *(a i)
" is strictly speaking not correct. It should say *((a) (i))
. For example consider a[i & mask]
- this is not equivalent to *(a i & mask)
but to *((a) (i & mask))
. Important details like that are often missing in K&R.
x 1 is not x 1 in bytes, but x 1*sizeof(the type in question)
Yes. Although to apply pointer arithmetic, x
must be an array. Or a plain variable, in which case it is regarded as an array of 1 item in which we can only de-reference that first item.