Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   C Programming (http://www.velocityreviews.com/forums/f42-c-programming.html)
-   -   Casts on lvalues (http://www.velocityreviews.com/forums/t955029-casts-on-lvalues.html)

BartC 12-02-2012 01:23 PM

Casts on lvalues
 
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

SG 12-02-2012 01:40 PM

Re: Casts on lvalues
 
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.


Ben Bacarisse 12-02-2012 01:53 PM

Re: Casts on lvalues
 
"BartC" <bc@freeuk.com> 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.

Keith Thompson 12-02-2012 08:34 PM

Re: Casts on lvalues
 
"BartC" <bc@freeuk.com> 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) kst-u@mib.org <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"

BartC 12-02-2012 09:18 PM

Re: Casts on lvalues
 
"Keith Thompson" <kst-u@mib.org> wrote in message
news:lny5hg8a7r.fsf@nuthaus.mib.org...
> "BartC" <bc@freeuk.com> 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


Eric Sosman 12-02-2012 10:10 PM

Re: Casts on lvalues
 
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
esosman@comcast-dot-net.invalid

James Kuyper 12-03-2012 12:14 AM

Re: Casts on lvalues
 
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

Edward A. Falk 12-04-2012 11:57 PM

Re: Casts on lvalues
 
In article <xDIus.482873$GX.378393@fx01.am4>, BartC <bc@freeuk.com> 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, falk@despams.r.us.com
http://thespamdiaries.blogspot.com/

BartC 12-05-2012 01:35 AM

Re: Casts on lvalues
 
"Edward A. Falk" <falk@rahul.net> wrote in message
news:k9m2m6$vfu$1@blue-new.rahul.net...
> In article <xDIus.482873$GX.378393@fx01.am4>, BartC <bc@freeuk.com> 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


Piotr Kalinowski 12-05-2012 03:07 PM

Re: Casts on lvalues
 
"BartC" <bc@freeuk.com> writes:

> "Edward A. Falk" <falk@rahul.net> wrote in message
> news:k9m2m6$vfu$1@blue-new.rahul.net...
> 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


All times are GMT. The time now is 01:42 PM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.