Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > delete[] p or delete[] *p

Reply
Thread Tools

delete[] p or delete[] *p

 
 
Paul
Guest
Posts: n/a
 
      03-27-2011
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??

 
Reply With Quote
 
 
 
 
Joshua Maurice
Guest
Posts: n/a
 
      03-27-2011
On Mar 26, 10:21*pm, cg_chas <(E-Mail Removed)> wrote:
> On Sun, 27 Mar 2011 01:19:37 -0400, cg_chas <(E-Mail Removed)> wrote:
> >On Sun, 27 Mar 2011 05:34:51 +0100, "Paul" <(E-Mail Removed)> wrote:

>
> >>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?
 
Reply With Quote
 
 
 
 
Öö Tiib
Guest
Posts: n/a
 
      03-27-2011
On Mar 27, 7:34*am, "Paul" <(E-Mail Removed)> wrote:
> 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.
 
Reply With Quote
 
Kai-Uwe Bux
Guest
Posts: n/a
 
      03-27-2011
Joshua Maurice wrote:

> On Mar 26, 10:21 pm, cg_chas <(E-Mail Removed)> wrote:
>> On Sun, 27 Mar 2011 01:19:37 -0400, cg_chas <(E-Mail Removed)> wrote:
>> >On Sun, 27 Mar 2011 05:34:51 +0100, "Paul" <(E-Mail Removed)>
>> >wrote:

>>
>> >>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
 
Reply With Quote
 
Paul
Guest
Posts: n/a
 
      03-27-2011

>"Öö Tiib" <(E-Mail Removed)> wrote in message
>news:(E-Mail Removed)...
>On Mar 27, 7:34 am, "Paul" <(E-Mail Removed)> wrote:
>> 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 (&)[S]>
class R{
public:
R()p((int (*)[S])new int[S+1]), r(*(pp+1)){r[-1]=S;}
U getArray(){return r;}
void delete_it(){ delete[] (int*)pp; }
private:
int (*pp)[S];
T (&r)[S];
};

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.

 
Reply With Quote
 
Vidar Hasfjord
Guest
Posts: n/a
 
      03-27-2011
On Mar 27, 5:34*am, "Paul" <(E-Mail Removed)> wrote:
> [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
 
Reply With Quote
 
Goran
Guest
Posts: n/a
 
      03-28-2011
On Mar 27, 6:34*am, "Paul" <(E-Mail Removed)> wrote:
> 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.
 
Reply With Quote
 
SG
Guest
Posts: n/a
 
      03-28-2011
On 27 Mrz., 20:58, Vidar Hasfjord wrote:
> [...]
> 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
 
Reply With Quote
 
Vidar Hasfjord
Guest
Posts: n/a
 
      03-28-2011
On Mar 28, 12:25 pm, SG <(E-Mail Removed)> wrote:
> 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 <class T> void destruct (T*); // replaces delete

> 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
 
Reply With Quote
 
Michael Doubez
Guest
Posts: n/a
 
      03-28-2011
On 28 mar, 15:29, Vidar Hasfjord <(E-Mail Removed)> wrote:
> On Mar 28, 12:25 pm, SG <(E-Mail Removed)> wrote:
>
> > 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 ?

--
Michael
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off




Advertisments