is this portable

0 views
Skip to first unread message

Martin Vorbrodt

unread,
Oct 9, 2004, 6:54:42 AM10/9/04
to
Hi there. Is this code portable between platforms? Is it also 100% standard
compliant?

#include <iostream>
using namespace std;

class Point {
public:
enum COORDS { X = 0, Y, Z };
Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}

const int& operator[](COORDS c) const
{ return (&_x)[c]; }

private:
int _x, _y, _z;
};

int main() {
Point p(1, 2, 3);

cout << p[Point::X] << endl;
cout << p[Point::Y] << endl;
cout << p[Point::Z] << endl;
}

Is it save to get the address of the first member, and do pointer arithmetic
on it to get to all 3 elements?

Thanx
Martin

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Alf P. Steinbach

unread,
Oct 10, 2004, 5:30:01 AM10/10/04
to
* Martin Vorbrodt:

> Hi there. Is this code portable between platforms? Is it also 100% standard
> compliant?
>
> #include <iostream>
> using namespace std;
>
> class Point {
> public:
> enum COORDS { X = 0, Y, Z };
> Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}
>
> const int& operator[](COORDS c) const
> { return (&_x)[c]; }

No (because of padding & alignment issues), and no (it's Undefined Behavior).


> private:
> int _x, _y, _z;
> };

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Chris Uzdavinis

unread,
Oct 10, 2004, 5:38:06 AM10/10/04
to
"Martin Vorbrodt" <mvor...@poczta.onet.pl> wrote in message news:<ck6adr$her$1...@news.onet.pl>...

> Hi there. Is this code portable between platforms? Is it also 100% standard
> compliant?
...

> class Point {
> public:
> enum COORDS { X = 0, Y, Z };
> Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}
>
> const int& operator[](COORDS c) const
> { return (&_x)[c]; }
>
> private:
> int _x, _y, _z;
> };

No. Absolutely not. The address of _x tells you where _x is located.
It does not imply anything about the other members, and it's
undefined to use &x as the base pointer of an array, since it doesn't
point into one. You really don't want to use it as if it were one,
because that takes you into undefined territory.

If you do this, you're depending on compiler implementation detail
such as the order that the members are layed out in memory, and that
there's no padding bytes between the members. It goes without saying
that depending on compiler implementation details is not portable.

> Is it save to get the address of the first member, and do pointer arithmetic
> on it to get to all 3 elements?

Tricks like this may sometimes (unfortunately) work as expected, and
sometimes even so-called experts do them. But undefined behavior is
undefined behavior, and the appearance of "working" is one way
undefined behvaior manifests itself. This kind of coding is
needlessly dangerous and certain to eventually cause you harm. (That
is, it WILL break someday, on some platform, and you'll spend hours
debugging a problem that should not exist.) I suggest you write code
that is so obviously correct that you don't have to ask. For example:

class Point {
public:
enum COORDS { X = 0, Y, Z };
Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}

const int& operator[](COORDS c) const

{ switch(c) {
case X: return _x;
case Y: return _y;
case Z: return _z;
default: throw std::runtime_error("Invalid argument");
}

private:
int _x, _y, _z;
};

Or alternately,

class Point {
public:
enum COORDS { X = 0, Y, Z };

Point(int x, int y, int z) { xyz[X] = x; xyz[Y] = y; xyz[Z]
= z;}

const int& operator[](COORDS c) const

{ return xyz[c]; }

private:
int xyz[3];
};

In this second case, member xyz is an array and it's thereforce safe
to index into. (Assuming the argument to operator[] is in range, of
course.)

--
Chris

Thomas Maeder

unread,
Oct 10, 2004, 5:39:51 AM10/10/04
to
"Martin Vorbrodt" <mvor...@poczta.onet.pl> writes:

> Hi there. Is this code portable between platforms? Is it also 100% standard
> compliant?

No on both accounts.


> #include <iostream>
> using namespace std;
>
> class Point {
> public:
> enum COORDS { X = 0, Y, Z };
> Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}
>
> const int& operator[](COORDS c) const
> { return (&_x)[c]; }
>
> private:
> int _x, _y, _z;
> };

&_x points to a single object; if you use it like a pointer to an array, the
only valid index is (something convertible to) 0.

Your program has undefined behavior.

Maciej Sobczak

unread,
Oct 10, 2004, 5:40:40 AM10/10/04
to
Hi,

Martin Vorbrodt wrote:
> Hi there. Is this code portable between platforms? Is it also 100% standard
> compliant?
>
> #include <iostream>
> using namespace std;
>
> class Point {
> public:
> enum COORDS { X = 0, Y, Z };
> Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}
>
> const int& operator[](COORDS c) const
> { return (&_x)[c]; }
>
> private:
> int _x, _y, _z;
> };
>
> int main() {
> Point p(1, 2, 3);
>
> cout << p[Point::X] << endl;
> cout << p[Point::Y] << endl;
> cout << p[Point::Z] << endl;
> }
>
> Is it save to get the address of the first member, and do pointer arithmetic
> on it to get to all 3 elements?

No, it is no safe. There may be padding between the members and your
indexing operator assumes that the members are placed one after another
in memory. This is not guaranteed (but may happen to work on many
platforms).

What about this:

class Point
{
public:
enum COORDS { X = 0, Y, Z };
Point(int x, int y, int z)

{ coords_[0] = x; coords_[1] = y; coords_[2] = z;}

const int & operator[](COORDS c) const { return coords_[c]; }

private:
int coords_[3];
};

--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/

Tokyo Tomy

unread,
Oct 10, 2004, 6:05:12 PM10/10/04
to
"Martin Vorbrodt" <mvor...@poczta.onet.pl> wrote in message news:<ck6adr$her$1...@news.onet.pl>...
> Hi there. Is this code portable between platforms? Is it also 100% standard
> compliant?
>
> ...

> Is it save to get the address of the first member, and do pointer arithmetic
> on it to get to all 3 elements?
>

Pointer arithmetic is effective for continuous memory arrangement. I
don't think that the standard say something how to arrange class
members, _x, _y, and _z, on memory. Therefore your code is not
guaranteed to work, although I think it works in many cases.

Carl Barron

unread,
Oct 10, 2004, 6:25:22 PM10/10/04
to
In article <ck6adr$her$1...@news.onet.pl>, Martin Vorbrodt
<mvor...@poczta.onet.pl> wrote:

> Hi there. Is this code portable between platforms? Is it also 100% standard
> compliant?
>
> #include <iostream>
> using namespace std;
>
> class Point {
> public:
> enum COORDS { X = 0, Y, Z };
> Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}
>
> const int& operator[](COORDS c) const
> { return (&_x)[c]; }
>
> private:
> int _x, _y, _z;
> };
>
> int main() {
> Point p(1, 2, 3);
>
> cout << p[Point::X] << endl;
> cout << p[Point::Y] << endl;
> cout << p[Point::Z] << endl;
> }
>
> Is it save to get the address of the first member, and do pointer arithmetic
> on it to get to all 3 elements?
>

I can't name a compiler on which it will fail, but the compiler is
permitted to insert padding of its choice between _x,_y,_z and is
not permitted to do so with an int array. That said why not just provide
three access inlined access function members??

class Point
{
// ...
public
int X() const {return _x;}
int Y() const {return _y;}
int Z() const {return _z;}
};

This is portable and unless you prohibit inliniing it willl be at least
as efficient.
Point p(1,2,3);
std::cout << p.X() << ',' << p.Y() << ',' << p.Z() << '\n';

Ganesh

unread,
Oct 10, 2004, 6:34:40 PM10/10/04
to
> Hi there. Is this code portable between platforms? Is it also 100% standard
> compliant?

> Is it save to get the address of the first member, and do pointer arithmetic


> on it to get to all 3 elements?

You are assuming that the three members will be allocated contiguously
and hence access it using array indexing from the address of first
member. Though standard (9.2 para 12) ensures that "a (non-union)
class declared without an intervening access-specifier are allocated
so that later members have higher addresses within a class object",
the problem is "Implementation alignment requirements might cause two
adjacent members not to be allocated immediately after each other".
And treating class members as if it were an array doing pointer
arithmetic is undefined behaviour, and hence is unsafe, and
non-portable.

-Ganesh

jakacki

unread,
Oct 10, 2004, 6:35:01 PM10/10/04
to
> class Point {
> public:
> enum COORDS { X = 0, Y, Z };
> Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}
>
> const int& operator[](COORDS c) const
> { return (&_x)[c]; }
>
> private:
> int _x, _y, _z;
> };
>
> int main() {
> Point p(1, 2, 3);
>
> cout << p[Point::X] << endl;
> cout << p[Point::Y] << endl;
> cout << p[Point::Z] << endl;
> }
>
> Is it save to get the address of the first member, and do
> pointer arithmetic on it to get to all 3 elements?

No. q[n] is undefined for n>0 and q not being a pointer
into an array. Cf. Standard 5.2.1 and 5.7p5.

Why not:

class Point {
....


Point(int x, int y, int z)

{
p_[0] = x; p_[1] = y; p_[2] = z;
}
....
private:
int p_[3];
};

BR
Grzegorz

--
Free C++ frontend library: http://opencxx.sourceforge.net
China from the inside: http://www.staryhutong.com
Myself: http://www.dziupla.net/gj/cv

James Dennett

unread,
Oct 10, 2004, 6:51:19 PM10/10/04
to
Martin Vorbrodt wrote:

> Hi there. Is this code portable between platforms? Is it also 100% standard
> compliant?
>
> #include <iostream>
> using namespace std;
>
> class Point {
> public:
> enum COORDS { X = 0, Y, Z };
> Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}
>
> const int& operator[](COORDS c) const
> { return (&_x)[c]; }
>
> private:
> int _x, _y, _z;
> };
>
> int main() {
> Point p(1, 2, 3);
>
> cout << p[Point::X] << endl;
> cout << p[Point::Y] << endl;
> cout << p[Point::Z] << endl;
> }
>
> Is it save to get the address of the first member, and do pointer arithmetic
> on it to get to all 3 elements?

No, there may be padding between _x, _y and _z.

-- James

Francis Glassborow

unread,
Oct 10, 2004, 7:09:46 PM10/10/04
to
In article <6081b902.04100...@posting.google.com>, Chris
Uzdavinis <cuz...@gmail.com> writes

>"Martin Vorbrodt" <mvor...@poczta.onet.pl> wrote in message news:<ck6adr$her$1...@news.onet.pl>...
> > Hi there. Is this code portable between platforms? Is it also 100% standard
> > compliant?
>...
>
> > class Point {
> > public:
> > enum COORDS { X = 0, Y, Z };
> > Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}
> >
> > const int& operator[](COORDS c) const
> > { return (&_x)[c]; }
> >
> > private:
> > int _x, _y, _z;
> > };
>
>No. Absolutely not. The address of _x tells you where _x is located.

And though it might work in the context of a class, it is a dangerous
way to think. I once had to do major maintenance on some C code because
the original author assumed that:

int a, array[10];

And used that assumption for 11 global combinations of simple and array
variable.


allowed him to access array[0] as a[1], which was untrue on the compiler
I was using (I think it put arrays somewhere else entirely to minimise
stack use) In C++ you can do something like this:

int array[11];
int & a = array[0];

Or in your case, if you really want to go down this path:

int data[3]
int & _x;
int & _y;
int & _z;

and define the relationship in the ctor init list. But I cannot imagine
what it would gain you.

--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects

Duane Hebert

unread,
Oct 10, 2004, 7:14:10 PM10/10/04
to

"Chris Uzdavinis" <cuz...@gmail.com> wrote in message news:6081b902.04100...@posting.google.com...

> Or alternately,
>
> class Point {
> public:
> enum COORDS { X = 0, Y, Z };
> Point(int x, int y, int z) { xyz[X] = x; xyz[Y] = y; xyz[Z]
> = z;}
>
> const int& operator[](COORDS c) const
> { return xyz[c]; }
>
> private:
> int xyz[3];
> };
>
> In this second case, member xyz is an array and it's thereforce safe
> to index into. (Assuming the argument to operator[] is in range, of
> course.)
>

And it has the (IMO) additional benefit of not implicitly casting
the enum to an int.

Phlip

unread,
Oct 11, 2004, 7:46:32 AM10/11/04
to
Alf P. Steinbach wrote:

> * Martin Vorbrodt:
> > Hi there. Is this code portable between platforms? Is it also 100%
standard
> > compliant?
> >
> > #include <iostream>
> > using namespace std;
> >
> > class Point {
> > public:
> > enum COORDS { X = 0, Y, Z };
> > Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}
> >
> > const int& operator[](COORDS c) const
> > { return (&_x)[c]; }
>
> No (because of padding & alignment issues), and no (it's Undefined
Behavior).

I think this is a "yes". Point is a PODs, and padding is
implementation-defined. Hence, on platforms where it's not undefined
behavior, it's well-defined.

> > private:
> > int _x, _y, _z;
> > };

--
Phlip
http://industrialxp.org/community/bin/view/Main/TestFirstUserInterfaces

Alf P. Steinbach

unread,
Oct 11, 2004, 7:11:39 PM10/11/04
to
* Phlip:

> Alf P. Steinbach wrote:
>
> > * Martin Vorbrodt:
> > > Hi there. Is this code portable between platforms? Is it also 100%
> > > standard compliant?
> > >
> > > #include <iostream>
> > > using namespace std;
> > >
> > > class Point {
> > > public:
> > > enum COORDS { X = 0, Y, Z };
> > > Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}
> > >
> > > const int& operator[](COORDS c) const
> > > { return (&_x)[c]; }
> > > private:
> > > int _x, _y, _z;
> > > };
> >
> > No (because of padding & alignment issues), and no (it's Undefined
> > Behavior).
>
> I think this is a "yes". Point is a PODs, and padding is
> implementation-defined. Hence, on platforms where it's not undefined
> behavior, it's well-defined.

The term "Undefined Behavior" usually refers to the Holy Standard, and that's
the way I used it -- UB in that sense is absolute, not platform-dependent.

As I understand it what you mean by "Undefined Behavior", in this case, is
whether a given C++ implementation defines the behavior.

That does not affect portability between platforms (the first "no"), nor does
it affect standard compliance (the second "no").

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Walt Karas

unread,
Oct 11, 2004, 7:42:13 PM10/11/04
to
"Martin Vorbrodt" <mvor...@poczta.onet.pl> wrote in message news:<ck6adr$her$1...@news.onet.pl>...
> Hi there. Is this code portable between platforms? Is it also 100% standard
> compliant?
>
> #include <iostream>
> using namespace std;
>
> class Point {
> public:
> enum COORDS { X = 0, Y, Z };
> Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}
>
> const int& operator[](COORDS c) const
> { return (&_x)[c]; }
>
> private:
> int _x, _y, _z;
> };
>
> int main() {
> Point p(1, 2, 3);
>
> cout << p[Point::X] << endl;
> cout << p[Point::Y] << endl;
> cout << p[Point::Z] << endl;
> }
>
> Is it save to get the address of the first member, and do pointer arithmetic
> on it to get to all 3 elements?
>
> Thanx
> Martin
...

Whether it's standard or not, it's very desirable that this sort
of code should work with a compiler that's going to be used in
an embedded environment. It's a fairly common technique to use
a struct to represent the register map of a memory-mapped IC, then
cast the IC's base address to be a pointer to the struct type.

More generally, there is alot of legacy C code that assumes that
if two structs have a sequence of data members as a "common prefix",
then these data members will be at the same offsets in the two
structs. It's kind of a neaderthal version of a base class.

Seungbeom Kim

unread,
Oct 12, 2004, 5:29:26 PM10/12/04
to
Walt Karas wrote:

> Whether it's standard or not, it's very desirable that this sort
> of code should work with a compiler that's going to be used in
> an embedded environment. It's a fairly common technique to use
> a struct to represent the register map of a memory-mapped IC, then
> cast the IC's base address to be a pointer to the struct type.

The technique must have been provided by the specific implementation,
which defines the (originally undefined) behaviour and knows what it's
doing. Therefore, it neither is portable nor should be portable. The
desirability does not lead to the necessity of the standard's support.

> More generally, there is alot of legacy C code that assumes that
> if two structs have a sequence of data members as a "common prefix",
> then these data members will be at the same offsets in the two
> structs. It's kind of a neaderthal version of a base class.

I would say that, if they wanted to emulate inheritance in C, they
should have made the common (base) part into a struct and let it
included as the first member of each derived ones.

struct base { /* ... */ };
struct derived_A { struct base base_part; /* A part ... */ };
struct derived_B { struct base base_part; /* B part ... */ };

struct derived_A* pD = /* get some derived object */;
struct base* pB = &pD->base_part;

Isn't this much better than relying on undefined behaviour?

I'm not sure whether "(struct base*) pD" is guaranteed to work or not,
though.

--
Seungbeom Kim

Walt Karas

unread,
Oct 13, 2004, 6:16:34 PM10/13/04
to
Seungbeom Kim <musi...@bawi.org> wrote in message news:<ckgarr$pd0$1...@news.Stanford.EDU>...

Having the common prefix be a struct is more readable, but it
isn't really relevant to the issue of whether the compiler is
permitted to reorder data members of a class in the memory
layout of class instances.

Obviously there are cases where reordering data members would
save wasted "pad" bytes, for example:

class X
{
char a;
int b;
char c;
};

To avoid breaking old C code, maybe the rule should be
that reordering is permitted except in structs with no base
classes and no virtual functions.

ka...@gabi-soft.fr

unread,
Oct 13, 2004, 5:18:32 PM10/13/04
to
Seungbeom Kim <musi...@bawi.org> wrote in message
news:<ckgarr$pd0$1...@news.Stanford.EDU>...
> Walt Karas wrote:

> > Whether it's standard or not, it's very desirable that this sort of
> > code should work with a compiler that's going to be used in an
> > embedded environment. It's a fairly common technique to use a
> > struct to represent the register map of a memory-mapped IC, then
> > cast the IC's base address to be a pointer to the struct type.

> The technique must have been provided by the specific implementation,
> which defines the (originally undefined) behaviour and knows what it's
> doing. Therefore, it neither is portable nor should be portable. The
> desirability does not lead to the necessity of the standard's support.

> > More generally, there is alot of legacy C code that assumes that if
> > two structs have a sequence of data members as a "common prefix",
> > then these data members will be at the same offsets in the two
> > structs. It's kind of a neaderthal version of a base class.

> I would say that, if they wanted to emulate inheritance in C, they
> should have made the common (base) part into a struct and let it
> included as the first member of each derived ones.

The question isn't so much what you should do, today. Today, if you
want base classes, it shouldn't be to hard to find a C++ compiler, and
do them right.

The question is what people actually did, many years ago, before there
was even a C standard. The C++ standard explicitly contains support for
common initial sequences, at least in certain conditions, because C
gives that support. And C gives it because it was a common technique in
C programs when C was being standardized.

> struct base { /* ... */ };
> struct derived_A { struct base base_part; /* A part ... */ };
> struct derived_B { struct base base_part; /* B part ... */ };

> struct derived_A* pD = /* get some derived object */;
> struct base* pB = &pD->base_part;

> Isn't this much better than relying on undefined behaviour?

In one frequent case, "struct base" was actually just an enum, e.g.:

enum NodeType
{
nt_constant,
nt_variable,
nt_operator,
/* ... */
} ;

struct AbstractNode { NodeType type ; } ;
struct ConstantNode { NodeType type ; /* ... */ } ;
struct VariableNode { NodeType type ; /* ... */ } ;

The code would be something like:

void
processNode( AbstractNode* node )
{
switch ( node->type )
{
case nt_constant :
processConstantNode( (ConstantNode*)node ) ;
break ;
/* ... */
}
}

> I'm not sure whether "(struct base*) pD" is guaranteed to work or not,
> though.

I'm not sure in this particular case, but there are cases where it is
guaranteed. In practice, it will work, and things like the above were
not all that rare before we got C++.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Reply all
Reply to author
Forward
0 new messages