late night confusion with -> and .

Discussion in 'C Programming' started by Ark Khasin, Jan 22, 2008.

  1. Ark Khasin

    Ark Khasin Guest

    I heard a few times, and may have repeated thoughtlessly, that p->m is
    in a way a shorthand for (*p).m
    The standard doesn't seem to have a notion of this; this also sounds
    downright wrong if p is a pointer to a volatile type.
    How common is this misconception?
    --
    Thanks,
    Ark
     
    Ark Khasin, Jan 22, 2008
    #1
    1. Advertising

  2. Ark Khasin

    Ben Pfaff Guest

    Ark Khasin <> writes:

    > I heard a few times, and may have repeated thoughtlessly, that p->m is
    > in a way a shorthand for (*p).m
    > The standard doesn't seem to have a notion of this; this also sounds
    > downright wrong if p is a pointer to a volatile type.
    > How common is this misconception?


    I certainly have this misconception.

    I don't immediately see the distinction you're making. Can you
    explain further?
    --
    Ben Pfaff
    http://benpfaff.org
     
    Ben Pfaff, Jan 22, 2008
    #2
    1. Advertising

  3. Ark Khasin

    Ark Khasin Guest

    Ben Pfaff wrote:
    > Ark Khasin <> writes:
    >
    >> I heard a few times, and may have repeated thoughtlessly, that p->m is
    >> in a way a shorthand for (*p).m
    >> The standard doesn't seem to have a notion of this; this also sounds
    >> downright wrong if p is a pointer to a volatile type.
    >> How common is this misconception?

    >
    > I certainly have this misconception.
    >
    > I don't immediately see the distinction you're making. Can you
    > explain further?

    volatile T *p;
    (*p) - as in (*p).m - requires actually reading *p, which in turn may
    affect the content of *p. p->m only affects the member m.
    --
    Ark
     
    Ark Khasin, Jan 22, 2008
    #3
  4. Ark Khasin

    Ark Khasin Guest

    Ark Khasin wrote:
    > Ben Pfaff wrote:
    >> Ark Khasin <> writes:
    >>
    >>> I heard a few times, and may have repeated thoughtlessly, that p->m is
    >>> in a way a shorthand for (*p).m
    >>> The standard doesn't seem to have a notion of this; this also sounds
    >>> downright wrong if p is a pointer to a volatile type.
    >>> How common is this misconception?

    >>
    >> I certainly have this misconception.
    >>
    >> I don't immediately see the distinction you're making. Can you
    >> explain further?

    > volatile T *p;
    > (*p) - as in (*p).m - requires actually reading *p, which in turn may
    > affect the content of *p. p->m only affects the member m.

    And now I am confused even more.
    volatile struct T s;
    "Natural" s.m requires evaluation (read) of s, doesn't it?
    Should I write (&s)->m so as to leave other members unaffected?
    --
    Ark
     
    Ark Khasin, Jan 22, 2008
    #4
  5. Ark Khasin <> wrote:
    > I heard a few times, and may have repeated thoughtlessly,
    > that p->m is in a way a shorthand for (*p).m


    They are potentially distinct in C++ since one can overload
    operators. But that does not apply in C.

    > The standard doesn't seem to have a notion of this;


    The definitions of these operators seem to, at face value,
    imply that relationship. There's even a non-normative
    footnote that says (&E)->m is E->m if &E is valid.

    > this also sounds downright wrong if p is a pointer to a
    > volatile type.


    What difference would that make?

    > How common is this misconception?


    You've yet to demonstrate that it actually is a misconception.

    --
    Peter
     
    Peter Nilsson, Jan 22, 2008
    #5
  6. Ark Khasin

    Ben Pfaff Guest

    Ark Khasin <> writes:
    > Ben Pfaff wrote:
    >> Ark Khasin <> writes:
    >>
    >>> I heard a few times, and may have repeated thoughtlessly, that p->m is
    >>> in a way a shorthand for (*p).m
    >>> The standard doesn't seem to have a notion of this; this also sounds
    >>> downright wrong if p is a pointer to a volatile type.
    >>> How common is this misconception?

    >>
    >> I certainly have this misconception.
    >>
    >> I don't immediately see the distinction you're making. Can you
    >> explain further?

    > volatile T *p;
    > (*p) - as in (*p).m - requires actually reading *p, which in turn may
    > affect the content of *p. p->m only affects the member m.


    Hmm. The unary * operator yields an lvalue that designates an
    object. I suspect that does not necessarily mean that the
    contents of the lvalue is read, even by the abstract machine.
    But I do not have a citation to offer that confirms that
    suspicion. I hope that someone more authoritative has a better
    suggestion.
    --
    char a[]="\n .CJacehknorstu";int putchar(int);int main(void){unsigned long b[]
    ={0x67dffdff,0x9aa9aa6a,0xa77ffda9,0x7da6aa6a,0xa67f6aaa,0xaa9aa9f6,0x11f6},*p
    =b,i=24;for(;p+=!*p;*p/=4)switch(0[p]&3)case 0:{return 0;for(p--;i--;i--)case+
    2:{i++;if(i)break;else default:continue;if(0)case 1:putchar(a[i&15]);break;}}}
     
    Ben Pfaff, Jan 22, 2008
    #6
  7. Ark Khasin

    Ark Khasin Guest

    Peter Nilsson wrote:
    > Ark Khasin <> wrote:
    >> I heard a few times, and may have repeated thoughtlessly,
    >> that p->m is in a way a shorthand for (*p).m

    >
    > They are potentially distinct in C++ since one can overload
    > operators. But that does not apply in C.
    >
    >> The standard doesn't seem to have a notion of this;

    >
    > The definitions of these operators seem to, at face value,
    > imply that relationship. There's even a non-normative
    > footnote that says (&E)->m is E->m if &E is valid.
    >
    >> this also sounds downright wrong if p is a pointer to a
    >> volatile type.

    >
    > What difference would that make?
    >
    >> How common is this misconception?

    >
    > You've yet to demonstrate that it actually is a misconception.
    >
    > --
    > Peter

    Indeed, thank you for pointing out the Footnote 79) If &E is a valid
    pointer expression (where & is the ‘‘address-of ’’ operator, which
    generates a pointer to its operand), the expression (&E)->MOS is the
    same as E.MOS.
    I honestly don't know how to read it: an expression has a value and side
    effects. No-one argues with the values part. It's the side effects that
    bother me...
    --
    Ark
     
    Ark Khasin, Jan 22, 2008
    #7
  8. Ark Khasin

    Ark Khasin Guest

    Ben Pfaff wrote:
    > Ark Khasin <> writes:
    >> Ben Pfaff wrote:
    >>> Ark Khasin <> writes:
    >>>
    >>>> I heard a few times, and may have repeated thoughtlessly, that p->m is
    >>>> in a way a shorthand for (*p).m
    >>>> The standard doesn't seem to have a notion of this; this also sounds
    >>>> downright wrong if p is a pointer to a volatile type.
    >>>> How common is this misconception?
    >>> I certainly have this misconception.
    >>>
    >>> I don't immediately see the distinction you're making. Can you
    >>> explain further?

    >> volatile T *p;
    >> (*p) - as in (*p).m - requires actually reading *p, which in turn may
    >> affect the content of *p. p->m only affects the member m.

    >
    > Hmm. The unary * operator yields an lvalue that designates an
    > object. I suspect that does not necessarily mean that the
    > contents of the lvalue is read, even by the abstract machine.
    > But I do not have a citation to offer that confirms that
    > suspicion. I hope that someone more authoritative has a better
    > suggestion.

    My naive thinking is that if
    volatile unsigned char *uart_receive;
    *uart_receive; //discard the character, clear the interrupt source
    is a valid practice, then unary * does evaluate (read) the value.
    Similarly,
    volatile uart_t *uart;
    uart->receive; //discard the character, clear the interrupt source
    But then
    (*uart).receive;
    seems to do a lot more of unwanted stuff.

    --
    Ark
     
    Ark Khasin, Jan 22, 2008
    #8
  9. Ark Khasin

    user923005 Guest

    On Jan 21, 8:47 pm, Ark Khasin <> wrote:
    > I heard a few times, and may have repeated thoughtlessly, that p->m is
    > in a way a shorthand for (*p).m
    > The standard doesn't seem to have a notion of this; this also sounds
    > downright wrong if p is a pointer to a volatile type.


    If you have an object, and you want to change a member, then it is:
    p.m = foo;

    If you have a pointer to an object, and you want to change a member,
    then it is either:
    p->m = foo;
    Or:
    (*p).m = foo;

    Nobody uses the second form. Everyone uses the first form. I don't
    know why it came out that way, but if you write (*p).m you will get
    raised eyebrows, even though they are totally equivalent expressions.


    > How common is this misconception?


    Really, really rare.
     
    user923005, Jan 22, 2008
    #9
  10. On Jan 22, 6:13 am, Ark Khasin <> wrote:
    > Ben Pfaff wrote:
    > > Ark Khasin <> writes:
    > >> Ben Pfaff wrote:
    > >>> Ark Khasin <> writes:

    >
    > >>>> I heard a few times, and may have repeated thoughtlessly, that p->m is
    > >>>> in a way a shorthand for (*p).m
    > >>>> The standard doesn't seem to have a notion of this; this also sounds
    > >>>> downright wrong if p is a pointer to a volatile type.
    > >>>> How common is this misconception?
    > >>> I certainly have this misconception.

    >
    > >>> I don't immediately see the distinction you're making.  Can you
    > >>> explain further?
    > >> volatile T *p;
    > >> (*p) - as in (*p).m - requires actually reading *p, which in turn may
    > >> affect the content of *p. p->m only affects the member m.

    >
    > > Hmm.  The unary * operator yields an lvalue that designates an
    > > object.  I suspect that does not necessarily mean that the
    > > contents of the lvalue is read, even by the abstract machine.
    > > But I do not have a citation to offer that confirms that
    > > suspicion.  I hope that someone more authoritative has a better
    > > suggestion.

    >
    > My naive thinking is that if
    >     volatile unsigned char *uart_receive;
    >     *uart_receive; //discard the character, clear the interrupt source
    > is a valid practice, then unary * does evaluate (read) the value.


    Severe warning here: This is one of the few places with a very severe
    and very non-obvious between C and C++. Don't do this if you ever want
    to reuse the same code in a C++ program (or you expect that someone
    else _might_ want to do this in ten years time).

    In one language, an lvalue-to-rvalue conversion takes place, which
    accesses *uart_receive. In the other language, no such conversion
    takes place, and there is no access. Better write "unsigned char dummy
    = *uart_receive; ". (And I don't know which language does the access
    and which one doesn't, the distinction is sufficiently obscure that
    writing *uart_receive alone is a bug in each language).
     
    christian.bau, Jan 22, 2008
    #10
  11. Ark Khasin

    Bart C Guest

    "Ark Khasin" <> wrote in message
    news:17flj.6261$YH6.4468@trndny03...
    > Ben Pfaff wrote:
    >> Ark Khasin <> writes:
    >>
    >>> I heard a few times, and may have repeated thoughtlessly, that p->m is
    >>> in a way a shorthand for (*p).m
    >>> The standard doesn't seem to have a notion of this; this also sounds
    >>> downright wrong if p is a pointer to a volatile type.
    >>> How common is this misconception?

    >>
    >> I certainly have this misconception.
    >>
    >> I don't immediately see the distinction you're making. Can you
    >> explain further?


    > volatile T *p;
    > (*p) - as in (*p).m - requires actually reading *p, which in turn may
    > affect the content of *p. p->m only affects the member m.


    *p by itself may well involve reading *p, but it is followed by .m and is
    also an lvalue and you may find that the internal workings of the compiler
    will insert an implicit & in there which will cancel the *.

    Similarly, when writing:

    a=b

    It clearly does not fetch the value of a. The compiler may (I'm guessing)
    treat it as:

    *(&a) = b

    so no loading of a's value occurs, only it's address.

    Bart
     
    Bart C, Jan 22, 2008
    #11
  12. Ark Khasin

    Jack Klein Guest

    On Tue, 22 Jan 2008 04:06:36 -0800 (PST), "christian.bau"
    <> wrote in comp.lang.c:

    > On Jan 22, 6:13 am, Ark Khasin <> wrote:
    > > Ben Pfaff wrote:
    > > > Ark Khasin <> writes:
    > > >> Ben Pfaff wrote:
    > > >>> Ark Khasin <> writes:

    > >
    > > >>>> I heard a few times, and may have repeated thoughtlessly, that p->m is
    > > >>>> in a way a shorthand for (*p).m
    > > >>>> The standard doesn't seem to have a notion of this; this also sounds
    > > >>>> downright wrong if p is a pointer to a volatile type.
    > > >>>> How common is this misconception?
    > > >>> I certainly have this misconception.

    > >
    > > >>> I don't immediately see the distinction you're making.  Can you
    > > >>> explain further?
    > > >> volatile T *p;
    > > >> (*p) - as in (*p).m - requires actually reading *p, which in turn may
    > > >> affect the content of *p. p->m only affects the member m.

    > >
    > > > Hmm.  The unary * operator yields an lvalue that designates an
    > > > object.  I suspect that does not necessarily mean that the
    > > > contents of the lvalue is read, even by the abstract machine.
    > > > But I do not have a citation to offer that confirms that
    > > > suspicion.  I hope that someone more authoritative has a better
    > > > suggestion.

    > >
    > > My naive thinking is that if
    > >     volatile unsigned char *uart_receive;
    > >     *uart_receive; //discard the character, clear the interrupt source
    > > is a valid practice, then unary * does evaluate (read) the value.

    >
    > Severe warning here: This is one of the few places with a very severe
    > and very non-obvious between C and C++. Don't do this if you ever want
    > to reuse the same code in a C++ program (or you expect that someone
    > else _might_ want to do this in ten years time).
    >
    > In one language, an lvalue-to-rvalue conversion takes place, which
    > accesses *uart_receive. In the other language, no such conversion
    > takes place, and there is no access. Better write "unsigned char dummy
    > = *uart_receive; ". (And I don't know which language does the access
    > and which one doesn't, the distinction is sufficiently obscure that
    > writing *uart_receive alone is a bug in each language).


    You are just plain wrong about this. In both languages the lvalue to
    rvalue conversion is performed.

    Perhaps you are confused with the way the way the value yielded by the
    assignment operator can break volatile objects in C++.

    Given:

    /* receive and transmit registers share the same address,
    as in the ubitquious 16c550 family
    */
    volatile unsigned char *uart_rx_tx = /* ... */;

    ....then:

    void uart_xmit(int uch)
    {
    if ('\r' == (*uart_rx_tx = uch))
    {
    *uart_rx_tx = '\n';
    }
    }

    The if evaluation requires the value of the assignment operator.

    In C, the value of the assignment operator is an rvalue, not an
    lvalue. It is the value assigned, that is the rvalue that is actually
    written to the destination. It is the value of the right hand side
    after conversion, if necessary, to the type of the right hand side. So
    it is the value of the int "uch" after truncation to unsigned char.

    In C++, on the other hand, the assignment operators yield lvalues, in
    this case the object pointed to by "uart_rx_tx". Since the value is
    used in the if expression, lvalue to rvalue conversion must be
    performed. Since the object is volatile, the conversion must be
    performed by reading the value of the object.

    So a conforming C++ compiler must first convert the int "uch" to an
    unsigned char, write that unsigned char via the pointer, then read
    back the value via the pointer to yield the value of the assignment.
    In the process, it will not read the value just written to the UART's
    transmit register, but will instead read a received character from the
    UART's received register.

    --
    Jack Klein
    Home: http://JK-Technology.Com
    FAQs for
    comp.lang.c http://c-faq.com/
    comp.lang.c++ http://www.parashift.com/c -faq-lite/
    alt.comp.lang.learn.c-c++
    http://www.club.cc.cmu.edu/~ajo/docs/FAQ-acllc.html
     
    Jack Klein, Jan 23, 2008
    #12
  13. Ark Khasin

    Ben Pfaff Guest

    Jack Klein <> writes:

    > On Tue, 22 Jan 2008 04:06:36 -0800 (PST), "christian.bau"
    > <> wrote in comp.lang.c:
    >> In one language, an lvalue-to-rvalue conversion takes place, which
    >> accesses *uart_receive. In the other language, no such conversion
    >> takes place, and there is no access.

    >
    > You are just plain wrong about this. In both languages the lvalue to
    > rvalue conversion is performed.


    C does not have a conversion called an lvalue-to-rvalue
    conversion. C++ does (ISO 14882:1998 clause 4.2
    "Lvalue-to-rvalue conversion"). This is probably the distinction
    that Christian is making.
    --
    char a[]="\n .CJacehknorstu";int putchar(int);int main(void){unsigned long b[]
    ={0x67dffdff,0x9aa9aa6a,0xa77ffda9,0x7da6aa6a,0xa67f6aaa,0xaa9aa9f6,0x11f6},*p
    =b,i=24;for(;p+=!*p;*p/=4)switch(0[p]&3)case 0:{return 0;for(p--;i--;i--)case+
    2:{i++;if(i)break;else default:continue;if(0)case 1:putchar(a[i&15]);break;}}}
     
    Ben Pfaff, Jan 23, 2008
    #13
  14. On Wed, 23 Jan 2008 08:29:27 -0800, Ben Pfaff wrote:
    > Jack Klein <> writes:
    >> On Tue, 22 Jan 2008 04:06:36 -0800 (PST), "christian.bau"
    >> <> wrote in comp.lang.c:
    >>> In one language, an lvalue-to-rvalue conversion takes place, which
    >>> accesses *uart_receive. In the other language, no such conversion
    >>> takes place, and there is no access.

    >>
    >> You are just plain wrong about this. In both languages the lvalue to
    >> rvalue conversion is performed.

    >
    > C does not have a conversion called an lvalue-to-rvalue conversion.


    Technically true, but C does have an lvalue-to-value conversion, which
    might as well be called an lvalue-to-rvalue conversion.

    6.3.2.1p2:
    "Except when it is the operand of the sizeof operator, the unary &
    operator, the ++ operator, 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). [...]"

    > C++ does (ISO 14882:1998 clause 4.2
    > "Lvalue-to-rvalue conversion"). This is probably the distinction
    > that Christian is making.


    Considering C is the language in which the lvalue-to-rvalue conversion
    takes place, no, probably not. <OT>In C++, an lvalue expression
    statement, even of qualified type, is not converted to an rvalue, because
    the lvalue-to-rvalue conversion cannot generally be optimised away even
    if the rvalue is immediately discarded.</OT>
     
    Harald van Dijk, Jan 23, 2008
    #14
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. =?Utf-8?B?Qi4gQ2hlcm5pY2s=?=

    Late night beginners question

    =?Utf-8?B?Qi4gQ2hlcm5pY2s=?=, Jul 31, 2005, in forum: ASP .Net
    Replies:
    4
    Views:
    364
    =?Utf-8?B?Qi4gQ2hlcm5pY2s=?=
    Jul 31, 2005
  2. Remarkable
    Replies:
    1
    Views:
    328
  3. Replies:
    0
    Views:
    587
  4. nais-saudi
    Replies:
    0
    Views:
    428
    nais-saudi
    Sep 12, 2010
  5. Trans
    Replies:
    0
    Views:
    84
    Trans
    Apr 2, 2008
Loading...

Share This Page