4

I am looking at something that I discovered in an old code base, and I am pretty confused.

Here is a function definition:

void vUpdateSequenceDetailsAndIncrement(
            const CallEvent& roCPEvent,
            const CallInfo& roCallInfo,
            BOOL bCreationEvent);

Here it is being called:

vUpdateSequenceDetailsAndIncrement(roCPEvent, NULL, FALSE);

Here NULL is being passed directly to the reference parameter roCallInfo. This function eventually calls:

vTimeChange(*pSeqDetails, roCPEvent, roCallInfo);

which is defined:

void vTimeChange(const SequenceDetails& roSequenceDetails,
        const CallEvent& roCPEvent,
        const CallInfo& roCallInfo)

Again passing the possibly NULL value to roCallInfo. I thought that NULL could not be passed as a reference? Does anyone know if VC++ 4.x had some kind of problem which made this kind of code okay? If NULL can be passed as a reference then what happens when in vTimeChange something like this happens:

roCallInfo.getCallStartTime(); 

Is that not a dereference of NULL in the same way as if I were to do

CallInfo * info = NULL;
info->getCallStartTime();

? I'll probably put a guard in there anyways and let the compiler remove it if unnecessary, but I'd love to get to the bottom of how this is happening!

Thanks.

Dennis
  • 3,683
  • 1
  • 21
  • 43
  • 1
    Would this be relevant? http://stackoverflow.com/questions/57483/what-are-the-differences-between-pointer-variable-and-reference-variable-in-c/57656#57656 – Mark Ransom Sep 07 '11 at 17:25
  • @Mark Ransom - Yes actually. That does help. I thought I had all those questions read through. Still c-smiles answer below is interesting. – Dennis Sep 07 '11 at 17:30
  • The call to `info->getCallStartTime()` doesn't necessarily dereference NULL. That syntax just causes the address of `info` to be passed as `this`. If the first statement in the function is `if (this == NULL) return`, then there would be no dereferencing and no undefined behavior. – Ken Jackson Feb 06 '19 at 22:47
  • Wow this surprised me greatly too. After rearranging constructor arguments (but forgetting to update the call) I was inadvertently passing a nullptr into a constant reference and the C++20 compiler didn't warn me in the slightest. I think this is because I was using incomplete (forward-declarations) classes in parameters. The undefined behavior for me was that an entirely "random" different class constructor was being called with all null inputs. Needless to say it took me quite a long time to resolve this. – MasterHD Apr 11 '23 at 21:37

3 Answers3

8

Depends on how NULL is defined in VC 4.2

If it is just

#define NULL 0 

then you are actually getting this under the hood:

vUpdateSequenceDetailsAndIncrement(roCPEvent, CallInfo(0), FALSE);

and reference of temp var of type CallInfo is passed to the function (if CallInfo has compatible ctor)

c-smile
  • 26,734
  • 7
  • 59
  • 86
  • It is #defined to (0)... does that mean that using something like `roCallInfo.getCallStartTime();` is fine? And obviously it would also mean that `if(roCallInfo == NULL)` would always be `false`. – Dennis Sep 07 '11 at 17:26
  • This `if(&roCallInfo == NULL)` will be false but this `if(roCallInfo == NULL)` of yours will lead to call of `CallInfo::operator==(int)` – c-smile Sep 07 '11 at 17:29
  • 2
    Dennis, it means that `CallInfo` has a constructor that allows it to be *implicitly created* from an `int`. If it also has a definition for `operator==`, then your comparison against `NULL` will also work because `NULL` will again implicitly convert to a `CallInfo`. The program creates a *temporary* `CallInfo` object that lives for the duration of the statement it's involved in, so you're calling `getCallStartTime` on that temporary object. That's perfectly safe. If you don't want this implicit conversion, then mark the constructor `explicit`. – Rob Kennedy Sep 07 '11 at 17:29
  • Thanks lads. That helps a lot. – Dennis Sep 07 '11 at 17:31
5

I thought that NULL could not be passed as a reference?

A valid reference can't be null, but an invalid one can be null.
What you have here is an Invalid Reference.

The fact that a reference can't be null does not mean that references are somehow safer than pointers, as you see here in this case. There can be many ways in which an program may lead to Invalid References, Your code is one such example.

Reference Wikipedia:
There are also de facto ways that a reference can start out invalid. Because a reference is usually implemented as an underlying pointer, initializing a reference on a pointer-dereferencing expression, will usually be implemented by the compiler as a simple assignment from the pointer to the underlying pointer of the reference. Thus, if you have a NULL pointer or a pointer pointing to an invalid location in memory, you will de facto have a reference that points to NULL or an invalid location. C++ purists would argue that, technically, dereferencing a NULL or invalid pointer leads to undefined behavior anyway, so this does not violate the assertions above that a reference cannot be null or point to arbitrary places in memory. However, this ignores the fact that in this case, the underlying implementation simply performs an "assignment", and there is no access of the memory location involved, so this initialization of the reference usually does not cause problems, and programmers must be aware of the possibility of a de facto "invalid" reference in a real program.

Using the Invalid Reference roSequenceDetails would eventually lead to an Undefined Behavior.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
0

The expression *pSeqDetails results in undefined behavior if pSeqDetails is null. Anything can happen. Most implementations don't make any particular checks; your code will seem to work until you do something that actually requires the object, then it will fail. But an implementation could cause it to fail immediately.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Sorry, I've not made my question clear enough... I was looking for info on whether the passing of "NULL" directly is okay or not. `*pSeqDetails` is guarded for NULL already. Thanks though. – Dennis Sep 07 '11 at 17:21