Alignment, placement new, trap representations

Discussion in 'C++' started by Peter Ammon, Aug 25, 2004.

  1. Peter Ammon

    Peter Ammon Guest

    Here's some code under consideration:

    struct s {
    int i;
    };

    char buff[sizeof(s)];

    new(buff) s;



    I claim that this code is potentially dangerous because buff may not
    have the alignment that struct s requires. My opponent claims that this
    is not a concern, and if it were, then placement new would be mostly
    useless.

    He also claims that buff should be typed as an array of unsigned char,
    because otherwise you risk generating a trap representation for one of
    the chars. I claim that's not a problem until you use the value of one
    of the chars in buff.

    Who's right for each of the two above points of contention? Thanks,

    -Peter
    Peter Ammon, Aug 25, 2004
    #1
    1. Advertising

  2. Peter Ammon

    David Hilsee Guest

    "Peter Ammon" <> wrote in message
    news:cgghum$ce5$...
    > Here's some code under consideration:
    >
    > struct s {
    > int i;
    > };
    >
    > char buff[sizeof(s)];
    >
    > new(buff) s;
    >
    >
    >
    > I claim that this code is potentially dangerous because buff may not
    > have the alignment that struct s requires. My opponent claims that this
    > is not a concern, and if it were, then placement new would be mostly
    > useless.


    Placement new would not be useless. Placement new, if applied to
    dynamically allocated memory, works very well. How is invoking placement
    new on a dynamically allocated buffer all that different from casting the
    dynamically allocated buffer to a type and initializing it manually,
    C-style? That "manual" approach essentially the only option you have in C
    when you allocate memory dynamically.

    Sutter's GOTW covers the alignment issues that can result from using an
    array-of-char to store an object in his "Fast Pimpl" article:
    http://www.gotw.ca/gotw/028.htm

    > He also claims that buff should be typed as an array of unsigned char,
    > because otherwise you risk generating a trap representation for one of
    > the chars. I claim that's not a problem until you use the value of one
    > of the chars in buff.


    A quote from the C standard I found on Google:

    6.2.6.1#5: Certain object representations need not represent a value
    of the object type. If the stored value of an object has such a
    representation and is read by an lvalue expression that does not have
    character type, the behavior is undefined. If such a representation is
    produced by a side effect that modifies all or any part of the object by
    an lvalue expression that does not have character type, the behavior is
    undefined. Such a representation is called a trap representation.

    So, if I read that correctly, you're correct in assuming that the UB occurs
    when the value is read. However, since many of the I/O functions that
    perform unformatted output (e.g. std::eek:stream::write) take char *'s, I would
    be surprised if a char could have a trap representation, because that would
    mean that you could accidentally invoke UB by trying to write something to a
    stream. The quote seems to support my expectations by specifying that it
    must not have _character type_ to have a trap representation. I may be
    missing something, though.

    The readers of comp.lang.c discuss trap representation more frequently than
    the comp.lang.c++ folks. You might want to consider removing the
    C++-specific bits of the code and see what they say.

    --
    David Hilsee
    David Hilsee, Aug 25, 2004
    #2
    1. Advertising

  3. Peter Ammon

    Rolf Magnus Guest

    Peter Ammon wrote:

    > Here's some code under consideration:
    >
    > struct s {
    > int i;
    > };
    >
    > char buff[sizeof(s)];
    >
    > new(buff) s;
    >
    >
    >
    > I claim that this code is potentially dangerous because buff may not
    > have the alignment that struct s requires.


    That's correct.

    > My opponent claims that this is not a concern, and if it were, then
    > placement new would be mostly useless.


    Well, what would your opponent see as major use of placement new then?

    > He also claims that buff should be typed as an array of unsigned char,
    > because otherwise you risk generating a trap representation for one of
    > the chars.


    And why would there be a difference in this regard between char and
    unsigned char?

    > I claim that's not a problem until you use the value of
    > one of the chars in buff.
    >
    > Who's right for each of the two above points of contention? Thanks,


    For the first one, the point clearly goes to you. For the second one,
    I'm not sure I really understood it correctly.
    Rolf Magnus, Aug 25, 2004
    #3
  4. Peter Ammon wrote:
    >
    > Here's some code under consideration:
    >
    > struct s {
    > int i;
    > };
    >
    > char buff[sizeof(s)];
    >
    > new(buff) s;
    >
    > I claim that this code is potentially dangerous because buff may not
    > have the alignment that struct s requires. My opponent claims that this
    > is not a concern, and if it were, then placement new would be mostly
    > useless.
    >


    In addition to what David has said, 5.3.4/10 explicitly allows using
    character arrays allocated with new for placement-construction of
    appropriately sized objects of other types.

    On the other hand, the alignment of buff defined in constructs such as
    void f() {
    char buff[sizeof(s)];
    }
    or
    struct Bbb {
    char bbb; //attempted (though not guaranteed) malice
    char buff[sizeof(s)];
    };
    new Bbb;

    is unspecified, and so is certainly a concern.

    Denis
    Denis Remezov, Aug 25, 2004
    #4
  5. Rolf Magnus wrote:
    >
    > Peter Ammon wrote:
    >
    > > Here's some code under consideration:
    > >
    > > struct s {
    > > int i;
    > > };
    > >
    > > char buff[sizeof(s)];
    > >
    > > new(buff) s;


    [...]

    > > He also claims that buff should be typed as an array of unsigned char,
    > > because otherwise you risk generating a trap representation for one of
    > > the chars.

    >
    > And why would there be a difference in this regard between char and
    > unsigned char?
    >


    The way I understand it, all bit patterns of unsigned char represent
    valid numbers, but signed char, on the other hand, is allowed to have
    a trap representation (have there existed any real examples? perhaps
    minus zero on one's complement systems?). Char may be implemented
    the same way as either unsigned or signed char (still being a
    distinct type).
    I would like to ascertain that myself, so consider what I've just said
    speculations.

    For all that, I think that this trap representation possibility is
    irrelevant as long as buff elements are not accessed as chars. It
    is allowed, for example, to copy a POD object into an array of char
    and back by using memcpy or memmove.

    Denis
    Denis Remezov, Aug 25, 2004
    #5
  6. Peter Ammon

    Old Wolf Guest

    Peter Ammon <> wrote:
    > Here's some code under consideration:
    >
    > struct s {
    > int i;
    > };
    >
    > char buff[sizeof(s)];
    >
    > new(buff) s;
    >
    > I claim that this code is potentially dangerous because buff may not
    > have the alignment that struct s requires.


    Yes

    > My opponent claims that this is not a concern, and if it were,
    > then placement new would be mostly useless.


    placement new should be used with memory allocated on the freestore.
    "operator new" is guaranteed to return memory that is aligned for
    any type. For placement new you could go:

    char *buff = new char[sizeof(s)];

    although the more usual syntax is:

    void *buff = ::eek:perator new(sizeof(s));
    new(buff) s;

    This syntax also avoids the signed char issue.

    > He also claims that buff should be typed as an array of unsigned char,
    > because otherwise you risk generating a trap representation for one of
    > the chars. I claim that's not a problem until you use the value of one
    > of the chars in buff.


    There has been some debate over whether signed char can have trap
    representations, the consensus seems to be that it can't (search
    the archives for "trap representation" "signed char").

    Anyhow, memory does not have a type, only expressions do.
    AFAIK you can copy whatever garbage you like into a memory location,
    as long as the copying-expression is a type without traps.
    You only run into difficulty when you access that memory through
    a type that might have a trap representation (or alignment issues).
    For example (still AFAIK):
    {
    int x;
    double y = 0;
    assert(sizeof x <= sizeof y);
    memcpy(&x, &y, sizeof x);
    }
    is well-defined.

    PS. I'm not 100% about anything I've said here (especially that
    last paragraph), so maybe wait for someone else to shoot me down,
    before claiming the win :)
    Old Wolf, Aug 26, 2004
    #6
    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. pemo

    Trap Representations - c99 [again]

    pemo, Mar 15, 2006, in forum: C Programming
    Replies:
    10
    Views:
    447
    Chris Torek
    Mar 19, 2006
  2. Army1987

    Trap representations for unsigned integers

    Army1987, Apr 21, 2007, in forum: C Programming
    Replies:
    17
    Views:
    582
    Peter Nilsson
    Apr 30, 2007
  3. Replies:
    7
    Views:
    300
    Branimir Maksimovic
    May 13, 2007
  4. Spiros Bousbouras

    Trap representations

    Spiros Bousbouras, Jan 22, 2009, in forum: C Programming
    Replies:
    23
    Views:
    827
    Richard Tobin
    Jan 29, 2009
  5. Ian Collins

    Re: C++ trap representations

    Ian Collins, Feb 2, 2009, in forum: C Programming
    Replies:
    3
    Views:
    388
Loading...

Share This Page