Steven said:
Steven said:
Larry I Smith wrote:
Steven T. Hatton wrote: [...]
AFAIK, you *can* pass a null to func() in your example, and the
compiler
will accept it. Your code will segfault when you do so. If you pass
a
pointer, you can check for null, before accessing it. I, therefore,
suggest using a pointer instead of a reference.
There is no such thing as a null reference. However, see below.
If a function takes a ref, then NULL can not be passed.
Attempting to pass NULL causes a compile error.
For example:
[snip]
func(0); // causes a compile error
In your example, you are not trying to pass null, you are trying to pass
a
literal. That results in the attempt to create a temporary object of
type int and assign it to the non-const reference.
True, 0 or NULL is the null pointer constant, which is not the same
thing as a null pointer.
Actually it is an integer literal which when assigned to a pointer to type T
is converted to a null pointer constant to T. The distinction may seem
trivial, but the error is not due to the fact that 0 is being used. It is
due to the fact that a literal is being used to initialize a temporary of
type int.
True, but that does not contradict what I said.
§4.10
"A null pointer constant is an integral constant expression (5.19)
rvalue of integer type that evaluates to zero. A null pointer constant
can be converted to a pointer type; the result is the null pointer
value of that type..."
As I pointed out, the null pointer constant is not the same thing as a
null pointer so I was agreeing with you.
The act resulting in the undefined behavior is in main where the attempt to
dereference a null pointer is made. Where the actual /behavior/ occurs is
a different story. Basically, accessing the null pointer gives
'permission' to produce undefined behavior.
Undefined simply means you've left the world of standard C++ and
your program could do anything. As some on Usenet are fond of saying,
blue demons could fly out of your nose.
You enter the land of undefined behavior as soon as you dereference
a null pointer. How and when that manifests in some particular way
in a particular case is... well... undefined.
"But," you may say, "main doesn't dereference the pointer. The
seg fault [or whatever] occurs in func!"
First, it is not unusual for a run-time error (if one occurs
at all) to happen some time after the undefined behavior that
caused it.
The segfault *is* the undefined behavior that results from the attempt to
dereference a null pointer.
A segfault is one possible consequence of doing something that causes
undefined behavior.
The C++ Standard does not define what is meant by dereferencing a pointer.
The Standard uses dereferencing as a synonym for indirectione, e.g.,
§1.9/4
"Certain other operations are described in this International
Standard as undefined (for example, the effect of dereferencing
the null pointer). [Note: this International Standard imposes no
requirements on the behavior of programs that contain undefined
behavior.]"
§8.3.2
Note: in particular, a null reference cannot exist in a well-defined
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.
§5.3.1/1
"The unary * operator performs /indirection/: the expression to which it is
applied shall be a pointer to an object type, or a pointer to a function
type and the result is an lvalue referring to the object or function to
which the expression points. If the type of the expression is ?pointer to
T,? the type of the result is ?T.?"
An lvalue is said to refer to specific storage, so I would say that pretty
much means returning an address.
An lvalue is just an expression that refers to an object or function
(§3.10) and an object occupies storage. A local variable of type int
is an lvalue, for example. This says nothing about how a C++
implementation might choose to represent pointers and references
under the hood.
No, applying the indirection operator to a variable of type pointer to type
T is the definition of a behavior. The _result_ is undefined.
Very well, the result of dereferencing a null pointer is undefined.
[snip]
For the record, I don't believe I actually said the result was a null
reference.
You said "AFAIK, you *can* pass a null to func() in your example",
and the example in question declared func as taking a parameter of
reference type.
At a practical level, I think I understand your general point that
using references instead of pointers does not really protect you
against undefined behavior. Undefined behavior could still occur
if you form a reference by dereferencing a null pointer, return
a reference to a local object, etc.
However, my response would be that it's the responsibility of
the code that initializes the reference (e.g., by dereferencing
a pointer in your example) to make sure the object actually exists.
That's the code that needs to be scrutinized for possible undefined
behavior. A function that takes a reference parameter should be
able to assume that the reference is actually bound to an object;
if it is not then the program is already undefined and the fault
lies elsewhere.