Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Null references??

71 views
Skip to first unread message

Umesh Nair

unread,
Oct 25, 2001, 5:52:16 PM10/25/01
to
Hi,

I have seen code like the following:

// some_file.h

void foo (int x, ostream& os = *(ostream *)0);


// some_file.cxx

void foo (int x, ostream& os)
{
ostream* out_p;
if (&os == (ostream*)0) {
out_p = &cout;
} else {
out_p = &os;
}
...
...
}

I know this is ugly. My question is whether this is legal. This is an
attempt to use null references, just like null pointers, to denote error or
default values. The code above is compiled on many popular compilers.

Thanks,

-------------------------
Umesh Nair

Please post the reply to the group.
My actual e-mail ID does not have "spam" or "maps".
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]

Mike Wahler

unread,
Oct 26, 2001, 2:32:02 AM10/26/01
to
Umesh Nair wrote in message <3bd6...@solnews.wv.mentorg.com>...

>Hi,
>
>I have seen code like the following:
>
>// some_file.h
>
>void foo (int x, ostream& os = *(ostream *)0);
>
>// some_file.cxx
>
>void foo (int x, ostream& os)
>{
> ostream* out_p;
> if (&os == (ostream*)0) {
> out_p = &cout;
> } else {
> out_p = &os;
> }
>}
>
>I know this is ugly. My question is whether this is legal. This is an
>attempt to use null references, just like null pointers, to denote error or
>default values. The code above is compiled on many popular compilers.

ISO/IEC 14882:1998(E)

8 Declarators

8.3.2 References

4 There shall be no references to references, no arrays of
references, and no pointers to references. The declaration
of a reference shall contain an initializer (8.5.3) except
when the declaration contains an explicit extern specifier
(7.1.1), is a class member (9.2) declaration within a class
declaration, or is the declaration of a parameter or a return
type (8.3.5); see 3.1. A reference shall be initialized to
refer to a valid object or function. [Note: in particular,
a null reference cannot exist in a well苓efined program,
because the only way to create such a reference would be to
bind it to the "object" obtained by dereferencing a null
pointer, which causes undefined behavior. As described in 9.6,
a reference cannot be bound directly to a bit苯ield. ]


-Mike


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

[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]

[ Note that the FAQ URL has changed! Please update your bookmarks. ]

Andre Kostur

unread,
Oct 26, 2001, 2:33:17 AM10/26/01
to
"Umesh Nair" <umesh...@maps.mentor.spam.com> wrote in
<3bd6...@solnews.wv.mentorg.com>:

> Hi,
>
> I have seen code like the following:
>
> // some_file.h
>
> void foo (int x, ostream& os = *(ostream *)0);

At this point you have invoked "undefined behaviour". Dereferencing a zero
pointer is never safe.

> // some_file.cxx
>
> void foo (int x, ostream& os)
> {
> ostream* out_p;
> if (&os == (ostream*)0) {

A test that should not have to be done. In order for this to happen, the
variable 'os' had to be created by dereferencing a zero pointer, which is
undefined behaviour to begin with.

> out_p = &cout;
> } else {
> out_p = &os;
> }
> ...
> ...
> }
>
> I know this is ugly. My question is whether this is legal. This is an
> attempt to use null references, just like null pointers, to denote

There is no such thing as a null reference. A reference always refers to an
object. To have it do otherwise is undefined behaviour.

> error or default values. The code above is compiled on many popular
> compilers.

Unfortunately whether a compiler will compile a file or not is not
sufficient evidence that it is correct C++ code. Heck there are compilers
out there in widespread use which _won't_ compile some perfectly correct C++
code.....

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

[ comp.std.c++ is moderated. To submit articles, try just posting with ]


[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]

Ivan Vecerina

unread,
Oct 29, 2001, 12:27:51 PM10/29/01
to
Umesh Nair said on 25 Oct 2001 17:52:16 -0400:

>I have seen code like the following:
....

>void foo (int x, ostream& os = *(ostream *)0);
(... then test &os for null in the function body ...)

This is undefined behaviour. Will work on all compilers
I know, but dereferencing a null pointer is just not legal,
(even if a reference is usually just another pointer).

For info and fun, see <http://www.gotw.ca/conv/002.htm> .

A standard-compliant alternative would be:

void foo(int x, ostream* os=0);
void foo(int x, ostream& os) { return foo(x,&os); }

Not exactly the same, but guaranteed to be portable.


hth -ivec.

--
Ivan Vecerina, Dr. med. <> http://www.post1.com/~ivec
Research Manger, XiTact <> http://www.xitact.com
Brainbench MVP for C++ <> http://www.brainbench.com

James Kanze

unread,
Oct 30, 2001, 2:48:27 AM10/30/01
to
"Umesh Nair" <umesh...@maps.mentor.spam.com> wrote in message
news:<3bd6...@solnews.wv.mentorg.com>...

> I have seen code like the following:

Fire the guy who wrote it.

> // some_file.h

> void foo (int x, ostream& os = *(ostream *)0);

If foo is called without a second parameter, undefined behavior
occurs. I've used at least one compiler which would core dump at the
call site.

> // some_file.cxx

> void foo (int x, ostream& os)
> {
> ostream* out_p;
> if (&os == (ostream*)0) {

Since this condition is never true, a good compiler will optimize the
test out, and just generate code for the else branch.

> out_p = &cout;
> } else {
> out_p = &os;
> }
> ...
> ...
> }

> I know this is ugly. My question is whether this is legal. This is
> an attempt to use null references, just like null pointers, to
> denote error or default values. The code above is compiled on many
> popular compilers.

It may compile without error; it's very likely not to work correctly
if optimizing is turned on.

--
James Kanze mailto:ka...@gabi-soft.de
Beratung in objektorientierer Datenverarbeitung --
-- Conseils en informatique orientée objet
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany, Tél.: +49 (0)69 19 86 27

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

[ comp.std.c++ is moderated. To submit articles, try just posting with ]


[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]

Umesh Nair

unread,
Oct 30, 2001, 10:51:26 AM10/30/01
to
James Kanze wrote in message ...

James,

I agree with you (and the other posters. Thanks everyone), but what
you said is not completely true. The piece of software that has this
code has been working fine for almost a decade. It survived six platforms
(including Linux, NT and various unixes) and four flavors of compilers
with different optimization levels. In all cases, the above code was
executed
because that function was almost all the time called without the default
parameter. I saw the code, got surprised, and is planning to correct it.

The reason why it is working is, most compilers implement pointers and
references almost identically, and the hidden pointer in a reference
variable
is dereferenced only when it is used. But it is not conforming to the
standard.
So, I can expect a crash any time on a more standard conforming compiler.
Hence this post.

Thanks to all people who responded to this post.

- Umesh

-------------------------
Umesh Nair

Please post the reply to the group.
My actual e-mail ID does not have "spam" or "maps".
---

Allan W

unread,
Nov 2, 2001, 2:20:05 AM11/2/01
to
Umesh Nair said on 25 Oct 2001 17:52:16 -0400:
> // some_file.h

> void foo (int x, ostream& os = *(ostream *)0);
>
> // some_file.cxx
> void foo (int x, ostream& os)
> {
> ostream* out_p;
> if (&os == (ostream*)0) {
> out_p = &cout;
> } else {
> out_p = &os;
> }
> ...
> ...
> }

iv...@mail.com (Ivan Vecerina) wrote:
> This is undefined behaviour. Will work on all compilers
> I know, but dereferencing a null pointer is just not legal,
> (even if a reference is usually just another pointer).

I agree.

> For info and fun, see <http://www.gotw.ca/conv/002.htm> .
>
> A standard-compliant alternative would be:
>
> void foo(int x, ostream* os=0);
> void foo(int x, ostream& os) { return foo(x,&os); }
>
> Not exactly the same, but guaranteed to be portable.

The effect of the null pointer was to use std::cout. So a simpler
version would be:

// some_file.h
void foo (int x, ostream& os = std::cout);

This is legal, isn't it?

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

[ comp.std.c++ is moderated. To submit articles, try just posting with ]


[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]

Umesh Nair

unread,
Nov 5, 2001, 10:41:55 AM11/5/01
to
Allan W wrote in message
<7f2735a5.01110...@posting.google.com>...
>[...]

>The effect of the null pointer was to use std::cout. So a simpler
>version would be:
>
> // some_file.h
> void foo (int x, ostream& os = std::cout);
>
>This is legal, isn't it?

It definitely is, and is how I refactored the code.
I was sure that the above code is illegal, but I wanted to make sure
before changing it.

Thanks, folks.
---

Ivan Vecerina

unread,
Nov 7, 2001, 3:40:57 AM11/7/01
to
Allan W said on 02 Nov 01 07:20:05 GMT:

>iv...@mail.com (Ivan Vecerina) wrote:
>> A standard-compliant alternative would be:
>>
>> void foo(int x, ostream* os=0);
>> void foo(int x, ostream& os) { return foo(x,&os); }
>>
>> Not exactly the same, but guaranteed to be portable.
>
>The effect of the null pointer was to use std::cout. So a simpler
>version would be:
>
> // some_file.h
> void foo (int x, ostream& os = std::cout);

Yes indeed, I have to admit that I did not pay enough attention
to the body of the function.

>This is legal, isn't it?

Perfectly. This solution requires <iostream> to be included
by the header (or all its users), but this should not have
a noticeable impact on compilation.

Including <iostream> is even SAFER, as it should ensure that cout
will be initialized even if foo() is called prior to program
startup or after program termination (e.g. by the constructor
or destructor of a global object).
((early initialization of cout is usually enforced in <iostream>
by defining a static instance of type ios_base::Init .... ))

Thank you for pointing out this simpler and better solution,
Ivan


--
Ivan Vecerina, Dr. med. <> http://www.post1.com/~ivec

Soft Dev Manger, XiTact <> http://www.xitact.com


Brainbench MVP for C++ <> http://www.brainbench.com

Umesh P Nair

unread,
Nov 7, 2001, 10:23:39 AM11/7/01
to
Ivan Vecerina wrote in message <3be7...@news.swissonline.ch>...

>Including <iostream> is even SAFER, as it should ensure that cout
>will be initialized even if foo() is called prior to program
>startup or after program termination (e.g. by the constructor
>or destructor of a global object).
> ((early initialization of cout is usually enforced in <iostream>
> by defining a static instance of type ios_base::Init .... ))


But the nifty_counter in the iostream implementation should have
handled this situation, right?

-------------------------
Umesh Nair

Please post the reply to the group.
My actual e-mail ID does not have "spam" or "maps".
---

t...@cs.ucr.edu

unread,
Nov 7, 2001, 10:24:03 AM11/7/01
to
In comp.lang.c++.moderated James Kanze <ka...@gabi-soft.de> wrote:
: "Umesh Nair" <umesh...@maps.mentor.spam.com> wrote in message
: news:<3bd6...@solnews.wv.mentorg.com>...

:> I have seen code like the following:

: Fire the guy who wrote it.

:> // some_file.h

:> void foo (int x, ostream& os = *(ostream *)0);

: If foo is called without a second parameter, undefined behavior
: occurs.

May a conforming compiler reject this code even if the program
contains no such call?

May a conforming compiler reject this code if user input determines
whether or not such a call occurs at run time?

(This thread presents an interesting variant of the points under
discussion in another thread, entitled "Is *(int*)0 an lvalue?")

Tom Payne
---

Ivan Vecerina

unread,
Nov 9, 2001, 5:41:19 AM11/9/01
to
Umesh P Nair said on 7 Nov 2001 10:23:39 -0500:

>
>Ivan Vecerina wrote in message <3be7...@news.swissonline.ch>...
>
>>Including <iostream> is even SAFER, as it should ensure that cout
>>will be initialized even if foo() is called prior to program
>>startup or after program termination (e.g. by the constructor
>>or destructor of a global object).
>> ((early initialization of cout is usually enforced in <iostream>
>> by defining a static instance of type ios_base::Init .... ))
>
>
>But the nifty_counter in the iostream implementation should have
>handled this situation, right?

The ios_base::Init class I mentioned is a 'nifty counter'
implementation. Note that the technique ensures that the static
nifty counter object is constructed before any other object in
a translation unit. But this does not work accross translation
units.

Error scenario:
- the first global constructed is a user global in a file that does
not include <iostream> ==> no nifty counter is created yet.
- the constructor of that global calls a function f() of another
translation unit, which uses cout. But the globals
of that translation unit have not been initialized yet,
so neither has been cout.

This situation is very unlikely, but not impossible. It can occur
with current <iostream> implementations (maybe all?).
See for instance the warning at:
http://www.dinkumware.com/htm_cpl/iostream.html


For those who want to read about the nifty_counter technique
(as described by Stroustrup in the ARM), see this quote:
http://www.kai.com/C_plus_plus/v4.0/doc/tutorials/static_initialization.html

Regards,
Ivan

--
Ivan Vecerina, Dr. med. <> http://www.post1.com/~ivec
Soft Dev Manger, XiTact <> http://www.xitact.com
Brainbench MVP for C++ <> http://www.brainbench.com

0 new messages