Alignment, placement new, trap representations

P

Peter Ammon

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
 
D

David Hilsee

Peter Ammon said:
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.
 
R

Rolf Magnus

Peter said:
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.
 
D

Denis Remezov

Peter said:
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
 
D

Denis Remezov

Rolf said:
Peter said:
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
 
O

Old Wolf

Peter Ammon said:
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 :)
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top