delete[] p or delete[] *p

P

Paul

Hi,
Suppose I have the following code:

int (*p)[5] = (int (*)[5])new int[5];

How do I delete the allocated memory?
Is it:
a) delete[] p;
b) delete[] *p;
c) delete[] (int*)p;

?? Any ideas??
 
J

Joshua Maurice

Hi,
Suppose I have the following code:
int (*p)[5] = (int (*)[5])new int[5];
How do I delete the allocated memory?
Is it:
a) delete[] p;
b) delete[] *p;
c) delete[] (int*)p;
?? Any ideas??
You have casted the pointer type returned from new into a different pointer, but
regardless, new allocated an array of 5 ints, so you call delete [] on the
pointer that new returned.

my correction: should say  you have casted the pointer type returned from new
into a different pointer "type"

I'd like to point out that AFAIK the first cast may result in a loss
of information. You're only guaranteed round-way trips through void*,
char*, and unsigned char*, (and an integer type of sufficient size).
Thus the delete[] could be UB on a conforming platform. That's right,
right?
 
Ö

Öö Tiib

Hi,
Suppose I have the following code:

int (*p)[5] = (int (*)[5])new int[5];

How do I delete the allocated memory?
Is it:
a) delete[] p;
b) delete[] *p;
c) delete[] (int*)p;

?? Any ideas??

I think that c) but why you reinterpret_cast at first place?

I start to wonder do you ever use raw dynamic arrays and if then why
do you use them? I don't. In C code I do but there are allocated with
malloc() and deallocated with free(). Usage of "delete[]" itself
usually indicates that some novice in team has copy-pasted something
cheesy from google or from a book into our code base.

In C++ it is often clear how a particular container of T's should be
used (as stack or as set or as queue or as vector). Sometimes it is
unclear. I usually take std::vector<T> first when it is unsure how
container will be used. Later it settles and usually it remains as
such for ages after.

Sometimes very rarely it later becomes apparent that a container
causes performance or maintainability issues related to nature or
usage of it. Then i revisit what exact underlying container type is
used. Refactoring vector into some other container may make sense at
that spot.

The chosen type depends on what is done with container and what is not
done with it. There are so lot of somewhat similar to vector
containers to choose. boost::array<T>, boost::scoped_array<T>,
std::map<int,T>, std::list<T>, boost::intrusive::slist<T> and so on.
Basically ... just describe exact nature and usage of container and no
doubt that there is very close template available and free to use.
Vector may become even raw static array, but i see no reasons to use
raw dynamic array in C++.

Raw static arrays i use for static tables. Sometimes initial
requirements described some "list" of information and vector was
chosen and later it did appear that it is actually static table of
information. There is so elegant (IMHO) syntax for initializing static
arrays in C so it is lot better to maintain. Like i understand C++0x
will allow using such syntax for initializing other containers as
well.
 
K

Kai-Uwe Bux

Joshua said:
Hi,
Suppose I have the following code:
int (*p)[5] = (int (*)[5])new int[5];
How do I delete the allocated memory?
Is it:
a) delete[] p;
b) delete[] *p;
c) delete[] (int*)p;
?? Any ideas??
You have casted the pointer type returned from new into a different
pointer, but regardless, new allocated an array of 5 ints, so you call
delete [] on the pointer that new returned.

my correction: should say you have casted the pointer type returned from
new into a different pointer "type"

I'd like to point out that AFAIK the first cast may result in a loss
of information. You're only guaranteed round-way trips through void*,
char*, and unsigned char*, (and an integer type of sufficient size).
Thus the delete[] could be UB on a conforming platform. That's right,
right?

That depends on:

a) which cast is used and
b) what the alignment requirement for the involved types are.

According to [5.2.10/7] you can reinterpret_cast round trip from T1* to T2*
and back provided the alignment of T2 are not stricter than the ones for T1.

To me, the C-style cast within the expression

(int (*)[5]) new int[5]

looks like a reinterpret_cast. Thus, [5.2.10/7] applies. As to whether
int[5] may have stricter alignment requirements than int, I think the
standard remains silent.


Best,

Kai-Uwe Bux
 
P

Paul

Hi,
Suppose I have the following code:

int (*p)[5] = (int (*)[5])new int[5];

How do I delete the allocated memory?
Is it:
a) delete[] p;
b) delete[] *p;
c) delete[] (int*)p;

?? Any ideas??

I think that c) but why you reinterpret_cast at first place?
To convert the dynamic array into an array object type, that would decay
into a pointer that carried size information, for example:

template<typename T, int S, typename U= T (&)>
class R{
public:
R():pp((int (*))new int[S+1]), r(*(pp+1)){r[-1]=S;}
U getArray(){return r;}
void delete_it(){ delete[] (int*)pp; }
private:
int (*pp);
T (&r);
};

template<typename T>
void foo(T r){
std::cout<<"size of array:\t" << r[-1];
std::cout<< "\ntype of array:\t" << typeid(T).name();
}

int main() {
R<int, 7> r;
int (&r1)[7] = r.getArray();
foo(r.getArray());
std::cout<<"\ntype of r1:\t"<< typeid(r1).name();

r.delete_it();
}
I start to wonder do you ever use raw dynamic arrays and if then why
do you use them? I don't. In C code I do but there are allocated with
malloc() and deallocated with free(). Usage of "delete[]" itself
usually indicates that some novice in team has copy-pasted something
cheesy from google or from a book into our code base.

OFC dynamic arrays are very usefull , especially if creating a specialised
container.
In C++ it is often clear how a particular container of T's should be
used (as stack or as set or as queue or as vector). Sometimes it is
unclear. I usually take std::vector<T> first when it is unsure how
container will be used. Later it settles and usually it remains as
such for ages after.

std::vector is not always the best solution for use within specialised class
designs. I don't see std::vector as a replacement for dynamic arrays, its a
good quick and easy solution if you need a basic resizeable vector with safe
some built in safety.
Sometimes very rarely it later becomes apparent that a container
causes performance or maintainability issues related to nature or
usage of it. Then i revisit what exact underlying container type is
used. Refactoring vector into some other container may make sense at
that spot.

I think in C++ we have the power to create a wide variety of specialised
types, data records etc. I don't like to think that there is a certain
limited amount of types, defined by the std lib , that we should use.
The chosen type depends on what is done with container and what is not
done with it. There are so lot of somewhat similar to vector
containers to choose. boost::array<T>, boost::scoped_array<T>,
std::map<int,T>, std::list<T>, boost::intrusive::slist<T> and so on.
Basically ... just describe exact nature and usage of container and no
doubt that there is very close template available and free to use.
Vector may become even raw static array, but i see no reasons to use
raw dynamic array in C++.

I prefer to design my own type for a spefic need, if there is a something
suitable in the std lib then great I will use it. but often they are just
not perfectly suited to the job.

Raw static arrays i use for static tables. Sometimes initial
requirements described some "list" of information and vector was
chosen and later it did appear that it is actually static table of
information. There is so elegant (IMHO) syntax for initializing static
arrays in C so it is lot better to maintain. Like i understand C++0x
will allow using such syntax for initializing other containers as
well.

You are probably basing your views on personal experience as many of us do.
One of the major attractions to C++ , for me , is the ability to easily
create specialised types whereas assembly its a bit more complex, I like to
use this feature of the language often , and I get much satisfaction from
doing so.
As you may have guessed I am no big fan of the std lib, I do like some of
the template features but generally I don't bother with it unless it suits
the job perfectly.
AFAIK boost multi-array, and I don't think this is part of the std lib, is
the closest thing to my needs and its a bit overkill tbh.

Also learning about different ways of allocating memory is usefull for
learning purposes and enhances your programming skills, sometimes I prefer
to invoke the OS's API directly for memory allocation simply to become more
familiar with the API.

HTH
Paul.
 
V

Vidar Hasfjord

[Suppose I have the following code:

int (*p)[5] = (int (*)[5])new int[5];

How do I delete the allocated memory?

As your question has been answered elsewhere, I'll just add an
interesting aside from a language design perspective. If C++ had a
more regular design the type of the "new int [5]" expression would not
degenerate to "int*". A regular design would render the array
overloads of new and delete superfluous, and make your cast here
unnecessary. Consider the irregularity of:

typedef struct {int a, b, c, d, e;} Struct; // aggregate type
typedef int Array [5]; // aggregate type
Struct* ps = new Struct; // Ok!
Array* pa = new Array; // Error! Should be "int* pa".
delete ps; // Ok!
delete pa; // Error! Should be "delete [] pa".

If instead C++ was regular and this was correct code, the type of the
pointer would carry the size information and allow the regular delete
operator to release the correct amount of memory. These irregularities
are avoided by std::array:

typedef std::array <int, 5> Array; // aggregate type
Array* pa = new Array; // Ok!
delete pa; // Ok!

Dereferencing the pointer, (*pa), gives you a reference (lvalue) to
the array as a whole, and an indexing operator is implemented for the
array class itself (not via degeneration into pointer arithmetic).

The new std::array circumvents the numerous irregularities between
pointers and arrays that causes so much confusion. I.e. you can copy
arrays, pass and return arrays by value to and from functions, and the
array will never implicitly convert to a pointer to the first element.
Hence I recommend its use instead of C-style arrays.

Regards,
Vidar Hasfjord
 
G

Goran

Hi,
Suppose I have the following code:

int (*p)[5] = (int (*)[5])new int[5];

How do I delete the allocated memory?
Is it:
a) delete[] p;
b) delete[] *p;
c) delete[] (int*)p;

d) when you allocate memory using new T[], you use delete [], passing
it a T* returned to you by a prior cal to "new T[]". This is how new/
delete are supposed to work. You broke that with your casts, which is
the original sin of this code.

I would guess that all of a, b, and c will work on many
implementations, but I know of no guarantee for that. One can imagine
an implementation with bounds-checking where such code fails badly.

Your "R" example further down makes little sense, perhaps you should
have given it a bit more of thought. (E.g. what's with r[-1] = S, that
requires that T has operator=(int) or something, and S is a compile-
time constant anyhow!?)

Did you mean to create a variant of the old C trick with size,
followed by an array of struct, where size is a run-time value? There
was a +/- heated discussion some time ago about this here, somebody
made one half-decent C++ implementation then. (No conversion to an
array though, that wasn't the discussion's goal).

Goran.
 
S

SG

[...]
unnecessary. Consider the irregularity of:

  typedef struct {int a, b, c, d, e;} Struct; // aggregate type
  typedef int Array [5]; // aggregate type
  Struct* ps = new Struct; // Ok!
  Array* pa = new Array; // Error! Should be "int* pa".
  delete ps; // Ok!
  delete pa; // Error! Should be "delete [] pa".

If instead C++ was regular and this was correct code, the type of the
pointer would carry the size information and allow the regular delete
operator to release the correct amount of memory.

For the size to be part of the pointer _type_ this size must be a
compile-time constant. But new[] does not require a compie-time
constant for the number of elements:

void foo(int x) {
new int[x]; // x is not a compile-time constant
}

So, the current new[] operator can do something that you can't emulate
with "new array<T,N>" properly.

Perhaps an alternate syntax would have been better and more regular:

void foo(int x)
{
int *p = new[x] int; // not real C++ code (unfortunately)
delete[] p;

int (*q)[5] = new int[5]; // not real C++ code (unfortunately)
delete q;
}

Anyhow, I consider new[]/delete[] and raw arrays to be nearly useless.
We have better alternatives and soon get some more: vector<T>,
array<T,N>, unique_ptr<array<T,N>>, for example. So, "fixing" arrays
and new[] w.r.t. regularity wouldn't buy you much. You'd mainly lose
compatibility with old code.

my 2 cents,
SG
 
V

Vidar Hasfjord

For the size to be part of the pointer _type_ this size must be a
compile-time constant. But new[] does not require a compie-time
constant for the number of elements: [...]

Good point. But, if C++ was regular it would require the size to be a
compile-time constant; as it does everywhere else in the language
where an array type is expressed. I.e. the argument to new would
simply be a regular type expression.

The unrelated use of square brackets to specify a run-time argument is
highly irregular. And, more over, with templates in the language it is
unnecessary. Dynamic allocation can be implemented as a library
function. All you need is a low-level memory allocation function
(malloc) and placement-new (or a similar construct to allow you to
construct an object in allocated memory). E.g.

template <class T> T* construct (size_t n = 1); // replaces new
template said:
Anyhow, I consider new[]/delete[] and raw arrays to be nearly useless.
We have better alternatives and soon get some more: vector<T>,
array<T,N>, unique_ptr<array<T,N>>, for example.

Agree. C++ programmers, except those dealing with legacy code or
implementing the standard library, can now avoid these irregularities.

Regards,
Vidar Hasfjord
 
M

Michael Doubez

For the size to be part of the pointer _type_ this size must be a
compile-time constant. But new[] does not require a compie-time
constant for the number of elements: [...]

Good point. But, if C++ was regular it would require the size to be a
compile-time constant; as it does everywhere else in the language
where an array type is expressed. I.e. the argument to new would
simply be a regular type expression.

The unrelated use of square brackets to specify a run-time argument is
highly irregular.

This seems contradictory, the irregularity of array with runtime size
is AFAIS intended for providing a regular type expression syntax to
use with new operator.
And, more over, with templates in the language it is
unnecessary. Dynamic allocation can be implemented as a library
function. All you need is a low-level memory allocation function
(malloc) and placement-new (or a similar construct to allow you to
construct an object in allocated memory). E.g.

  template <class T> T* construct (size_t n = 1); // replaces new
  template <class T> void destruct (T*); // replaces delete

And how do you handle constructor parameters for non-array
initialisation ?
For a workable solution, you would need perfect forwarding which isn't
yet standard.

Moreover, this means that unless you standardise it, every project
would have it's own syntax for allocation class-objects; if you
standardize it, why not make it an operator with a nice syntax.
Anyhow, I consider new[]/delete[] and raw arrays to be nearly useless.
We have better alternatives and soon get some more: vector<T>,
array<T,N>, unique_ptr<array<T,N>>, for example.

Agree. C++ programmers, except those dealing with legacy code or
implementing the standard library, can now avoid these irregularities.

Then, how do you implement a dynamic length array of non-copiable
objects ?
 
Ö

Öö Tiib

Anyhow, I consider new[]/delete[] and raw arrays to be nearly useless..
We have better alternatives and soon get some more: vector<T>,
array<T,N>, unique_ptr<array<T,N>>, for example.
Agree. C++ programmers, except those dealing with legacy code or
implementing the standard library, can now avoid these irregularities.

Then, how do you implement a dynamic length array of non-copiable
objects ?

Neither copyable nor movable? One may consider either
boost::scoped_array<T> or std::vector<unique_ptr<T>> depending on
other constraints.
 
A

A

Suppose I have the following code:
int (*p)[5] = (int (*)[5])new int[5];
How do I delete the allocated memory?

start using std::vector (or some other container) instead and forget about
manual memory management. leave it to compiler. you'll have much less memory
leaks.
since i started using stl i forgot about how stupid all that manual handling
of pointers and arrays used to be.

look how simpler it is:

// init
std::vector<int*> p(5);

// automatically deleted when it loses scope
 
N

Noah Roberts

Hi,
Suppose I have the following code:

int (*p)[5] = (int (*)[5])new int[5];

How do I delete the allocated memory?
Is it:
a) delete[] p;
b) delete[] *p;
c) delete[] (int*)p;

?? Any ideas??

You have casted the pointer type returned from new into a different pointer, but
regardless, new allocated an array of 5 ints, so you call delete [] on the
pointer that new returned.
my correction: should say you have casted the pointer type returned from new
into a different pointer "type"

Both statements are correct. What you actually need to correct is,
"...so you call delete [] on the pointer that new returned," to, "...so
you call delete [] on the same pointer type that new returned." Key
point here is that casting creates new objects.
 
P

Paul

Vidar Hasfjord said:
[Suppose I have the following code:

int (*p)[5] = (int (*)[5])new int[5];

How do I delete the allocated memory?
As your question has been answered elsewhere, I'll just add an
interesting aside from a language design perspective. If C++ had a
more regular design the type of the "new int [5]" expression would not
degenerate to "int*". A regular design would render the array
overloads of new and delete superfluous, and make your cast here
unnecessary. Consider the irregularity of:

I don't think my question has bene answered, what I was asking was the
difference between each delete invocation, it seems that each is identical
but I get warning for delete *p, see code:

class Base{
public:
Base() {std::cout<<"Constructing base\n";}
~Base(){std::cout<<"Destructing base\n";}
};

int main() {
Base (*pA)[4] = (Base (*)[4])new Base[4];
Base* pB = new Base[3];

std::cout<<"\nDeleting pA:\n";
delete[] pA;
//delete[] *pA;
std::cout<<"\nDeleting pB:\n";
delete[] pB;


If you run the above code you see that each invocation of delete[] produces
the desired effect, even if you use *pA instead of pA. This however gives a
warning but I think the array is converted to a pointer. I did my tests on a
windows platform but it may not be the same on other platforms.
This suggests to me that the pointer-type is not rellevant, on my platform
at least, when calling delete[].
typedef struct {int a, b, c, d, e;} Struct; // aggregate type
typedef int Array [5]; // aggregate type
Struct* ps = new Struct; // Ok!
Array* pa = new Array; // Error! Should be "int* pa".
delete ps; // Ok!
delete pa; // Error! Should be "delete [] pa".
If instead C++ was regular and this was correct code, the type of the
pointer would carry the size information and allow the regular delete
operator to release the correct amount of memory. These irregularities
are avoided by std::array:

Yeah but it's never going to be a regular language, that is not the nature
of comp programming. All you can do is wrap it up in some class and make it
apper like regular syntax. I'm no big fan of unnecessary wrapping.
typedef std::array <int, 5> Array; // aggregate type
Array* pa = new Array; // Ok!
delete pa; // Ok!

Yes but look at all the extra code you need to introduce behind the scenes
to get these semantics. I don't see any advantage in using an STL class over
a pointer in many cases, there are some cases where they are perfect and
really good , but not always.
Dereferencing the pointer, (*pa), gives you a reference (lvalue) to
the array as a whole, and an indexing operator is implemented for the
array class itself (not via degeneration into pointer arithmetic).

Thats simple to achieve without the introduction of a STL class, for
example:

int (*pA)[6] = (int (*)[6])new int[6];
int (&rA)[6] = *pA;

The only advantage I see for using STL vectors is that they are expandable.
If you need a quick fix generic expandable array I agree they are the
solution. But for a simple fixed size array I wouldn't bother with an STL
class.
The new std::array circumvents the numerous irregularities between
pointers and arrays that causes so much confusion. I.e. you can copy
arrays, pass and return arrays by value to and from functions, and the
array will never implicitly convert to a pointer to the first element.
Hence I recommend its use instead of C-style arrays.

I've never really used it TBH, my compiler setup doesn't seem to have that
library.
I don't really see the need for it in most cases, I'm quite happy with
normal C++ arrays, I'm just a bit rusty on C++ so I was trying to confirm
the behaviour of delete[] with the given cases.
TY for your post
Paul.
 
V

Vidar Hasfjord

This suggests to me that the pointer-type is not rellevant, on my platform
at least, when calling delete[].

While it may not make a difference in this particular example, in
general it is very much relevant. The correct type is needed for
delete to call the correct destructor. Also note that both "delete"
and "delete[]" can be overloaded globally and per class, in which case
the correct type is needed to select the correct overload.

Regards,
Vidar Hasfjord
 
P

Paul

Kai-Uwe Bux said:
Paul said:
Hi,
Suppose I have the following code:

int (*p)[5] = (int (*)[5])new int[5];

How do I delete the allocated memory?
Is it:
a) delete[] p;
b) delete[] *p;
c) delete[] (int*)p;

?? Any ideas??

I think that c) but why you reinterpret_cast at first place?
To convert the dynamic array into an array object type, that would decay
into a pointer that carried size information, for example:

template<typename T, int S, typename U= T (&)>
class R{
public:
R():pp((int (*))new int[S+1]), r(*(pp+1)){r[-1]=S;}
U getArray(){return r;}
void delete_it(){ delete[] (int*)pp; }
private:
int (*pp);
T (&r);
};

template<typename T>
void foo(T r){
std::cout<<"size of array:\t" << r[-1];
std::cout<< "\ntype of array:\t" << typeid(T).name();
}

int main() {
R<int, 7> r;
int (&r1)[7] = r.getArray();
foo(r.getArray());
std::cout<<"\ntype of r1:\t"<< typeid(r1).name();

r.delete_it();
}

[...]

The above seems to be rather roundabout. Also, the templates indicate a
generality that is not really there: if you try to instantiate R<long,7>
the
code will not compile. Furthermore, I do not understand why you would want
to store size information within the array when it is available through
the
type system: after all, the array size is passed as a template parameter
whence it must be a compile time constant. Finally, the casting could
invoke
undefined behavior as soon as you access array elements (any use other
than
casting back is undefined, I think).

For comparison look at the free functions new_it<> and delete_it<>. To me,
it seems, that they do the same as the above class R<> except that the
size
is not required to be a compile time constant. (Also, now the templates
work
for types T!=int.)

#include <iostream>
#include <ostream>
#include <typeinfo>


template<typename T, int S, typename U= T (&)>
class R{
public:
R():pp((int (*))new int[S+1]), r(*(pp+1)){r[-1]=S;}
U getArray(){return r;}
void delete_it(){ delete[] (int*)pp; }
private:
int (*pp);
T (&r);
};

template < typename IntegerType >
IntegerType * new_it ( int size ) {
IntegerType * ptr = new IntegerType [size+1];
ptr[0] = size;
return ( ptr+1 );
}

template < typename IntegerType >
void delete_it ( IntegerType * ptr ) {
delete ( ptr-1 );
}

template<typename T>
void foo(T r){
std::cout<<"size of array:\t" << r[-1];
std::cout<< "\ntype of array:\t" << typeid(T).name();
}

int main() {
{
R<int, 7> r; // try R<long,7> here.
int (&r1)[7] = r.getArray();
foo(r.getArray());
std::cout<<"\ntype of r1:\t"<< typeid(r1).name();
r.delete_it();
}
{
std::cout << "\n";
int * ptr = new_it<int>(7); // try long * ptr = new_it<long>(7) here;
foo(ptr);
delete_it( ptr );
std::cout << "\n";
}
}

Yes there are lots of ways to do it , the code was just a example..
TY
Paul.
 
P

Paul

Vidar Hasfjord said:
This suggests to me that the pointer-type is not rellevant, on my
platform
at least, when calling delete[].
While it may not make a difference in this particular example, in
general it is very much relevant. The correct type is needed for
delete to call the correct destructor. Also note that both "delete"
and "delete[]" can be overloaded globally and per class, in which case
the correct type is needed to select the correct overload.

I'm not talking about pointer-types , in general. I'm talking about pointer
to arrays and pointer to array-object types as in:
int* or int(*)[size];
Base* or Base (*)[size]
etc etc.

If I call delete[] on int*, it's no different to int (*)[size]? Or is it?
How does the compiler know how many destructors to call?
And what if call delete[] *(int (*)[size])? Is it implicitly converted to
int*?
 
V

Vidar Hasfjord

This seems contradictory, the irregularity of array with runtime size
is AFAIS intended for providing a regular type expression syntax to
use with new operator.

Granted, you can say a similarity of syntax is regularity, but the
semantic irregularity it causes is high, and it was the semantic
irregularities I alluded to here. I view it as undesirable that the
type-id syntax has been reused to specify a run-time parameter.
And how do you handle constructor parameters for non-array
initialisation ?
For a workable solution, you would need perfect forwarding which isn't
yet standard.

Sorry, these were meant to replace just "new[]" and "delete[]",
leaving "new" and "delete" as before, but with regular semantics.
Perfect forwarding would make it feasable to also replace "new" and
"delete", although the language would still need placement-new, or
something similar, to construct objects in allocated memory.
Moreover, this means that unless you standardise it, every project
would have it's own syntax for allocation class-objects; if you
standardize it, why not make it an operator with a nice syntax.

I'm just hypothesising about a regular subset of C++ here, but yes,
you may assume it would have the necessary features in the language
and support in the library. But I don't see how an operator can be
much nicer than template syntax. This is the road we're on, anyway (C+
+0x):

auto pa = make_shared <A> (...); // Create A, return shared_ptr.

Regards,
Vidar Hasfjord
 
V

Vidar Hasfjord

If I call delete[] on int*, it's no different to int (*)[size]? Or is it?

You may have to consult the C++ standard to see if it gives any
guarantees. Despite your casts, "int*" is what "new" returned, so that
seems the only sure thing. Maybe allowing "int (*)[size]" is a
concession from the compiler for having decayed the type in the first
place? :)
How does the compiler know how many destructors to call?
See:
http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.14

delete[] *(int (*)[size])? Is it implicitly converted to int*?

Yes, and Visual C++ 2010 also produces a warning in this case (warning
C4154: deletion of an array expression; conversion to pointer
supplied).

Regards,
Vidar Hasfjord
 
P

Paul

Vidar Hasfjord said:
If I call delete[] on int*, it's no different to int (*)[size]? Or is it?

You may have to consult the C++ standard to see if it gives any
guarantees. Despite your casts, "int*" is what "new" returned, so that
seems the only sure thing. Maybe allowing "int (*)[size]" is a
concession from the compiler for having decayed the type in the first
place? :)
How does the compiler know how many destructors to call?

See:
http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.14

Yes I already knew about these techniques.
What my concern was that with delete [] called on int (*)[size] type
pointer...
C++ would call the destructor for the 1st element, then move
size*sizeof(int), that is sizeof( *(int (*)[size]) ) to look for the second
element. As oppose to moving only sizeof(int) per element, in the case of
delete[] int*
.. Therefore I would be freeing more memory than I allocated, but this
doesn't seen to happen
delete[] *(int (*)[size])? Is it implicitly converted to int*?

Yes, and Visual C++ 2010 also produces a warning in this case (warning
C4154: deletion of an array expression; conversion to pointer
supplied).
Ah nice that confirms that , for VC++ at least.
Ty again for your post.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,905
Latest member
Kristy_Poole

Latest Threads

Top