Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #20224 > unrolled thread

Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type?

Started byJohn Reye <jononanon@googlemail.com>
First post2012-05-02 09:06 -0700
Last post2012-05-07 17:16 -0700
Articles 11 on this page of 31 — 10 participants

Back to article view | Back to comp.lang.c


Contents

  Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? John Reye <jononanon@googlemail.com> - 2012-05-02 09:06 -0700
    Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? "In a very dark China Blue, China Blue condition." <chine.bleu@yahoo.com> - 2012-05-02 09:33 -0700
    Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? James Kuyper <jameskuyper@verizon.net> - 2012-05-02 12:36 -0400
      Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? John Reye <jononanon@googlemail.com> - 2012-05-02 10:04 -0700
        Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? James Kuyper <jameskuyper@verizon.net> - 2012-05-02 13:23 -0400
      Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? John Reye <jononanon@googlemail.com> - 2012-05-03 09:20 -0700
        Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? John Reye <jononanon@googlemail.com> - 2012-05-03 09:25 -0700
          Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? John Reye <jononanon@googlemail.com> - 2012-05-03 10:16 -0700
          Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? John Reye <jononanon@googlemail.com> - 2012-05-03 11:05 -0700
          Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? James Kuyper <jameskuyper@verizon.net> - 2012-05-03 15:21 -0400
        Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? John Reye <jononanon@googlemail.com> - 2012-05-03 09:46 -0700
        Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? Nobody <nobody@nowhere.com> - 2012-05-03 19:14 +0100
          Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? John Reye <jononanon@googlemail.com> - 2012-05-03 11:45 -0700
            Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? Jens Gustedt <jens.gustedt@loria.fr> - 2012-05-03 21:05 +0200
              Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? John Reye <jononanon@googlemail.com> - 2012-05-03 12:14 -0700
              Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? James Kuyper <jameskuyper@verizon.net> - 2012-05-03 15:16 -0400
                Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? Lanarcam <lanarcam1@yahoo.fr> - 2012-05-03 21:56 +0200
                  Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? James Kuyper <jameskuyper@verizon.net> - 2012-05-03 16:12 -0400
                    Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? John Reye <jononanon@googlemail.com> - 2012-05-03 13:45 -0700
                    Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? Nobody <nobody@nowhere.com> - 2012-05-04 01:34 +0100
                Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? Jens Gustedt <jens.gustedt@loria.fr> - 2012-05-03 22:32 +0200
              Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? John Reye <jononanon@googlemail.com> - 2012-05-03 12:10 -0700
                Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? Keith Thompson <kst-u@mib.org> - 2012-05-03 16:14 -0700
              Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? Tim Rentsch <txr@alumni.caltech.edu> - 2012-05-07 17:24 -0700
            Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? James Kuyper <jameskuyper@verizon.net> - 2012-05-03 15:19 -0400
              Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? John Reye <jononanon@googlemail.com> - 2012-05-03 12:25 -0700
        Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? James Kuyper <jameskuyper@verizon.net> - 2012-05-03 17:44 -0400
    Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? Barry Schwarz <schwarzb@dqel.com> - 2012-05-02 12:35 -0700
      Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? "christian.bau" <christian.bau@cbau.wanadoo.co.uk> - 2012-05-02 14:40 -0700
      Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? James Kuyper <jameskuyper@verizon.net> - 2012-05-02 17:51 -0400
      Re: Can *common* struct-members of 2 different struct-types, that are the same for the first common members, be accessed via pointer cast to either struct-type? Tim Rentsch <txr@alumni.caltech.edu> - 2012-05-07 17:16 -0700

Page 2 of 2 — ← Prev page 1 [2]


#20343

FromJens Gustedt <jens.gustedt@loria.fr>
Date2012-05-03 22:32 +0200
Message-ID<4FA2EB4B.4010908@loria.fr>
In reply to#20327
Am 05/03/2012 09:16 PM, schrieb James Kuyper:
> On 05/03/2012 03:05 PM, Jens Gustedt wrote:
>> Am 05/03/2012 08:45 PM, schrieb John Reye:
>>> char a;
>>> char *p = &a;
>>>
>>> a = 1;
>>> *p = 2;
>>> printf("%d\n", a);
>>>
>>> Does the C standard guarantee, that the value 2 will get printed in
>>> the above code??
>>
>> no
> 
> Why not? In the abstract machine those statements must be executed in
> sequence. There's a sequence point at the end of each statement, which
> means that side effects such as the change in value of a in the second
> statement, must be complete by the time the value of 'a' is read by the
> third statement.

ah, right, I missed the fact that this was a pointer of the same (and
char) type.

>> for that you'd have to declare them
>>
>> char volatile a;
>> char volatile *p = &a;
> 
> Why should that be necessary?

so it should only be necessary if you'd do a modification through a
pointer of different type.

Thinking of it, I wonder if the change would be necessarily visible if
we had

unsigned a = 1;
unsigned two = 2;
memcpy(&a, &two, sizeof a);
printf("%u\n", a);

Jens

[toc] | [prev] | [next] | [standalone]


#20336

FromJohn Reye <jononanon@googlemail.com>
Date2012-05-03 12:10 -0700
Message-ID<b209821f-7452-4c93-bdea-115cb039944d@y7g2000vbx.googlegroups.com>
In reply to#20323
On May 3, 9:05 pm, Jens Gustedt wrote:
> > char a;
> > char *p = &a;
>
> > a = 1;
> > *p = 2;
> > printf("%d\n", a);
>
> > Does the C standard guarantee, that the value 2 will get printed in
> > the above code??
>
> no
>
> for that you'd have to declare them
>
> char volatile a;
> char volatile *p = &a;
>
> Jens


Thanks!!!
So it would seem that good C coders sprinkle a lot of volatile. ;)

[toc] | [prev] | [next] | [standalone]


#20367

FromKeith Thompson <kst-u@mib.org>
Date2012-05-03 16:14 -0700
Message-ID<ln7gwsoo1r.fsf@nuthaus.mib.org>
In reply to#20336
John Reye <jononanon@googlemail.com> writes:
> On May 3, 9:05 pm, Jens Gustedt wrote:
>> > char a;
>> > char *p = &a;
>>
>> > a = 1;
>> > *p = 2;
>> > printf("%d\n", a);
>>
>> > Does the C standard guarantee, that the value 2 will get printed in
>> > the above code??
>>
>> no
>>
>> for that you'd have to declare them
>>
>> char volatile a;
>> char volatile *p = &a;
>
> Thanks!!!
> So it would seem that good C coders sprinkle a lot of volatile. ;)

volatile is completely unnecessary in this case; the original code is
well 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"

[toc] | [prev] | [next] | [standalone]


#20476

FromTim Rentsch <txr@alumni.caltech.edu>
Date2012-05-07 17:24 -0700
Message-ID<kfnd36fzfhs.fsf@x-alumni2.alumni.caltech.edu>
In reply to#20323
Jens Gustedt <jens.gustedt@loria.fr> writes:

> Am 05/03/2012 08:45 PM, schrieb John Reye:
>> On May 3, 8:14 pm, Nobody wrote:
>>> The compiler is free to lose track of that information during
>>> optimisation, and may well do so.
>> 
>> char a;
>> char *p = &a;
>> 
>> a = 1;
>> *p = 2;
>> printf("%d\n", a);
>> 
>> Does the C standard guarantee, that the value 2 will get printed in
>> the above code??
>
> no
>
> for that you'd have to declare them
>
> char volatile a;
> char volatile *p = &a;

This suggestion is dangerously wrong.  Not only is using
'volatile' irrelevant in this case, it makes no difference in any
other case (ie, of type punning).  The effective type rules delimit
what types of accesses are permitted, and adding volatile doesn't
change that -- using 'volatile double *' to access a value stored
using a 'volatile long *' is undefined behavior just as much as
'double *' and 'long *' would be.  In practical terms the
'volatile' variants may have a better chance of working, but as
far as the Standard goes they are equally undefined behavior.

[toc] | [prev] | [next] | [standalone]


#20329

FromJames Kuyper <jameskuyper@verizon.net>
Date2012-05-03 15:19 -0400
Message-ID<4FA2DA34.7050406@verizon.net>
In reply to#20320
On 05/03/2012 02:45 PM, John Reye wrote:
> On May 3, 8:14 pm, Nobody wrote:
>> The compiler is free to lose track of that information during
>> optimisation, and may well do so.
> 
> char a;
> char *p = &a;
> 
> a = 1;
> *p = 2;
> printf("%d\n", a);
> 
> Does the C standard guarantee, that the value 2 will get printed in
> the above code??

Yes.

The key point is that *p and a have the same type, so there's no
violation of the anti-aliasing rules. There's also a special exemption
in those rules for character types, so the behavior would remain
well-defined even if 'a' had the type 'int'.

[toc] | [prev] | [next] | [standalone]


#20334

FromJohn Reye <jononanon@googlemail.com>
Date2012-05-03 12:25 -0700
Message-ID<43b9a1fe-c2af-44b1-8172-3d113deb61ad@z14g2000vbh.googlegroups.com>
In reply to#20329
On May 3, 9:19 pm, James Kuyper <jameskuy...@verizon.net> wrote:
> On 05/03/2012 02:45 PM, John Reye wrote:
>
> > On May 3, 8:14 pm, Nobody wrote:
> >> The compiler is free to lose track of that information during
> >> optimisation, and may well do so.
>
> > char a;
> > char *p = &a;
>
> > a = 1;
> > *p = 2;
> > printf("%d\n", a);
>
> > Does the C standard guarantee, that the value 2 will get printed in
> > the above code??
>
> Yes.
>
> The key point is that *p and a have the same type, so there's no
> violation of the anti-aliasing rules. There's also a special exemption
> in those rules for character types, so the behavior would remain
> well-defined even if 'a' had the type 'int'.

Great, thanks.
(I'm quite relieved, otherwise I might have started sprinkling
unnecessary "volatiles"!!)

[toc] | [prev] | [next] | [standalone]


#20358

FromJames Kuyper <jameskuyper@verizon.net>
Date2012-05-03 17:44 -0400
Message-ID<4FA2FC33.4000004@verizon.net>
In reply to#20295
On 05/03/2012 12:20 PM, John Reye wrote:
> On May 2, 6:36 pm, James Kuyper  wrote:
>> the implementation is not
>> obligated to consider the possibility that an lvalue referring to a
>> "struct a" object refers to the same object as one that refers to a
>> "struct b" object. As a result, when implementing code such as the
>> following:
>>
>>         tmp.c1 = 1;
>>         printf("%d\n", ((struct b*)&tmp)->c1);
>>
>> An implementation is not required to notice that the printf() is
>> referring to the same object as the assignment statement.
> 
> 
> Hmmm... I think that would be one heck of a rubbish compiler (or more
> precisely *optimizing* compiler)! ;)
> If the standard allows that kind of stuff, then it simply is not
> bullet-proof enough.

Many people think so; C is not the language to use if you want
bullet-proof code.

> Because tmp occurs in both lines. Every compiler should notice that!

The rule violated by your code is written to cover situations where that
is not the case, without creating a special exception for when it is the
case. Type punning is very fundamentally a hack; if you really want to
write for bullet-proof code, you want a language where such a hack
constitutes a syntax error or a constraint violation; ideally, a
language which doesn't even present you with a mechanism for describing
such behavior. For bullet-proof code, you don't want a language which
actually defines the behavior that C leaves undefined.

> Even if I "obfuscate" like this:
>          tmp.c1 = 1;
>          char *cp = ((struct b*)&tmp);
>          printf("%d\n", cp->c1);
> 
> I'd expect any compiler that gets it wrong, to be complete rubbish.
> Why? Because it's an optimizer BUG.
> Why?
> Because any simple compiler, that does not optimize... get's it right!
> And if any simple compiler get's it right, then any optimization must
> guarantee to get it right as well.
> 
> I mean: if the C standard allows one to create optimizers that result
> in such ... ummm... surprises (read: "rubbish"), then the standard is
> faulty in my eyes.

OK: here's an example of the kind of code for which the relevant rules
were defined, which may give you a better understanding of why they
exist. It's enormously simplified from any realistic example, but the
key features of this example that are relevant to the aliasing rules are
also commonplace features of much real-world code:

struct department {
    int department_id;
    // ...
};

struct employee {
    int employee_id;
    int department_id;
    // ...
};

// The key point is that employee_id and department_id are both at the
// start of their structs, and have the same type.

static void lay_off_employees(
    struct department *dept,
    struct employee employees[],
    int num
){
    for(int emp = 0; emp<num; emp++)
    {
        if(employees[emp].department_id == dept->department_id)
        {
            // Deal with lots of other issues.
            // We're done now!
            employees[emp].employee_id = -1;
        }
    }
    // ...
}

Key point to notice: dept->department_id is not changed through an
lvalue of type "struct department" anywhere inside the lay_off_employees
loop. It can therefore be treated as a loop invariant, which need only
actually be evaluated once, before the loop begins. In other words, the
implementation is free to compile that function as if it were written:

    int dept_id = dept->department_id;
    for(int emp=0; emp<num; emp++)
    {
        if(employee[emp].department_id == dept_id)
    ...

This is one of the simplest and oldest of loop optimization strategies.
Now, consider the following ridiculous code:

    employees[523].employee_id = human_resources.department_id;
    lay_off_employees((struct department*)(employees+523),
        employees, 600);

The possibility of writing such code means that dept->department_id is
not actually a loop invariant. If it weren't for C's anti-aliasing
rules, the behavior of such code would be defined, and as soon as
emp==523, the value of dept->department_id would have to change to -1.

The simplest approach to making that work would be to accept the
performance hit of repeatedly re-evaluating dept->department_id during
every pass through the loop, just in case it might change as a result of
something that happens inside the loop. There are more complicated
approaches can be used to reduce the performance hit, by retrieving it
only once at the start of the loop, and a second time after emp==523.
However, those ARE more complicated approaches, and they don't scale
well when the number of different expressions that might alias each
other gets as large as 5 or 10 different expressions (in typical
real-world code the number of potentially-aliasing expressions is much
larger than that, particularly if there's a lot of pointers floating
around).

Because it is undefined behavior for *dept to refer to the same object
as employees[emp], an implementation is allowed to not worry about the
possibility that someone would write such ridiculous code, so they can
treat dept->department_id as if it actually were a loop invariant that
could be moved out of the loop. Only code that has undefined behavior
would be broken by treating it as a loop invariant.

This seems like a trivial issue in this simplified context, but keep in
mind that, without the anti-aliasing rules, any lvalue derived from a
pointer could potentially alias any other lvalue, if that pointer had
the right value and compatible alignment requirements. The anti-aliasing
rules guarantee that a compiler only needs to worry about aliasing
between lvalues of certain types, greatly reducing the problem, with
corresponding increases in the feasibility of performing such optimizations.

[toc] | [prev] | [next] | [standalone]


#20234

FromBarry Schwarz <schwarzb@dqel.com>
Date2012-05-02 12:35 -0700
Message-ID<7133q7d4rf5q8376612uus05rbt5qrvifn@4ax.com>
In reply to#20224
On Wed, 2 May 2012 09:06:22 -0700 (PDT), John Reye
<jononanon@googlemail.com> wrote:

>Assume identical common top struct members:
>
>struct a {
>  int i1;
>  char c1;
>  short sa1[3];
>};
>
>struct b {
>  int i2;
>  char c2;
>  short sa2[3];
>
>  int differ_here;
>};
>
>
>struct a tmp;
>
>
>
>Are the following 2 line always equivalent (as in: yielding the same
>lvalue) and allowed:
>
>tmp.c1
>((struct b*)&tmp)->c1

Since struct b does not contain a member c1, the second line should
produce a diagnostic. 

I appear to be in the minority.  If you change the second line to 
     ((struct b*)&tmp)->c2
and if the two structures are guaranteed to have the same alignment,
then I believe the requirement in 6.5.2.3-5 (which technically only
applies if the two structures are members of a union) would force the
compiler to generate the appropriate code to yield the same lvalue.
This would probably work everywhere except the DS9000.

-- 
Remove del for email

[toc] | [prev] | [next] | [standalone]


#20242

From"christian.bau" <christian.bau@cbau.wanadoo.co.uk>
Date2012-05-02 14:40 -0700
Message-ID<90a01e30-3272-4666-abc7-9f9a763ed34c@c30g2000vby.googlegroups.com>
In reply to#20234
On May 2, 8:35 pm, Barry Schwarz <schwa...@dqel.com> wrote:

> I appear to be in the minority.  If you change the second line to
>      ((struct b*)&tmp)->c2
> and if the two structures are guaranteed to have the same alignment,
> then I believe the requirement in 6.5.2.3-5 (which technically only
> applies if the two structures are members of a union) would force the
> compiler to generate the appropriate code to yield the same lvalue.
> This would probably work everywhere except the DS9000.

Well, it wouldn't work on the DS9000 (which is a 100% standard
conforming but highly malicious implementation), so it is not
portable. But lets say I write

    void f (struct a *p, struct b *q)
    {
        q->c2 = 1;
        p->c1 = 2;
        printf ("%d\n", q->c2);
    }

and call

    f (&tmp, (struct b*) &tmp);

do you think the function might print the number 1? If it could, then
the fact that in one case it is easy to detect for the compiler and
one case it is not, shouldn't make a difference.

[toc] | [prev] | [next] | [standalone]


#20243

FromJames Kuyper <jameskuyper@verizon.net>
Date2012-05-02 17:51 -0400
Message-ID<4FA1AC45.3080805@verizon.net>
In reply to#20234
On 05/02/2012 03:35 PM, Barry Schwarz wrote:
> On Wed, 2 May 2012 09:06:22 -0700 (PDT), John Reye
> <jononanon@googlemail.com> wrote:
> 
>> Assume identical common top struct members:
>>
>> struct a {
>>  int i1;
>>  char c1;
>>  short sa1[3];
>> };
>>
>> struct b {
>>  int i2;
>>  char c2;
>>  short sa2[3];
>>
>>  int differ_here;
>> };
>>
>>
>> struct a tmp;
>>
>>
>>
>> Are the following 2 line always equivalent (as in: yielding the same
>> lvalue) and allowed:
>>
>> tmp.c1
>> ((struct b*)&tmp)->c1
> 
> Since struct b does not contain a member c1, the second line should
> produce a diagnostic. 
> 
> I appear to be in the minority.  If you change the second line to 
>      ((struct b*)&tmp)->c2
> and if the two structures are guaranteed to have the same alignment,
> then I believe the requirement in 6.5.2.3-5 (which technically only
> applies if the two structures are members of a union) would force the
> compiler to generate the appropriate code to yield the same lvalue.

It might generate a retrieval using the same offset from the base of the
struct, but the key issue is whether the value it retrieves from that
location is the one that would, otherwise be considered the "current" value.

> This would probably work everywhere except the DS9000.

So the DS9000 is the only platform that would aggressively optimize
based upon the fact that a "struct a" lvalue could never, with defined
behavior, refer to the same object as a "struct b" lvalue?
I'd thought that the best modern optimizers were more aggressive than
that, at least at their highest levels.

[toc] | [prev] | [next] | [standalone]


#20475

FromTim Rentsch <txr@alumni.caltech.edu>
Date2012-05-07 17:16 -0700
Message-ID<kfnhavrzfv4.fsf@x-alumni2.alumni.caltech.edu>
In reply to#20234
Barry Schwarz <schwarzb@dqel.com> writes:

> On Wed, 2 May 2012 09:06:22 -0700 (PDT), John Reye
> <jononanon@googlemail.com> wrote:
>
>>Assume identical common top struct members:
>>
>>struct a {
>>  int i1;
>>  char c1;
>>  short sa1[3];
>>};
>>
>>struct b {
>>  int i2;
>>  char c2;
>>  short sa2[3];
>>
>>  int differ_here;
>>};
>>
>>
>>struct a tmp;
>>
>>
>>
>>Are the following 2 line always equivalent (as in: yielding the same
>>lvalue) and allowed:
>>
>>tmp.c1
>>((struct b*)&tmp)->c1
>
> Since struct b does not contain a member c1, the second line should
> produce a diagnostic. 
>
> I appear to be in the minority.  If you change the second line to 
>      ((struct b*)&tmp)->c2
> and if the two structures are guaranteed to have the same alignment,
> then I believe the requirement in 6.5.2.3-5 (which technically only
> applies if the two structures are members of a union) would force the
> compiler to generate the appropriate code to yield the same lvalue.
> This would probably work everywhere except the DS9000.

An accident waiting to happen.  The important question isn't
whether it works today, but whether it has to keep working in the
future.  As compilers get better at optimizing, the likelihood
that code like this will have problems becomes non-negligible.
Since the Standards allows them to take advantage of such
cases, compiler writers inevitably will, and then...  kaboom!

[toc] | [prev] | [standalone]


Page 2 of 2 — ← Prev page 1 [2]

Back to top | Article view | comp.lang.c


csiph-web