简体   繁体   中英

Why can't casting an address to int* be an lvalue but casting to a struct pointer can?

I suspect this is true for all primitive types in C/C++.

For example, if you do this:

((unsigned int*)0x1234) = 1234;

The compiler will not let it pass. Whereas if you do this

((data_t*)0x1234 )->s = 1234;

where data_t is a struct , the compiler allows it.

This seems to be the case for at least two compilers I experimented on, one ARM GCC, one TDM-GCC.

Why is this?

The first code snippet doesn't work because the left hand side is not an lvalue. It is only a pointer value, and pointers by themselves are not lvalues.

The second code snippet works because a pointer is being dereferenced, and a dereferenced pointer is an lvalue. It may not be immediately clear from the syntax this is the case, so let's rewrite this:

((data_t*)0x1234 )->s = 1234;

As:

(*(data_t*)0x1234).s = 1234;

Now we can see that the value which is casted to a pointer is dereferenced to an lvalue of struct type, and a member of that struct is subsequently accessed and assigned to.

This is described in section 6.5.2.3p4 of the C standard regarding the -> operator:

A postfix expression followed by the -> operator and an identifier designates a member of a structure or union object. The value is that of the named member of the object to which the first expression points, and is an lvalue. If the first expression is a pointer to a qualified type, the result has the so-qualified version of the type of the designated member.

Regarding the first snippet, section 6.5.4p5 regarding the typecast operator states:

Preceding an expression by a parenthesized type name converts the value of the expression to the named type. This construction is called a cast. 104) A cast that specifies no conversion has no effect on the type or value of an expression.

Where footnote 104 states:

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.

So this describes why the first snippet won't compile but the second snippet will.

However, treating an arbitrary value as a pointer and dereferencing it is implementation defined behavior at best, and most likely undefined behavior.

Your examples are:

((unsigned int*)0x1234) = 1234;
((data_t*)0x1234 )->s = 1234;

Neither ((unsigned int*)0x1234) nor ((data_t*)0x1234 ) is an lvalue, and you can't assign to either of them.

More generally, the prefix of -> doesn't have to be an lvalue. But prefix->member is always an lvalue, whether prefix is or not. Similarly, *p is an value whether p is an lvalue or not.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM