Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Casts on lvalues

Reply
Thread Tools

Casts on lvalues

 
 
BartC
Guest
Posts: n/a
 
      12-02-2012
Suppose I have these types:

#define byte unsigned char

typedef struct {
int a,b,c,d;
} R; /* assume this is 16 bytes */

And these variables:

int n; /* represents a *byte* offset */
R* p;

I want to be able to do the following:
p += n;

but it doesn't work because n is a byte offset; it's not in terms of R
objects. But the obvious cast:

(byte*)p+=n;

doesn't appear to compile. The workarounds seem to be:

p += n/sizeof(R);

which I don't like. Even though p is always aligned (and n is known to be a
multiple of 16), and I know the divide will cancel out, it seems funny
having to introduce a divide op in the first place. And:

p = (R*)((byte*)p+n);

which is what I'm using but looks very untidy in real code.

In any case, the question remains, *is* there a way to cast an lvalue as
I've shown above?

--
Bartc
 
Reply With Quote
 
 
 
 
SG
Guest
Posts: n/a
 
      12-02-2012
Am 02.12.2012 14:23, schrieb BartC:
> Suppose I have these types:
>
> #define byte unsigned char
>
> typedef struct {
> int a,b,c,d;
> } R; /* assume this is 16 bytes */
>
> And these variables:
>
> int n; /* represents a *byte* offset */
> R* p;
>
> I want to be able to do the following:
> p += n;
>
> but it doesn't work because n is a byte offset; it's not in terms of R
> objects. But the obvious cast:
>
> (byte*)p+=n;
>
> doesn't appear to compile. The workarounds seem to be:
>
> p += n/sizeof(R);
>
> which I don't like. Even though p is always aligned (and n is known to be a
> multiple of 16), and I know the divide will cancel out, it seems funny
> having to introduce a divide op in the first place. And:
>
> p = (R*)((byte*)p+n);
>
> which is what I'm using but looks very untidy in real code.
>
> In any case, the question remains, *is* there a way to cast an lvalue as
> I've shown above?


In C++ there is. But you would invoke undefined behaviour because doing
it in this case violates the aliasing rule. I really don't see the
problem with

p += n/sizeof(R);

It looks like exactly the thing you should write. Maybe throw an

assert(n % sizeof(R) == 0);

in there as well.

 
Reply With Quote
 
 
 
 
Ben Bacarisse
Guest
Posts: n/a
 
      12-02-2012
"BartC" <(E-Mail Removed)> writes:

> Suppose I have these types:
>
> #define byte unsigned char
>
> typedef struct {
> int a,b,c,d;
> } R; /* assume this is 16 bytes */
>
> And these variables:
>
> int n; /* represents a *byte* offset */
> R* p;
>
> I want to be able to do the following:
> p += n;
>
> but it doesn't work because n is a byte offset; it's not in terms of R
> objects. But the obvious cast:
>
> (byte*)p+=n;
>
> doesn't appear to compile. The workarounds seem to be:
>
> p += n/sizeof(R);
>
> which I don't like. Even though p is always aligned (and n is known to be a
> multiple of 16), and I know the divide will cancel out, it seems funny
> having to introduce a divide op in the first place. And:
>
> p = (R*)((byte*)p+n);
>
> which is what I'm using but looks very untidy in real code.
>
> In any case, the question remains, *is* there a way to cast an lvalue as
> I've shown above?


No, I don't think so. Certainly not directly -- a cast expression is
not a lvalue. You can do dangerous thing like:

*(byte **)&p += n; /* don't do this!! */

or use a union with an R * and a char * pointer in it, but both
techniques rely on the representation of char and struct pointers being
the same -- they re-interpret the pointer rather than converting it.

I think your best bet is to tidy up what you currently use

static inline void *addr_inc(void *p, int n) { return (char *)p + n; }

will let you write p = addr_inc(p, n); which is much less messy and not
hard to follow.

--
Ben.
 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      12-02-2012
"BartC" <(E-Mail Removed)> writes:
> Suppose I have these types:
>
> #define byte unsigned char


Why are you using a macro rather than a typedef?

> typedef struct {
> int a,b,c,d;
> } R; /* assume this is 16 bytes */


Or you could assume that it's `sizeof R` bytes.

> And these variables:
>
> int n; /* represents a *byte* offset */
> R* p;
>
> I want to be able to do the following:
> p += n;
>
> but it doesn't work because n is a byte offset; it's not in terms of R
> objects. But the obvious cast:
>
> (byte*)p+=n;
>
> doesn't appear to compile.


Right. A cast operator doesn't require an lvalue as its operand.
If the operand happens to be an lvalue, it's not treated as one.
It undergoes an "lvalue conversion" as described in N1570 6.3.2.1p2.
The result of a cast is not an lvalue.

> The workarounds seem to be:
>
> p += n/sizeof(R);
>
> which I don't like. Even though p is always aligned (and n is known to be a
> multiple of 16), and I know the divide will cancel out, it seems funny
> having to introduce a divide op in the first place. And:
>
> p = (R*)((byte*)p+n);
>
> which is what I'm using but looks very untidy in real code.
>
> In any case, the question remains, *is* there a way to cast an lvalue as
> I've shown above?


No.

Why is n a byte offset in the first place? You're dealing with
objects of your anonymous struct type; can't you just give your
struct a name and make p a pointer to it? If you need a byte
pointer, you can convert a struct pointer to byte*.

Incidentally, I'd probably use unsigned char directly; it's clear enough
and doesn't make the reader wonder how "byte" has been defined.

--
Keith Thompson (The_Other_Keith) http://www.velocityreviews.com/forums/(E-Mail Removed) <http://www.ghoti.net/~kst>
Will write code for food.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
 
Reply With Quote
 
BartC
Guest
Posts: n/a
 
      12-02-2012
"Keith Thompson" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed)...
> "BartC" <(E-Mail Removed)> writes:


>> int n; /* represents a *byte* offset */
>> R* p;
>>
>> I want to be able to do the following:
>> p += n;
>>
>> but it doesn't work because n is a byte offset; it's not in terms of R
>> objects. But the obvious cast:


> Why is n a byte offset in the first place?


The offsets come from externally generated data, and initially were simple
counts: +2, -1 etc. The code looked like this:

R *p,*q;

p=q+n;

Very nice. Then I noticed this addition involved internally multiplying n by
16 (or a shift as it was), which wasn't so nice! It was just as easy to
generate these numbers as multiples of 16 anyway (+32, -16 etc) so I did
that. But then the code wasn't so pretty.

--
Bartc

 
Reply With Quote
 
Eric Sosman
Guest
Posts: n/a
 
      12-02-2012
On 12/2/2012 4:21 PM, pete wrote:
> Keith Thompson wrote:
>
>> The result of a cast is not an lvalue.

>
> I don't think that the standard explicitly states this;


It's in a footnote to 6.5.4p5. Footnotes are non-normative,
but then there's the definition of lvalue in 6.3.2.1p1:

"An lvalue is an expression [...] that potentially
designates an object [...]"

There is no cast that produces an object designator,[*] hence
there is no cast that produces an lvalue.
[*] You can (sometimes) derive an lvalue from the result
of a cast by applying an operator to it, as in

* (unsigned char*)&foo = 42;

or
((struct baz*)&foo) -> mumble = 17;

> but as far as I can tell,
> the result of any conversion of the type of any expression,
> is not an lvalue.


Even a non-conversion is not an lvalue:

double trouble;
(double)trouble = 3.14; // BZZZT!

--
Eric Sosman
(E-Mail Removed)d
 
Reply With Quote
 
James Kuyper
Guest
Posts: n/a
 
      12-03-2012
On 12/02/2012 04:21 PM, pete wrote:
> Keith Thompson wrote:
>
>> The result of a cast is not an lvalue.

>
> I don't think that the standard explicitly states this;
> but as far as I can tell,
> the result of any conversion of the type of any expression,
> is not an lvalue.


The standard says that something is not an lvalue in only a few places:
6.3.2.1p3 when an lvalue of array type is converted to a pointer to its
first element.
6.5.2.3p3 function_returning_struct_type().member
6.5.16p3 assignment expression

Most of the time, it only says when something IS an lvalue:
6.5.1p2 object_identifier
6.5.1p4 "string literal"
6.5.1p5 (lvalue)
6.5.1.1p4 _Generic(x, int:lvalue1, default:lvalue2)
6.5.2.3p3 lvalue.member
6.5.2.3p4 expression->member
6.5.2.5p4 compound literal
6.5.3.4p4 *pointer_to_object
6.9.1p9 function parameter identifier
--
James Kuyper
 
Reply With Quote
 
Edward A. Falk
Guest
Posts: n/a
 
      12-04-2012
In article <xDIus.482873$(E-Mail Removed)4>, BartC <(E-Mail Removed)> wrote:
>
>I want to be able to do the following:
> p += n;
>
>but it doesn't work because n is a byte offset; it's not in terms of R
>objects. But the obvious cast:
>
> (byte*)p+=n;
>
>doesn't appear to compile. The workarounds seem to be:
>
> p += n/sizeof(R);


Other options:

p += n/sizeof(*p);

p = (R *)((byte *)p +n)

But frankly, none of these is a very good choice. If I were
code-reviewing something like this, I'd ask the programmer what it is
that they're *really* trying to do. Constructs like this are
a likely sign of broken logic and broken code.

Note also that the n/sizeof(...) variant fails horribly if the
byte offset isn't a multiple of the structure size.

Not to say that something like this should *never* be written --
I can think of a number of cases where multiple data structures
might be packed into a buffer, in which case this is exactly the
sort of thing you might be doing. But in cases like this, you're
best off keeping p as a byte pointer and just casting it to a
pointer to R when you need to dereference it.

I'm assuming you're taking the proper precautions w.r.t. alignment
issues. That's a whole 'nother discussion.

--
-Ed Falk, (E-Mail Removed)
http://thespamdiaries.blogspot.com/
 
Reply With Quote
 
BartC
Guest
Posts: n/a
 
      12-05-2012
"Edward A. Falk" <(E-Mail Removed)> wrote in message
news:k9m2m6$vfu$(E-Mail Removed)...
> In article <xDIus.482873$(E-Mail Removed)4>, BartC <(E-Mail Removed)> wrote:
>>
>>I want to be able to do the following:
>> p += n;
>>
>>but it doesn't work because n is a byte offset; it's not in terms of R


> Other options:
>
> p += n/sizeof(*p);
>
> p = (R *)((byte *)p +n)
>
> But frankly, none of these is a very good choice. If I were
> code-reviewing something like this, I'd ask the programmer what it is
> that they're *really* trying to do. Constructs like this are
> a likely sign of broken logic and broken code.


I've already said elsewhere that using an object rather than a byte offset
involved an unnecessary multiplication or shift.

I noticed it when I wanted shadow some functions with assembly code.

--
Bartc

 
Reply With Quote
 
Piotr Kalinowski
Guest
Posts: n/a
 
      12-05-2012
"BartC" <(E-Mail Removed)> writes:

> "Edward A. Falk" <(E-Mail Removed)> wrote in message
> news:k9m2m6$vfu$(E-Mail Removed)...
> I've already said elsewhere that using an object rather than a byte
> offset involved an unnecessary multiplication or shift.


It was not unnecessary. It's called pointer arithmetic and allowed you
to use more elegant code at slightly higher level of abstraction that
is supposed to more closely represent your intentions.

You have decided this abstraction is unnecessary, because you feel you
need to optimise this shift/multiplication away (and maybe you do, I
couldn't possibly know that). More specifically, you have implicitly
decided this optimisation is worth the trade off in form of increased
complexity of the code (decreased readability or whatever it is that
bugged you enough to actually ask about it, seeking a different way to
express what you are doing).

There's no free lunch. Now deal with the consequences, or revert your
decision, as others already presented answer to your original question.

Regards,
Piotr Kalinowski
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
conversion of array to pointer not limited to lvalues nicolas.sitbon@gmail.com C Programming 18 08-06-2008 04:41 PM
casts and lvalues jacob navia C Programming 68 06-27-2007 03:32 PM
Lvalues and Rvalues ramasubramanian.rahul@gmail.com C Programming 3 10-14-2006 09:55 PM
Avoiding "use of cast expressions in lvalues is deprecated" steve.j.donovan@gmail.com C Programming 23 09-21-2006 05:45 PM
lvalues -> incomplete types Mantorok Redgormor C Programming 7 02-07-2004 03:45 PM



Advertisments