6

What is the default value of a function pointer in C++? (Apparently it can't be NULL, so what is it?)

How is this program supposed to behave and why?

struct S { void (*f)(); };

int main()
{
    S s = S();
    s.f();   // What is the value of s.f?
}
user541686
  • 205,094
  • 128
  • 528
  • 886
  • 3
    *(Apparently it can't be `NULL`, ...)*. Why? – Nawaz Feb 12 '13 at 09:23
  • @Nawaz: My understanding was that function pointers cannot be `NULL`... am I wrong? – user541686 Feb 12 '13 at 09:24
  • 3
    @Mehrdad: Yes, you're wrong. – Nawaz Feb 12 '13 at 09:25
  • @WhozCraig, Pubby: I could almost swear I came across some piece of code a while ago that said something along the lines of "since function pointers can't be null, we have to make a default handler" or something of that sort... I guess I misunderstood then? – user541686 Feb 12 '13 at 09:29
  • @Mehrdad it certainly sounds like it. – WhozCraig Feb 12 '13 at 09:30
  • @Mehrdad Or that piece of code was wrong. *Any* pointer can be null. – Angew is no longer proud of SO Feb 12 '13 at 09:30
  • 1
    @Angew: Hmm... *any* pointer? So you mean even *member* function pointers can be null? – user541686 Feb 12 '13 at 09:32
  • 1
    @Mehrdad maybe it was handling the corner case when the pointer was NULL? i.e. call default handler when pointer points to no function. – Pubby Feb 12 '13 at 09:32
  • @Pubby: Possibly, though that wasn't my impression when I saw it. – user541686 Feb 12 '13 at 09:33
  • 1
    @Mehrdad Yes. Object pointers, function pointers, data member pointers, member function pointers, `void*`, all can be null. That's why unlike C, in C++ the macro `NULL` **cannot** be defined as `((void*)0)`, which wouldn't be assignable into member pointers. – Angew is no longer proud of SO Feb 12 '13 at 09:42
  • 1
    @Mehrdad Even member pointers. And to be precise, a pointer can be null; it cannot be `NULL` (which is a null pointer constant, which curiously enough, is _not_ a pointer). – James Kanze Feb 12 '13 at 09:43
  • 2
    @Angew The reason the macro `NULL` cannot be defined as `((void*)0)` is because the C++ standard says it can't. `0` has type `int`, and except for special wording, couldn't be assigned to any pointer. `((void*)0)` has type `void*`, and except for special wording in the C standard, could not be assigned to a function pointer in C. Both standards contain special language to allow null pointer constants to break the type system. C++ uses the traditional definition; for some reason, the C committee extended the legal types in a null pointer constant. – James Kanze Feb 12 '13 at 09:46
  • @JamesKanze: Thanks for the clarification, I didn't know you can't assign `NULL` to function pointers! – user541686 Feb 12 '13 at 09:55
  • @Mehrdad You can assign `NULL` to any pointer, because it meets the special definition of a null pointer constant (an integral constant expression evaluating to 0). This is a very special (magic?) conversion, since it only works if the expression is constant, and only for one specific value. (Note that `int i = 0; void* p = reinterpret_cast( i );` may result in something different than `void* p = 0;`.) – James Kanze Feb 12 '13 at 09:59
  • Stroustrup says somewhere that there is never any need for NULL in a properly written C++ program. Zero always suffices. – user207421 Feb 12 '13 at 09:59
  • @JamesKanze On a deep enough level, you are right indeed. In the level of casual usage, I believe my explanation to be a bit more understandable. – Angew is no longer proud of SO Feb 12 '13 at 10:03
  • 1
    @EJP While there's technically no need, it can help code readability in certain contexts. That is, assuming you consistently use `NULL` for all null pointer values and *only* for null pointer values. – Angew is no longer proud of SO Feb 12 '13 at 10:04
  • @Angew Except that your explination is completely false. There is no fundamental reason why `((void*)0)` could not have been made a null pointer constant, as it is in C, except tradition. It works as well as `0`. Without the special rules, you couldn't assign `0` to _any_ pointer. – James Kanze Feb 12 '13 at 10:15
  • 1
    @Angew I agree with you about using `NULL`, there are valid arguments for both sides (which is why `nullptr` was added to the language). Given `template void f( T );`, some people find it surprising that `f( NULL )` instantiates the template for `int`. If your coding style makes extensive use of (abuses?) templates and function overloading, you may prefer using `0` as the null pointer; if it doesn't you may prefer `NULL` (as I do). – James Kanze Feb 12 '13 at 10:17
  • @JamesKanze Yes, my comment applies to C++03 of course. In C++11, one should only use `nullptr` as the null pointer constant. – Angew is no longer proud of SO Feb 12 '13 at 10:37
  • *"default value of a function pointer"* - Like any other primitive type, **indeterminate**. *zero-initialized value* (which seems the one you're after here), though - null pointer. – Christian Rau Feb 12 '13 at 10:40

5 Answers5

18

First any pointer can be null. It is the one universal truth about pointers. That said, yours will be null, but not necessarily for the reasons you may think;

C++11 § 8.5,p10

An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.

This is important because your declaration includes this :

S s = S();

By the definition of value initialization:

C++11 § 8.5,p7

To value-initialize an object of type T means:

  • if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);

  • if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, if T’s implicitly-declared default constructor is non-trivial, that constructor is called.

  • if T is an array type, then each element is value-initialized;

  • otherwise, the object is zero-initialized.

Which brings us to what it means for your object-type to be zero-initialized:

C++11 § 8.5,p5

To zero-initialize an object or reference of type T means:

  • if T is a scalar type (3.9), the object is set to the value 0 (zero), taken as an integral constant expression, converted to T (103)

  • if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class subobject is zero-initialized and padding is initialized to zero bits;

  • if T is a (possibly cv-qualified) union type, the object’s first non-static named data member is zero- initialized and padding is initialized to zero bits;

  • if T is an array type, each element is zero-initialized;

  • if T is a reference type, no initialization is performed.

103) As specified in 4.10, converting an integral constant expression whose value is 0 to a pointer type results in a null pointer value.

The latter is the reason you're pointer is null. It will not be guaranteed-so by the standard given the same code, but changing the declaration of s to this:

S s;

Given a declaration like the above, a different path is taken through the standard:

C++11 § 8.5,p11

If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, an object with automatic or dynamic storage duration has indeterminate value. [ Note: Objects with static or thread storage duration are zero-initialized, see 3.6.2.

Which then begs the last question, what is default initialization:

C++11 § 8.5,p6

To default-initialize an object of type T means:

  • if T is a (possibly cv-qualified) class type (Clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);

  • if T is an array type, each element is default-initialized;

  • otherwise, no initialization is performed.

Rafael Almeida
  • 10,352
  • 6
  • 45
  • 60
WhozCraig
  • 65,258
  • 11
  • 75
  • 141
  • Oh well, 'A' for effort :-) – Kerrek SB Feb 12 '13 at 09:52
  • I was aware of the initialization actually, that's why I did it... but +1 for mentioning it anyway lol. – user541686 Feb 12 '13 at 09:54
  • A whole bunch of Standard quotes just to say "The default value of a function pointer is NULL"? – Puppy Nov 22 '14 at 15:13
  • 1
    @Puppy Its not that it *can* be NULL; its how it *got* there. It got there by initialization, and the method of initialization is paramount to its final value. If what you said verbatim were universally true, "the default value of a function pointer is NULL", it wouldn't matter. Its not, so it does. – WhozCraig Nov 22 '14 at 16:33
8

In your case the object s is zero-initialized which means the function pointer is NULL.

struct S { void (*f)(); };

int main()
{
    S s = S();
    if ( s.f == NULL)
       std::cout << "s.f is NULL" << std::endl;
}

Output:

s.f is NULL

Online demo.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
3

Function pointer can be NULL, this way you can indicate that they don't point to anything!

Sean
  • 60,939
  • 11
  • 97
  • 136
3

A function pointer can be NULL and you may assign NULL to it. Have a look here for instance:

#include <iostream>

using namespace std;

struct S { void (*f)(); };

int main()
{
    S s = S();
    s.f = NULL;
    return 0;
}

I believe the way you call the constructor of the structure(with ()), f will be NULL.

Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
0

In C++ (and C), pointers (regardless of type) do not have a default value per se; they take what ever happens to be in memory at the time. However, they do have a default initialised value of NULL.

Default Initialisation

When you don't explicitly define a constructor, C++ will call the default initialiser on each member variable, which will initialise pointers to 0. However, if you define a constructor, but do not set the value for a pointer, it does not have a default value. The behaviour is the same for integers, floats and doubles.

Aside

int main()
{
    S s = S();
    s.f();   // <-- This is calling `f`, not getting the pointer value.
}
Alex Chamberlain
  • 4,147
  • 2
  • 22
  • 49