Functions that don't use this, called with uninitialized pointers

43 views
Skip to first unread message

Andrei Alexandrescu

unread,
Jan 12, 1999, 3:00:00 AM1/12/99
to
Hello, the problem is simple:

class A {
int f(int i) { return i + 5; }
};

int main()
{
A * pA;
pA->f(1);
}

The question is, is the code guaranteed to work? Please refrain yourselves
from making comments on bad style etc. I've seen this in a test.

Thanks,

Andrei

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

Francis Glassborow

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
In article <369b7...@10.1.1.65>, Andrei Alexandrescu <alexandrescua@mi
cromodeling.com> writes

>Hello, the problem is simple:
>
>class A {
> int f(int i) { return i + 5; }
>};
>
>int main()
>{
> A * pA;
> pA->f(1);
>}
>
>The question is, is the code guaranteed to work? Please refrain yourselves
>from making comments on bad style etc. I've seen this in a test.

It is guaranteed not to work. f() is a private member of A.

Now make it a public member and it still cannot work as f() is a member
function and requires a this value. You are now deep in undefined
territory.

Francis Glassborow Chair of Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

Biju Thomas

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
Andrei Alexandrescu wrote:
>
> class A {
> int f(int i) { return i + 5; }
> };
> int main()
> {
> A * pA;
> pA->f(1);
> }
> The question is, is the code guaranteed to work?

No. It is undefined behaviour since a member function on an invalid
(uninitialized) object is invoked. It is equivalent to (*pA).f(1), and,
there is no object at (*pA).

But, it may work on many compilers as expected.

--
Regards,
Biju Thomas

Thiemo Seufer

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to

Andrei Alexandrescu wrote in message <369b7...@10.1.1.65>...

>Hello, the problem is simple:
>
>class A {
> int f(int i) { return i + 5; }
>};
>
>int main()
>{
> A * pA;
> pA->f(1);
>}
>
>The question is, is the code guaranteed to work? Please refrain yourselves
>from making comments on bad style etc. I've seen this in a test.


It's guaranteed NOT to work ( f() is not public :-)

Assuming public, I think the code will work (at least, I don't know why
it shouldn't) if it does not dereference 'this'.

No guarantees...

Thiemo Seufer

John Goodwin

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
On 13 Jan 1999 08:41:54 -0500, Biju Thomas <biju...@ibm.net> wrote:

>Andrei Alexandrescu wrote:
>>
>> class A {
>> int f(int i) { return i + 5; }
>> };
>> int main()
>> {
>> A * pA;
>> pA->f(1);
>> }
>> The question is, is the code guaranteed to work?
>

>No. It is undefined behaviour since a member function on an invalid
>(uninitialized) object is invoked. It is equivalent to (*pA).f(1), and,
>there is no object at (*pA).

It may be useful to consider it that way at a higher level, but most
ordercode processors would not in fact operate in that fashion.

The *call* to the member function above does not require a pointer.
True the uninitialisd pA will be passed (without dereferencing), but
since this is not used it's hard to see how it would cause an error.

I'd be interested to see if anyone can suggest how it could be
compiled to produce an error (assuming no debug code is introduced).

[This is not a defence of the technique of course]

JG

Antoun Kanawati

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
Andrei Alexandrescu wrote:
>
> Hello, the problem is simple:
>
> class A {
public:

> int f(int i) { return i + 5; }
> };
>
> int main()
> {
> A * pA;
> pA->f(1);
> }
>
> The question is, is the code guaranteed to work? Please refrain yourselves
> from making comments on bad style etc. I've seen this in a test.

Theoretically, you are invoking a member function on an undefined
entity,
therefore, the result of your action is undefined.

In practice, if your compiler is conventional (that is: no runtime
checks of any
sort :-), this will almost always work: the call is non-virtual, and
will be bound
at compile time; only the static type of (*pA) is involved, not the
contents.

Back in the "old days" (before static member functions), people used
NULL pointers
to invoke such member functions; e.g.: ((A *)0)->f().
--
Antoun Kanawati, akan...@bmc.com, to...@bgs.com
BMC Software, Inc., Waltham, MA, USA
DISCLAIMER: my opinions are mine, not the company's, etc...

Mungo Henning

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
Francis Glassborow wrote:
>
> In article <369b7...@10.1.1.65>, Andrei Alexandrescu
<alexandrescua@mi

I changed 'class' to 'struct' to circumvent the access problem:

> >struct A {


> > int f(int i) { return i + 5; }
> >};
> >
> >int main()
> >{
> > A * pA;
> > pA->f(1);
> >}
>

> Now make it a public member and it still cannot work as f() is a
member
> function and requires a this value. You are now deep in undefined
> territory.

Just how deep into undefined territory is the above code? Since the
(now)
struct is well defined, doesn't contain the keyword 'virtual', there's
no
derivation involved then when dealing with any pointer-to-A, if you
dereference
said pointer to call the function f() then there's only one possible
function
to call.

I'm sure that if you then added caveats such as derivation etc things
would become more difficult for the code, but as it stands what's the
problem?

Passing around "bad" pointers is acceptable, just as long as you don't
follow the pointer.
So is there any harm (apart from old-style cast) in:

((A*)0)->f(42);

Thanks in advance,

Mungo Henning

John Goodwin

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
On 13 Jan 1999 08:11:32 -0500, Francis Glassborow
<fra...@robinton.demon.co.uk> wrote:

>In article <369b7...@10.1.1.65>, Andrei Alexandrescu
<alexandrescua@mi

>cromodeling.com> writes


>>Hello, the problem is simple:
>>

>>class A {


>> int f(int i) { return i + 5; }
>>};
>>
>>int main()
>>{
>> A * pA;
>> pA->f(1);
>>}
>>

>>The question is, is the code guaranteed to work? Please refrain
yourselves
>>from making comments on bad style etc. I've seen this in a test.
>

>It is guaranteed not to work. f() is a private member of A.
>

>Now make it a public member and it still cannot work as f() is a member
>function and requires a this value. You are now deep in undefined
>territory.

Can you elaborate why you beleive it *cannot* work.

[It works perfectly on VC4.2 (after making f public)]

pA could contain *anything*, but since the "this" pointer is never
used, implicitly or explicitly, why should it fail?

JG

Larry Brasfield

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
Andrei Alexandrescu wrote in message <369b7...@10.1.1.65>...
>Hello, the problem is simple:
>
>class A {
> int f(int i) { return i + 5; }
>};
>
>int main()
>{
> A * pA;
> pA->f(1);
>}
>
>The question is, is the code guaranteed to work?

I don't think so. There is nothing to keep an implementation
from having some strange convention, say for handling virtual
functions, that requires all 'this' pointers to be found in a lookup
table prior to every member function call, whether the class
has virtual functions or not.

If you had asked "Will it usually work?", I would say "Sure."
But C++ does not constrain implementations to the common
memory/object model that was established so long ago.

>Please refrain yourselves
>from making comments on bad style etc. I've seen this in a test.

No, I cannot refrain myself. Nor is restraint called for.

It's not a matter of style. A member function that does
not do anything with the class instance upon which it
must be called is plain stupid. It is likely to be inefficient
and perplexing at the call site(s). Failing to declare it
static is a way of confounding the purpose of the function
that goes beyond mere style.

--Larry Brasfield
Above opinions may be mine alone.
(Humans may reply at unundered larry_br@sea_net.com )


.

Ziv Caspi

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
On 12 Jan 1999 14:42:05 -0500, "Andrei Alexandrescu"
<alexan...@micromodeling.com> wrote:

>Hello, the problem is simple:
>
>class A {

public:

> int f(int i) { return i + 5; }
>};
>
>int main()
>{
> A * pA;
> pA->f(1);
>}
>
>The question is, is the code guaranteed to work?

No. pA is not initialized. Using it is undefined behavior.

---------------------------------------------
Ziv Caspi
zi...@netvision.net.il

John Duncan

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to

The behavior is implementation dependant, assuming that
it is made public as everyone else has mentioned. I don't
believe that there is any requirement that the proper
method binding be created for a never-constructed object.
One can statically determine the existence of a ctor call
by looking for

(1) an automatic allocation of the object,
(2) a use of the class name as an argument to operator new.

Chances are good that it will work. Here's my modified
version:

// test.cpp--
#include <iostream.h>

struct A {


int f(int i) { return i + 5; }
};

int main()
{
A * pA;

cout << (pA->f(1));
return 0;
}
// end test.cpp

and my shell session with Visual C++ 6.

C:\Projects\MSVC\test>cl test.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for
80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.

test.cpp
test.cpp(10) : warning C4700: local variable 'pA' used without having
been
initi
alized
Microsoft (R) Incremental Linker Version 6.00.8168
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

/out:test.exe
test.obj

C:\Projects\MSVC\test>test
6

C:\Projects\MSVC\test>

BUT, if you paid me for the work, I could probably write a standard-
compliant modification of GCC whose output barfed on this.

-John

Siemel Naran

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
On 12 Jan 1999 14:42:05 -0500, Andrei Alexandrescu

>class A {


> int f(int i) { return i + 5; }
>};
>
>int main()
>{
> A * pA;

> pA->f(1);
>}
>
>The question is, is the code guaranteed to work? Please refrain yourselves


>from making comments on bad style etc. I've seen this in a test.

No, it's not guaranteed. On most implementations, A::f() becomes
int f(A *const this, int i) { return i+5; }
But an implementation is free to add "assert(this)" at the head of
each function.

--
----------------------------------
Siemel B. Naran (sbn...@uiuc.edu)
----------------------------------

David Kastrup

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
"Andrei Alexandrescu" <alexan...@micromodeling.com> writes:

> Hello, the problem is simple:
>

> class A {
> int f(int i) { return i + 5; }
> };
>
> int main()
> {
> A * pA;
> pA->f(1);
> }
>
> The question is, is the code guaranteed to work?

No. It would be legal if f were a static member function.

--
David Kastrup Phone: +49-234-700-5570
Email: d...@neuroinformatik.ruhr-uni-bochum.de Fax: +49-234-709-4209
Institut für Neuroinformatik, Universitätsstr. 150, 44780 Bochum, Germany

Steve Clamage

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
"Andrei Alexandrescu" <alexan...@micromodeling.com> writes:

>Hello, the problem is simple:

>class A {
> int f(int i) { return i + 5; }
>};

>int main()
>{
> A * pA;
> pA->f(1);
>}

>The question is, is the code guaranteed to work? Please refrain yourselves


>from making comments on bad style etc. I've seen this in a test.

Auto variale pA is not initialized, but must be dereferenced
to call the non-static member function f. The results of the program
are undefined by the standard, meaning anything at all might happen.

A helpful compiler will complain about using an uninitialized
variable, and is within its rights to refuse to compile the
program at all.

Some runtime systems cause uninitialized variables to have values
that result in a run-time trap if accessed before being set. On
such a system the program would abort at run time, if the compiler
accepted the code in the first place.

--
Steve Clamage, stephen...@sun.com

Rolf F. Katzenberger

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
On 12 Jan 1999 14:42:05 -0500, in article <369b7...@10.1.1.65> "Andrei
Alexandrescu" <alexan...@micromodeling.com> wrote:

> Hello, the problem is simple:
>
> class A {
> int f(int i) { return i + 5; }

f isn't public.

> };

A has no public constructor.

> int main()
> {
> A * pA;

pA isn't pointing to an A.

> pA->f(1);
> }
>
> The question is, is the code guaranteed to work?

It doesn't even compile before you make f() public.


Regards,
Rolf


--
______________________________________________________________________
Rolf F. Katzenberger | Software Developer | Trainer 1998-11-18
Home: http://www.geocities.com/SiliconValley/Park/9557
PGP : http://wwwkeys.pgp.net:11371/pks/lookup?op=get&search=0x3B39491F

Francis Glassborow

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
In article <369CA688...@itacs.strath.ac.uk>, Mungo Henning
<mun...@itacs.strath.ac.uk> writes
>> >struct A {

>> > int f(int i) { return i + 5; }
>> >};

>> >
>> >int main()
>> >{
>> > A * pA;
>> > pA->f(1);

>> >}
>>
>> Now make it a public member and it still cannot work as f() is a
>member
>> function and requires a this value. You are now deep in undefined
>> territory.
>
>Just how deep into undefined territory is the above code? Since the
>(now)
>struct is well defined, doesn't contain the keyword 'virtual', there's
>no
>derivation involved then when dealing with any pointer-to-A, if you
>dereference
>said pointer to call the function f() then there's only one possible
>function
>to call.

When the function call is made, two arguments are forwarded to f(). One
of those is a pointer (the value in pA). If that pointer is invalid (is
outside the range of pointers -addresses- available to your program at
the time of execution of that statement) you have undefined behaviour.
Quite a few platforms may abort your process with a segment violation
fault.

To argue that the compiler will optimise away all the problems is just
asking for trouble. One day that f() will be changed with disastrous
consequences. I believe that writing unnecessarily flaky code is
unprofessional.

Guideline:

Do not write member functions that do not use any characteristic of the
class (one reason namespaces were invented was to make this completely
unnecessary)

Francis Glassborow Chair of Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Jean-Francois Brouillet

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
{ Please quote moderately! -vdv }
----------
>From: J...@opticon.demon.co.uk (John Goodwin)
>Newsgroups: comp.lang.c++.moderated
>Subject: Re: Functions that don't use this, called with uninitialized pointers
>Date: Wed, Jan 13, 1999, 18:02
>

> On 13 Jan 1999 08:11:32 -0500, Francis Glassborow
> <fra...@robinton.demon.co.uk> wrote:
>
>>In article <369b7...@10.1.1.65>, Andrei Alexandrescu
> <alexandrescua@mi
>>cromodeling.com> writes

>>>Hello, the problem is simple:
>>>

>>>class A {


>>> int f(int i) { return i + 5; }
>>>};
>>>
>>>int main()
>>>{
>>> A * pA;
>>> pA->f(1);
>>>}
>>>

>>>The question is, is the code guaranteed to work? Please refrain
> yourselves
>>>from making comments on bad style etc. I've seen this in a test.
>>

>>It is guaranteed not to work. f() is a private member of A.
>>

>>Now make it a public member and it still cannot work as f() is a member
>>function and requires a this value. You are now deep in undefined
>>territory.
>

> Can you elaborate why you beleive it *cannot* work.
>
> [It works perfectly on VC4.2 (after making f public)]
>
> pA could contain *anything*, but since the "this" pointer is never
> used, implicitly or explicitly, why should it fail?

Consider first a slight modification...

class aa {
// empty
} ;

class A : public aa {
// rest same as above
} ;

Does this change the nature of the question ?

Now consider a slight variation:

class aa {
public:
virtual int f(int i) ;

} ;

class A : public aa {
// rest same as above
} ;


Now, is the usual "but why should it fail" so obvious ?

--
jean-franco...@virgin.net

mar...@my-dejanews.com

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
In article <369c9f6c....@news.demon.co.uk>,

J...@opticon.demon.co.uk (John Goodwin) wrote:
> On 13 Jan 1999 08:41:54 -0500, Biju Thomas <biju...@ibm.net> wrote:
>
> >Andrei Alexandrescu wrote:
> >>
> >> class A {
> >> int f(int i) { return i + 5; }
> >> };
> >> int main()
> >> {
> >> A * pA;
> >> pA->f(1);
> >> }
> >> The question is, is the code guaranteed to work?
> >
> >No. It is undefined behaviour since a member function on an invalid
> >(uninitialized) object is invoked. It is equivalent to (*pA).f(1), and,
> >there is no object at (*pA).
>
> It may be useful to consider it that way at a higher level, but most
> ordercode processors would not in fact operate in that fashion.
>
> The *call* to the member function above does not require a pointer.
> True the uninitialisd pA will be passed (without dereferencing), but
> since this is not used it's hard to see how it would cause an error.
>
> I'd be interested to see if anyone can suggest how it could be
> compiled to produce an error (assuming no debug code is introduced).
>
I know of at least one compiler that will generate code that will fail.

The old microsoft 16 bit compiler had an option to pass the 'this' pointer in
registers. If you compiled in "far" mode, that meant loading the high bits of
the address into ES. On the x86, just *loading* an invalid value into a
segment register causes a runtime exception - it doesnt wait until you *use*
it. So the above code would (most likely) fail.

Mark Williams

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own

Francis Glassborow

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
In article <369c9f6c....@news.demon.co.uk>, John Goodwin
<J...@opticon.demon.co.uk> writes

>>Andrei Alexandrescu wrote:
>>>
>>> class A {
>>> int f(int i) { return i + 5; }
>>> };
>>> int main()
>>> {
>>> A * pA;
>>> pA->f(1);
>>> }
>>> The question is, is the code guaranteed to work?
>>
>>No. It is undefined behaviour since a member function on an invalid
>>(uninitialized) object is invoked. It is equivalent to (*pA).f(1), and,
>>there is no object at (*pA).
>
>It may be useful to consider it that way at a higher level, but most
>ordercode processors would not in fact operate in that fashion.
>The *call* to the member function above does not require a pointer.
>True the uninitialisd pA will be passed (without dereferencing), but
>since this is not used it's hard to see how it would cause an error.
>
>I'd be interested to see if anyone can suggest how it could be
>compiled to produce an error (assuming no debug code is introduced).

The type of f is pointer to member taking an int and returning an int.
Without optimisation:

the code must dereference f (that is the way functions work) to obtain
the location of f, the arguments for the call (this and a temp int with
value 1) placed for transfer. At this stage you have a wild pointer
floating around.

The very fact that even the simplest compiler circumvents this is what
makes writing code so very dangerous. Its working relies entirely on
several fortuitous circumstances (non-virtual function, makes no access
to member data etc.)

I do not believe that any programmer would actually write such code as a
permanent feature. It has a distinct feel of being a place holder.

Put it another way, the code may 'work' on most compilers but any team
leader that allowed code like that out of the door would have to work
very hard to remain in work:) in a respectable software shop.


Francis Glassborow Chair of Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Francis Glassborow

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
In article <369c9a19....@news.demon.co.uk>, John Goodwin
<J...@opticon.demon.co.uk> writes

>Can you elaborate why you beleive it *cannot* work.

Sorry, when I say cannot work, I mean reliably whatever is later added
to the implementation. In particular accessing any data member of A
will cause potentially catestrophic failure.

>
>[It works perfectly on VC4.2 (after making f public)]


>
>pA could contain *anything*, but since the "this" pointer is never
>used, implicitly or explicitly, why should it fail?

It is 'used' (technical term defined somewhere in the standard),
because it is passed as an argument to f().

Alex Martelli

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
Larry Brasfield wrote in message
[snip]

>It's not a matter of style. A member function that does
>not do anything with the class instance upon which it
>must be called is plain stupid. It is likely to be inefficient
>and perplexing at the call site(s). Failing to declare it
>static is a way of confounding the purpose of the function
>that goes beyond mere style.


Personally, I rate it just about on a par with any
other function that does not use all of the parameters
that are passed to it (and C++ even includes an
extension to C to facilitate other cases of such
functions, i.e. unnamed parameters): i.e., pretty
normal in evolving code. Perhaps the function does
not use the parameter TODAY, but it used to, and
maybe will again -- meanwhile, one keeps the
parameter there; why make all clients recompile?!
Not a very strong issue, but a frequent one.

Or perhaps, and this is a perfectly reasonable case
that also applies to completely stable code!, there
is a pointer-to-member somewhere, that must be
able to point to this function, or alternatively to
others; it is then quite reasonable to supply this
function with extra arguments it will not use (and
this includes the "this" pointer!), to "extend", so
to speak, its signature to that of the other functions
it must be interchangeable with. This may not be
a very frequent case, but it IS a very strong
argument for asserting that "plain stupid" is a
very unwise judgment to make for this idiom.

None of this has anything to do with calling a
function through an uninitialized pointer, which
is and remains undefined behaviour, whether the
function "uses" the this pointer or not.


Alex

-----------== Posted via Newsfeeds.Com, Uncensored Usenet News ==----------
http://www.newsfeeds.com/ The Largest Usenet Servers in the World!
-----------== Over 66,000 Groups, Plus a Dedicated Binaries Server
==----------

Steve Clamage

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
J...@opticon.demon.co.uk (John Goodwin) writes:

>On 13 Jan 1999 08:41:54 -0500, Biju Thomas <biju...@ibm.net> wrote:

>>Andrei Alexandrescu wrote:
>>>
>>> class A {
>>> int f(int i) { return i + 5; }
>>> };
>>> int main()
>>> {
>>> A * pA;
>>> pA->f(1);
>>> }
>>> The question is, is the code guaranteed to work?
>>
>>No. It is undefined behaviour since a member function on an invalid
>>(uninitialized) object is invoked. It is equivalent to (*pA).f(1), and,
>>there is no object at (*pA).

>It may be useful to consider it that way at a higher level, but most
>ordercode processors would not in fact operate in that fashion.

>The *call* to the member function above does not require a pointer.
>True the uninitialisd pA will be passed (without dereferencing), but
>since this is not used it's hard to see how it would cause an error.

>I'd be interested to see if anyone can suggest how it could be
>compiled to produce an error (assuming no debug code is introduced).

On a tagged architecture, attempting to read a pointer value that
is not valid results in a runtime trap that will by default abort
the program. It is not necessary to dereference the pointer to get
a trap; attempting to read the value is enough.

A pointer-checking C++ runtime system could have the same behavior.

The original question was whether the code was guaranteed to
work. Any expression of the forms
p->data_member
p->func_member()
whether static or nonstatic data or function members, has undefined
behavior unless p points to an object of the appropriate type.

Even when accessing a static member, the standard requires that
the object-expression to the left of the "->" be evaluated, and
"a->b" is defined to mean the same thing as "(*a).b".

Undefined behavior means that there are no guarantees.

--
Steve Clamage, stephen...@sun.com

Biju Thomas

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
Rolf F. Katzenberger wrote:
>
> On 12 Jan 1999 14:42:05 -0500, in article <369b7...@10.1.1.65> "Andrei
> Alexandrescu" <alexan...@micromodeling.com> wrote:
>
> >
> > class A {
> > int f(int i) { return i + 5; }
> > };
>
> A has no public constructor.
>

That is not true. The default constructor (provided by the compiler) is
public. Otherwise, what is the use of default constructor?

--
Regards,
Biju Thomas

Andrew Koenig

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
In article <369c9f6c....@news.demon.co.uk>,
John Goodwin <J...@opticon.demon.co.uk> wrote:

> I'd be interested to see if anyone can suggest how it could be
> compiled to produce an error (assuming no debug code is introduced).

Easy. Imagine a machine with separate address and data registers,
with the address registers having the property that putting an address
into an address register causes virtual-memory translation to occur
immediately. Imagine further a compiler whose calling sequence for
member functions involves putting the value of `this' into one of
these address registers

Then the caller might put the invalid address into the register intended
to contain `this,' on the assumption that the callee will use it,
and the virtual-memory translation might cause a trap at that point,
even though the callee never actually uses the address.

It's the same kind of logic that prohibits the following:

int* p1 = new int;
int* p2 = p1;
delete p1;
int* p3 = p2; // undefined behavior

Although "delete p1" could not have changed the value of p2, the fact
that p2 pointed to memory that has been deleted means that the value of
p2 cannot even be copied, whether or not any overt attempt is made to
dereference p2.
--
Andrew Koenig
a...@research.att.com
http://www.research.att.com/info/ark

John Goodwin

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
On 13 Jan 1999 13:01:49 -0500, Mungo Henning
<mun...@itacs.strath.ac.uk> wrote:

snip

>Passing around "bad" pointers is acceptable, just as long as you don't
>follow the pointer.

Passing "bad" pointers may work, but it's hardly "acceptable". That
bad pointer might get passed on, and eventualy, many months hence used
by another programmer.

Murphy's law states that it would then work until the shrink wraps had
been manufactured.

JG

Alex Martelli

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
John Goodwin wrote in message <369c9a19....@news.demon.co.uk>...
[snip]

>Can you elaborate why you beleive it *cannot* work.
>
>[It works perfectly on VC4.2 (after making f public)]
>
>pA could contain *anything*, but since the "this" pointer is never
>used, implicitly or explicitly, why should it fail?


"undefined behaviour" means that anything and everything can
happen, and calling pA->f() with pA uninitialized invokes
undefined behaviour. On some hardware, for example,
the calling convention could involve loading pA into a hardware
register, and such a load could implicitly and automatically
cause a check of pointer validity, with program termination
if the check fails; perfectly reasonable behaviour, on suitable
hardware, since the language _does_ allow the compiler to
assume pA is valid when pA->f() is called.

In general, it is rather pointless to speculate on exactly what
"undefined behaviour" could mean, and why -- it is by far
wiser to simply eschew causing it, even if a given release of
a given compiler should appear, on a small sample of tests,
not too react too badly.


Alex

-----------== Posted via Newsfeeds.Com, Uncensored Usenet News
==----------
http://www.newsfeeds.com/ The Largest Usenet Servers in the
World!
-----------== Over 66,000 Groups, Plus a Dedicated Binaries Server
==----------

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

John Goodwin

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
On 14 Jan 1999 00:19:32 -0500, Francis Glassborow
<fra...@robinton.demon.co.uk> wrote:

>In article <369c9a19....@news.demon.co.uk>, John Goodwin
><J...@opticon.demon.co.uk> writes

snip

>>pA could contain *anything*, but since the "this" pointer is never
>>used, implicitly or explicitly, why should it fail?
>

>It is 'used' (technical term defined somewhere in the standard),
>because it is passed as an argument to f().
>

I was using "used" in the sense of "used by the ordercode processor",
and none of those yet understand the standard :)

JG

John Goodwin

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
On 14 Jan 1999 00:21:32 -0500, mar...@my-dejanews.com wrote:

snip

>> I'd be interested to see if anyone can suggest how it could be
>> compiled to produce an error (assuming no debug code is introduced).
>>

>I know of at least one compiler that will generate code that will fail.
>
>The old microsoft 16 bit compiler had an option to pass the 'this'
pointer in
>registers. If you compiled in "far" mode, that meant loading the high
bits of
>the address into ES. On the x86, just *loading* an invalid value into a
>segment register causes a runtime exception - it doesnt wait until you
*use*
>it. So the above code would (most likely) fail.

Thanks for that. I had a feeling it might happen on some machine with
a weird and wonderful instruction set. Mind you that x86 segment
kludge is pretty weird*!

JG

* Joke

John Goodwin

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
On 14 Jan 1999 00:17:33 -0500, Francis Glassborow
<fra...@robinton.demon.co.uk> wrote:

>In article <369c9f6c....@news.demon.co.uk>, John Goodwin
><J...@opticon.demon.co.uk> writes


>>>Andrei Alexandrescu wrote:
>>>>
>>>> class A {
>>>> int f(int i) { return i + 5; }
>>>> };

>>>> int main()
>>>> {
>>>> A * pA;
>>>> pA->f(1);
>>>> }
>>>> The question is, is the code guaranteed to work?
>>>
>>>No. It is undefined behaviour since a member function on an invalid
>>>(uninitialized) object is invoked. It is equivalent to (*pA).f(1),
and,
>>>there is no object at (*pA).
>>
>>It may be useful to consider it that way at a higher level, but most
>>ordercode processors would not in fact operate in that fashion.
>>The *call* to the member function above does not require a pointer.
>>True the uninitialisd pA will be passed (without dereferencing), but
>>since this is not used it's hard to see how it would cause an error.
>>

>>I'd be interested to see if anyone can suggest how it could be
>>compiled to produce an error (assuming no debug code is introduced).
>

>The type of f is pointer to member taking an int and returning an int.

Agreed.

>Without optimisation:
>
>the code must dereference f (that is the way functions work) to obtain
>the location of f,

Only if you consider placing a literal value in the IP register to be
dereferencing !

> the arguments for the call (this and a temp int with
>value 1) placed for transfer. At this stage you have a wild pointer
>floating around.

Agreed.

>The very fact that even the simplest compiler circumvents this is what
>makes writing code so very dangerous.

Again, agreed. Of course, a decent development compiler would complain
about the use of an uninitialised variable, but not all are that
forthcoming :(

>Its working relies entirely on
>several fortuitous circumstances (non-virtual function, makes no access
>to member data etc.)

Yes, and in the example given (which is what we were discussing), all
the circumstances to the left of etc. do indeed hold.

>I do not believe that any programmer would actually write such code as
a
>permanent feature. It has a distinct feel of being a place holder.

Agreed.

>Put it another way, the code may 'work' on most compilers but any team
>leader that allowed code like that out of the door would have to work
>very hard to remain in work:) in a respectable software shop.

Agreed, which is why I said:

|[This is not a defence of the technique of course]

JG

John Goodwin

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
On 13 Jan 1999 20:43:30 -0500, cla...@Eng.Sun.COM (Steve Clamage)
wrote:

>"Andrei Alexandrescu" <alexan...@micromodeling.com> writes:
>
>>Hello, the problem is simple:
>

>>class A {
>> int f(int i) { return i + 5; }
>>};
>
>>int main()
>>{
>> A * pA;
>> pA->f(1);
>>}
>

>>The question is, is the code guaranteed to work? Please refrain
yourselves
>>from making comments on bad style etc. I've seen this in a test.
>

>Auto variale pA is not initialized, but must be dereferenced
>to call the non-static member function f.

Am I (and my colleagues) at odds with the general concensus on what
constitutes a dereference?

I cannot see any reason why, in this case, the pointer needs to be
dereferenced to obey the code.

It is not needed for the call instruction, and even if the "this"
pointer is passed, it should not be dereferenced in the passing
process, and is not used in the function.

John Goodwin

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
On 14 Jan 1999 06:37:09 -0500, cla...@Eng.Sun.COM (Steve Clamage)
wrote:

snip

>The original question was whether the code was guaranteed to
>work. Any expression of the forms
> p->data_member
> p->func_member()
>whether static or nonstatic data or function members, has undefined
>behavior unless p points to an object of the appropriate type.
>
>Even when accessing a static member, the standard requires that
>the object-expression to the left of the "->" be evaluated,

Can you explain *exactly* what you mean by "evaluated" ?

Are you saying that the standard says that a compiler is obliged to
insert redundant code to perfom some spurious "evaluation" ?

That would be a *very* odd requirement. Indeed, I can't imagine anyone
ever producing a compliant optimising compiler for the x86
architecture if that is the case.

> and
>"a->b" is defined to mean the same thing as "(*a).b".
>
>Undefined behavior means that there are no guarantees.

I hope you didn't think I was arguing that there were!

Valentin Bonnard

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
David Kastrup wrote:
>
> "Andrei Alexandrescu" <alexan...@micromodeling.com> writes:
>
> > Hello, the problem is simple:
> >
> > class A {
> > int f(int i) { return i + 5; }
> > };
> >
> > int main()
> > {
> > A * pA;
> > pA->f(1);
> > }
> >
> > The question is, is the code guaranteed to work?
>
> No. It would be legal if f were a static member function.

It used to be legal, but now the object has to be evaluted
even with a static function: fun()->static_member has to
call fun(). So the behaviour would be undefined as well.

--

Valentin Bonnard mailto:bonn...@pratique.fr
info about C++/a propos du C++: http://pages.pratique.fr/~bonnardv/

Valentin Bonnard

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
John Goodwin wrote:
>
> On 13 Jan 1999 20:43:30 -0500, cla...@Eng.Sun.COM (Steve Clamage)
> wrote:
>
> >"Andrei Alexandrescu" <alexan...@micromodeling.com> writes:

> >> A * pA;
> >> pA->f(1);

> >>The question is, is the code guaranteed to work? Please refrain


> >>yourselves
> >>from making comments on bad style etc. I've seen this in a test.

> >Auto variale pA is not initialized, but must be dereferenced
> >to call the non-static member function f.
>
> Am I (and my colleagues) at odds with the general concensus on what
> constitutes a dereference?

It isn't a consensus, it's a definition.

pointer->member means