2

Consider following code:

unsigned char* a = new unsigned char[100];
void* a1 = reinterpret_cast<void*>(a);
operator delete[](a1);

short* b = new short[100];
void* b1 = reinterpret_cast<void*>(b);
operator delete[](b1);

It seems to me to be a valid syntax for scalar types as delete-expression is equivalent to calling destructor and operator delete. Same for a POD type:

struct POD {
    int i;
    float f;
    char c;
};
POD* c = new POD[100];
void* c1 = reinterpret_cast<void*>(c);
operator delete[](c1);

Another thing with scalar and POD types allocated using operator new and deallocated with delete expression:

void* d = operator new[](sizeof(unsigned char) * 100);
unsigned char* d1 = reinterpret_cast<unsigned char*>(d);
delete[] d1;

void* e = operator new[](sizeof(short) * 100);
short* e1 = reinterpret_cast<short*>(e);
delete[] e1;

void* f = operator new[](sizeof(POD) * 100);
POD* f1 = reinterpret_cast<POD*>(f);
delete[] f1;

All these examples seem to be well-formed, but I didn't manage to find if it really is. Can someone confirm it or tell me where I am wrong? Especially I am concerned by the fact that all these types have different alignment and it should be passed to operator new/delete:

alignof(unsigned char) 1
alignof(short) 2
alignof(POD) 4


UPD. Some seem to confuse this question with Is it safe to delete a void pointer?. I am asking about mixing expression new with operator delete and operator new with expression delete. According to standard expression forms are implemented with operator forms. And the question is how valid would be mixing them for scalar and POD types?

Community
  • 1
  • 1
Teivaz
  • 5,462
  • 4
  • 37
  • 75
  • I would be really surprised if they were OK. My (educated?) guess says it's most likely UB. – bolov Dec 12 '16 at 11:27
  • 1
    Possible duplicate of [Is it safe to delete a void pointer?](http://stackoverflow.com/questions/941832/is-it-safe-to-delete-a-void-pointer) – HostileFork says dont trust SE Dec 12 '16 at 11:28
  • @HostileFork this question addresses a totally different case – Teivaz Dec 12 '16 at 11:32
  • which case? As it is formulated now HostileFork is correct. – UmNyobe Dec 12 '16 at 11:34
  • @UmNyobe for starters question does not contain any deleting pointers to void. Then standard explicitly states that delete expression is implemented with delete operator; so the question is about how valid would be explicitly this syntax regarding to standard. The question linked does not contain any answer to my question. – Teivaz Dec 12 '16 at 11:42
  • Um, `unsigned char* a = new unsigned char[100]; void* a1 = reinterpret_cast(a); operator delete[](a1);` is how your question starts, and is pretty much *verbatim* what the other question is about. The standard seems to suggest it does not have to work--and the highest upvoted answer says (without qualifying it) that deleting `void*` is UB. The second half of your question suggests doing something even sketchier; you suggest alignment as one concern. Seeing it from a compiler author's point of view, I don't see the place where I'd be mandated to make this work. – HostileFork says dont trust SE Dec 12 '16 at 11:51
  • @HostileFork [operator delete](http://en.cppreference.com/w/cpp/memory/new/operator_delete) is different from delete expression. And this syntax is absolutely valid. Undefined behavior would be if I've used `delete b1;` which then would be similar to the question you are mentioning. – Teivaz Dec 12 '16 at 11:55
  • @HostileFork: The last statement you quoted is *not* deleting `void *`. It is calling `operator delete[]` - that is *part* of what the `delete` statement does - but not the bit that really needs to know the type. (`operator delete` is just responsible for freeing memory.) – Martin Bonner supports Monica Dec 12 '16 at 11:56
  • 2
    What exactly guarantees that `new int[10]` returns the same pointer as the one it obtains from calling `operator new[](whatever)` internally, and that `whatever == 10*sizeof(int)`? – n. m. could be an AI Dec 12 '16 at 11:57
  • @MartinBonner Right, but what is the difference if you *allocated* it as another type? *If it were well defined to do it with operator delete[], why wouldn't that just be what plain deleting did?* – HostileFork says dont trust SE Dec 12 '16 at 12:00

3 Answers3

3

It's not valid at all. When you allocate an array of objects with a destructor, the run time has to remember how many objects to call the destructor on. The easiest way to do that, is to allocate a bit of extra memory before the array, and store the count there. That means if you allocate 3 one-byte objects with a destructor, operator new is going to be asked for (say) 11 bytes of memory - three of which hold the objects, and 8 hold the count (in a size_t).

operator delete wants the address of that 11 byte block of memory - not the address of the 3 byte block that holds/held the three objects.

Now, I know you are asking about built-in types, which don't have a destructor - the point is, that the run-time library may well choose to allocate the count anyway for simplicity.

2

I think pairing new-expressions and delete-expressions with calls to (even the associated) allocation functions and deallocation functions—like operator delete[]() etc—results in undefined behavior. The former may do some additional housekeeping, while the latter operate on "raw memory", as far as I understand.

This is from ISO/IEC 14882:2014, clause 5.3.5, and it seems to state this explicitly (set in bold by myself):

In the first alternative (delete object), the value of the operand of delete may be a null pointer value, a pointer to a non-array object created by a previous new-expression, or a pointer to a subobject (1.8) representing a base class of such an object (Clause 10). If not, the behavior is undefined. In the second alternative (delete array), the value of the operand of delete may be a null pointer value or a pointer value that resulted from a previous array new-expression.81 If not, the behavior is undefined. [ Note: this means that the syntax of the delete-expression must match the type of the object allocated by new, not the syntax of the new-expression. — end note ]

mrtnhfmnn
  • 349
  • 3
  • 4
0

Some seem to confuse this question with Is it safe to delete a void pointer?.

(^-- The answer to which is "no, but it might appear to work incidentally...in some cases".)

I am asking about mixing expression new with operator delete and operator new with expression delete.

...which if it were legal, would probably make a good answer to the above question, wouldn't it?

"Nope, you can't use ordinary `delete[]`...but you can use `operator delete[]`.
Its type signature takes a `void*` instead of a typed pointer."

But that's not the answer (and if it were it would make the compiler seem kind of stubborn to not just do that for you.) Either way, the problem is that given just a void pointer and no type information, a compiler does not necessarily have the right "tear-down" code for whatever "build-up" it did specific to the type it was given in new.

(And if you don't think it might be interesting to do something special or distinct for basic types, what if you had an address-sanitizer or undefined-behavior-sanitizer type tool which wanted to throw in something special for short that would be measured differently from another type...maybe because you specifically asked to instrument shorts differently?)

Since ordinary delete is implemented in terms of operator delete[], there's no added magic powers...there's less!

Hence the answer is pretty much the same.

Community
  • 1
  • 1