8

I have a class which holds a number of function pointers and I would like for them all to be initialized to NULL when objects are constructed. To do this I was planning on using memset on the memory locations from the first pointer to that of the last pointer however, I am unsure if this would work 100% of the time.

Is it guaranteed that if these function pointers are declared contiguously within the class that their memory locations would also be contiguous. I assume that padding won't affect what I'm trying to do as any padding bytes would just be set to NULL also.

Example class implementation

class C
{
private:
    void (*func1)();
    void (*func2)();
    void (*func3)();
    void (*func4)();
};
Bo Persson
  • 90,663
  • 31
  • 146
  • 203
ctor
  • 5,938
  • 2
  • 26
  • 37
  • 9
    No and never use `memset` in C++ like this. – Alex Chamberlain Mar 15 '13 at 11:11
  • 2
    A better question is how to get all members initialized to NULL. Ask what you really want. – Pubby Mar 15 '13 at 11:12
  • What would be the best way to set them all to NULL? Obviously each could be done individually but as more were added to the class that could become very cumbersome. – ctor Mar 15 '13 at 11:13
  • @Pubby http://meta.stackexchange.com/q/66377/188010 – Alex Chamberlain Mar 15 '13 at 11:13
  • Even if it would work, it's scary, dangerous and untransparent. – JSQuareD Mar 15 '13 at 11:13
  • 1
    @ctor you can put the pointers in a separate struct, then initialize with aggregate syntax, ie `{0}`. This initializes everything to null. – Pubby Mar 15 '13 at 11:14
  • It is really sad that answers need to be more than three characters – PlasmaHH Mar 15 '13 at 11:21
  • @Pubby You should make an answer out of the `struct` suggestion. This is the simplest solution. And in the initializer list, you can just do `data()` (where `data` is the name of the struct), and get all of the members zero initialized. – James Kanze Mar 15 '13 at 11:28
  • Although I appreciate the comments here, they're not really 'on-topic'. Sure, I agree it's probably unwise to initialize with memset and certainly not good practice; moreover there are certainly betters ways of doing it as people have pointed out. However, the question is 'are they contiguous or not' and in the code that he has shown, memset will work fine here. Of course, that's no guarantee that things won't blow-up in the future. – The Welder Feb 23 '16 at 10:16

4 Answers4

10

It is guaranteed that they appear with increasing addresses in the order declared. This is true in general of data members without intervening access specifiers, so if there are other data members in the class then the only way they could intervene is if there are access specifiers in there.

I don't think it's guaranteed to be safe to modify padding bytes. I don't think it's guaranteed that the implementation won't put "something important" in between data members, although I can't immediately think of anything an implementation would want to put in there. Type information for a strangely-designed accurate-marking GC? Recognizable values to test for buffer overruns?

It's not guaranteed that all-bits-zero represents a null function pointer.

You could deal with the issue of the all-bits-zero representation using something like:

std::fill(&func1, &func4 + 1, (void(*)(void))0);

but that would still leave the issue of padding. You're guaranteed no padding in an array, but not (by the standard) in a class. The ABI used by your implementation might specify struct layout to the degree necessary to ensure that your class above is laid out the same as an array of 4 function pointers.

An alternative is to do the following:

struct function_pointers {
    void (*func1)();
    void (*func2)();
    void (*func3)();
    void (*func4)();
};

class C : private function_pointers
{
public:
    C() : function_pointers() {}
};

The initializer function_pointers() dictates that (since it doesn't have a user-declared constructor) the members of function_pointers are zero-initialized even if the instance of C itself is only default-initialized. function_pointers could be a data member rather than a base class, if you prefer to type a bit more to access func1 etc.

Note that C is now non-POD in C++03. In C++11 C remains standard-layout after this change, but would not be standard-layout if there were any data members defined in C, and it is not a trivial class. So if you were relying on POD/standard/trivial-ness then don't do this. Instead leave the definition of C as it is and use aggregate initialization (C c = {0};) to zero-initialize instances of C.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • If all bits zero does not represent a null function pointer then what else would setting it to 0 do? – ctor Mar 15 '13 at 11:15
  • *If* that's not a valid function pointer then it's UB to access the data member. It's unlikely not to be the representation, but you did ask what's guaranteed. Supposing that it isn't I think the most likely behavior is that it would fail to compare equal to null. Another possibility is that the CPU traps on trying to load the invalid representation into a register. Basically, if what you've written isn't a valid representation then it will behave like an uninitialized object. – Steve Jessop Mar 15 '13 at 11:16
  • @ctor `0` is a null pointer constant. Assigning it to a pointer gives that pointer a null pointer value. The null pointer value does not necessarily have all 0 bits as its representation. – Joseph Mansfield Mar 15 '13 at 11:21
  • @ctor There's a difference between setting a pointer to 0 (e.g. with an assignment or an initialization), and `memset`ing it. When you assign 0 to a pointer, there's a type conversion, which _can_ change bits. (In the past, there were machines where null pointers weren't all 0 bits. I don't know if this is still the case.) – James Kanze Mar 15 '13 at 11:22
  • re "without intervening access specifiers", well that was in C++03. as I recall C++11 gives a wider guarantee – Cheers and hth. - Alf Mar 15 '13 at 11:45
  • re putting somewhat important in between, can happen in derived class – Cheers and hth. - Alf Mar 15 '13 at 11:46
3

Do this first.

class C
{
public:
    C() : func1(nullptr), func2(nullptr), func3(nullptr), func4(nullptr)
    { };
private:
    void (*func1)();
    void (*func2)();
    void (*func3)();
    void (*func4)();
};
Alex Chamberlain
  • 4,147
  • 2
  • 22
  • 49
  • 3
    `: func1(), func2(), func3(), func4()` is shorter – Pubby Mar 15 '13 at 11:15
  • 1
    +1. I'd even add that if using `memset` (or something similar) is the best way to initialise these pointers, any optimiser worth its salt will of course use it. And if it's not - why try to do it by hand in the first place? – Angew is no longer proud of SO Mar 15 '13 at 12:23
1

NEVER do this in C++ unless you really know what you're doing. This is very error-prone and future maintainers of your code (that might even be you!) possibly won't recognize all the pitfalls present and things will likely go horribly wrong. The C++ way of achieving this is to write a constructor that properly initializes the function pointers to nullptr (if you're using C++11) or 0.

Michael Wild
  • 24,977
  • 3
  • 43
  • 43
  • Why? `memset` is perfectly fine in this case, so long as his code is the same as he posted. – Pubby Mar 15 '13 at 11:13
  • 1
    There being the problem. As long as it a plain struct (POD), there's no problem. However, I doubt it will stay this way. Some innocent soul will come along, add some nifty new thing and everything blows up in your face. – Michael Wild Mar 15 '13 at 11:15
  • @Pubby - I think it is a recipe for disaster: looking at the interface definition there is nothing that tells you that internal ordering of members is important (i.e. 'if you add something else between those functions, you may get memory corruptions'). This is the kind of code that makes you have defects like 'It makes no sense but the app crashes only on Win with this and this optimization and only at every fourth execution". It is hacky, obscure and a problem if you (or anyone else ever) have to maintain/change this code. – utnapistim Mar 15 '13 at 11:18
  • @Pubby `memset` is _not_ guaranteed to set a pointer to null. In this case, something like `func1()` is the best solution. – James Kanze Mar 15 '13 at 11:18
  • @JamesKanze, How can `memset( ..., NULL, ... )` possibly not garauntee for a location to be set to NULL? – ctor Mar 15 '13 at 11:21
  • 1
    @ctor: the implementation is free to use any representation it likes for a null pointer. Nowhere in the standard does it say this must be all-bits-zero. There have been historical architectures in which address 0 is used for some special hardware-specific purpose, and null pointers are represented by a different address. – Steve Jessop Mar 15 '13 at 11:24
  • @ctor `memset` ignores type, and any implicit conversions. Formally, it's useless for anything but integral types. I've never heard of an implementation where it wouldn't work for floating point as well (although I don't think it's guaranteed). There definitely have been implementations where it wouldn't work for pointers, however (and cases where `int i = 0; char* p = (char*)i;` gave different results than `char* p = (char*)0;`). – James Kanze Mar 15 '13 at 11:26
  • @MichaelWild You defenitly should make a proposal for next C++ standard to remove POD's, memset and all that useless low-level staff from C++. – Konstantin Oznobihin Mar 15 '13 at 12:51
  • @KonstantinOznobihin That's not the point. The point is that using this kind of construct is very, very error-prone. And unless you're doing low-level development (such as implementing a network-protocol, interacting with the OS kernel etc.), the answer is simple: don't use it. Modern C++ is as far away from C as C is from assembler. Probably even further... – Michael Wild Mar 15 '13 at 12:59
  • @KonstantinOznobihin By the way, the standard mentions `memset` only as being part of the `` header, nothing more. – Michael Wild Mar 15 '13 at 13:04
  • @MichaelWild that still dosn't add any sense for such generalization. If you mean something other, like "...using this kind of construct is very, very error-prone...", why not put it to the answer? Though it wouldn't be more useful either it'd be correct at least. – Konstantin Oznobihin Mar 15 '13 at 13:11
  • 1
    @KonstantinOznobihin POD structs are necessary to interface with C (and any ABI defined in terms of C, like Windows or Unix). Even `memset` can have its uses, if you're implementing very low level stuff. – James Kanze Mar 15 '13 at 14:13
  • @MichaelWild The standard says that `memset` (and most other functions in ``) are defined by the C standard. And the C standard describes the semantics of `memset` very exactly. – James Kanze Mar 15 '13 at 14:15
  • But simply because it's there doesn't mean you should be using it in normal high-level code. As I pointed out, it is very error prone. If you disagree, use `memset`, `malloc`, `memcpy` and `strcpy` as much as you like, but don't call it C++. – Michael Wild Mar 15 '13 at 14:25
  • @Pubby Whilst I accept your point that it's unclear to anyone else maintaining the code, simply bad practice and unsafe for future modification, but as a conceptual thing, as the code stands, everything will work fine. – The Welder Feb 23 '16 at 10:23
1

Never ever use memset on non-trivial objects, particularly on those which are polymorphic (having virtual functions or deriving from base class having virtual methods and so forth). If you do it, you would blow-up the vptr which points to vtable! A disaster!

vptr & vtable are used to implement polymorphic behavior, hence respect those hidden class members.

memset should be used when we talk in terms of bits and bytes, not when we talk about objects.

Respect non-trivial objects, say no to memset :)

Arun
  • 2,087
  • 2
  • 20
  • 33