Topic: Why are zero-length arrays not allowed?


Author: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1998/11/20
Raw View
Francis Glassborow wrote:
>
> This could be fixed if the vtable included the sizeof the object.

But that would introduce a new problem. The stride of an array is
defined as sizeof the element, and sizeof is defined as a constant
expression. One of those rules would have to be broken, in order to
allow run-time variations in the stride of an array.

--

Ciao,
Paul


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Francis Glassborow <francis@robinton.demon.co.uk>
Date: 1998/11/21
Raw View
In article <3654EC40.B4CDBDD0@ix.netcom.com>, Paul D. DeRocco
<pderocco@ix.netcom.com> writes
>But that would introduce a new problem. The stride of an array is
>defined as sizeof the element, and sizeof is defined as a constant
>expression. One of those rules would have to be broken, in order to
>allow run-time variations in the stride of an array.

true, but currently you cannot even have a homogeneous array of derived,
and it is difficult to see how you might create a non-homogeneous one
(yes you could with initialisers but the compiler would know)  I am not
overly concerned but there are options that make some sense.



Francis Glassborow      Chair of Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: AllanW@my-dejanews.com
Date: 1998/11/18
Raw View
In article <3650CFCB.451B@noSPAM.central.beasys.com>,
  dtribble@technologist.com wrote:
>
> AllanW@my-dejanews.com wrote:
> ...
> > Something similar happens for new, except that "the new code"
> > actually is an operator, in the sense that it can be part of
> > an expression.
>
> So can 'delete'.  This fragment is legal (and not a bad programming
> habit):
>
>     delete p, p = NULL;

I didn't know that. Thank you for showing this to me.

> ...
> >     (1) Determine the size of the elements in the array.
> >            Get this from sizeof(x), or for polymorphic types
> >            get it from x's vtable.
>
> Arrays of polymorphic objects are a no-no.  (Arrays of polymorphic
> pointers are fine, though.)  You can't increment polymorphic
> pointers to their elements, you can't pass them to functions, you
> can't determine their size reliably, and you can't safely delete
> them polymorphically.
>
> Thus conforming implementations are not (and should not be) required
> to support code like this:
>
>     class Base { ... };
>     class Der: public Base { ... };
>
>     Base * p = new Der[N];
>     delete[] p;            // Gack

Probably true, although I don't see any reason why a compiler
couldn't handle it. However, polymorphic types aren't always
used morphically. AFAIK, conforming implementations ARE
required to support code like this:
    Base * b = new Base[N];
    Der  * d = new Der [N];
    // ...
    delete[] b, delete[] d; // See how I'm learning??

...
> if you write Foo::new(), you must write Foo::delete().

This is true, but it was extraneous to the point I was trying to
make. My post was in response to an assertion that there was no
need to store the number of elements in a dynamically-allocated
array because operator new already has the total size of the
block. This idea is so extremely appealing that people usually
have trouble accepting the fact that it simply won't work in
the general case.

--
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: David R Tribble <david.tribble@noSPAM.central.beasys.com>
Date: 1998/11/19
Raw View
(Talking about delete[]...)

David R Tribble dtribble@technologist.com wrote:
>> Arrays of polymorphic objects are a no-no.  (Arrays of polymorphic
>> pointers are fine, though.)  You can't increment polymorphic
>> pointers to their elements, you can't pass them to functions, you
>> can't determine their size reliably, and you can't safely delete
>> them polymorphically.
>>
>> Thus conforming implementations are not (and should not be) required
>> to support code like this:
>>
>>     class Base { ... };
>>     class Der: public Base { ... };
>>
>>     Base * p = new Der[N];
>>     delete[] p;            // Gack

AllanW@my-dejanews.com wrote:
> Probably true, although I don't see any reason why a compiler
> couldn't handle it. However, polymorphic types aren't always
> used morphically. AFAIK, conforming implementations ARE
> required to support code like this:
>     Base * b = new Base[N];
>     Der  * d = new Der [N];
>     // ...
>     delete[] b, delete[] d; // See how I'm learning??

Those deletes are not polymorphic deletes, so they are perfectly
safe.  The 'Gack' example I gave above, however, is a polymorphic
delete; the delete[] expression is given a pointer of type 'Base*'
which actually points to an array of Der objects.  The delete[]
cannot deduce what kind of objects are being deleted (especially
if Der has no vtable); it must assume they are of type Base.  The
results are disasterous.

Polymorphic arrays are a no-no.

-- David R. Tribble, dtribble@technologist.com --



[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: David R Tribble <david.tribble@noSPAM.central.beasys.com>
Date: 1998/11/19
Raw View
(Talking about how to implement 'delete[]'...)

David R Tribble dtribble@technologist.com wrote:
>> ...
>> If you write Foo::new(), you must write Foo::delete().

AllanW@my-dejanews.com wrote:
> This is true, but it was extraneous to the point I was trying to
> make. My post was in response to an assertion that there was no
> need to store the number of elements in a dynamically-allocated
> array because operator new already has the total size of the
> block. This idea is so extremely appealing that people usually
> have trouble accepting the fact that it simply won't work in
> the general case.

But my point was that it *will* work.  Provided that the block
allocated for an array of Type objects is padded with less than
sizeof(Type) bytes, then all the compiler needs in order to deduce
the number of array elements is the total block size divided by
sizeof(Type).  Since the delete[] expression provides an operand of
type 'Type*', it knows sizeof(Type) (which is sizeof(*p)).  So this
scheme will work, always.

Note that this scheme does not specify where or how the block size
is stored; it simply must be available in some way to the delete[]
expression.

As someone else pointed out, the padding constraint means that
arrays of objects with sizeof(Type) less than the space required to
hold the block size (if indeed the size is stored along with the
block), such as arrays of char, must be handled special for the
scheme to work for all array types.  If the stored size is the
requested size (sizeof(Type)*num_of_elems) and the actual block
size is simply rounded up to the nearest word multiple (which adds
padding bytes to the block), and 'delete[]' takes all of this into
account, the scheme will work for arrays of all types.

Bill Wade" <bill.wade@stoner.com> wrote:
>> I don't expect a real compiler to do this.

On the contrary, I do.  Unless I'm mistaken, I think the HP-UX
compiler does it this way (as probably many others do, too).
I certainly requires less memory overhead than storing both the
block size and the element count.

-- David R. Tribble, dtribble@technologist.com --



[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Francis Glassborow <francis@robinton.demon.co.uk>
Date: 1998/11/19
Raw View
In article <36547F38.2C9F@noSPAM.central.beasys.com>, David R Tribble
<david.tribble@noSPAM.central.beasys.com> writes
>The delete[]
>cannot deduce what kind of objects are being deleted (especially
>if Der has no vtable); it must assume they are of type Base.  The
>results are disasterous.

Even if it does the problem is usually that it cannot walk the array
correctly because the derived object is of a different size.
Unfortunately novices often believe it works because the first time they
try it the derived object does not have any extra data.

This could be fixed if the vtable included the sizeof the object.


Francis Glassborow      Chair of Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: AllanW@my-dejanews.com
Date: 1998/11/14
Raw View
> >> Bill Wade wrote:
> >> >> I've written at least one allocator where the hidden size was stored
> >> >> in a per-page table (every allocated block in any particular page has
> >> >> the same hidden size).

> AllanW@my-dejanews.com wrote in message <72as0n$ju6$1@nnrp1.dejanews.com>...
>
> >Think about it for a bit; it's hard to accept. Any implementation
> >of ::new[] must be able to get the allocation size, but since
> >::new[] can be replaced by user code, and since there's no
> >standard name for the allocation-size function, the delete code
> >can't use it. So the allocation size (or the number of elements)
> >MUST be kept somewhere else.

In article <72frhh$41d$1@uuneo.neosoft.com>,
  "Bill Wade" <bill.wade@stoner.com> wrote:
> Obviously at the point of the new[] expression the implementation can tell
> whose memory allocator it is using.

It can tell if class foo has defined foo::operator new[]. But there's
no way to know if global ::operator new[] is from the standard C++
library; it could be replaced in any compilation unit, and not
seen until link time.

> So I think an implementation could use David's trick (compute the element
> count as a function of the allocation size) when using built-in new[] and
> store the element count when using user-defined new[].

At compile time, we can't tell if ::operator new will be replaced.
So this cannot work unless the linker can change "the new code"
and "the delete code" throughout the program. This would be possible
by making calls to functions in the standard library, but this would
be slower than any implementation I am familiar with. In general
this is not practical.

--
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: David R Tribble <david.tribble@noSPAM.central.beasys.com>
Date: 1998/11/17
Raw View
AllanW@my-dejanews.com wrote:
...
> We usually
> describe the "delete" keyword by the name "delete operator".
> (Technically incorrect; this is the "delete statement." But I
> hear it the other way all the time.) And the function that is
> invoked in step 4 is almost always called "operator delete." But
> I hope that you can see that these two things are NOT the same
> thing. One is "the delete code", and the other is "operator
> delete." They are different.

Yes indeed.

...
> Something similar happens for new, except that "the new code"
> actually is an operator, in the sense that it can be part of
> an expression.

So can 'delete'.  This fragment is legal (and not a bad programming
habit):

    delete p, p = NULL;

> But still, "the new code" is distinct from
> "operator new" because it also calls constructors, and possibly
> other tasks related to steps 1 and 2 in the delete code.

'delete' does the exact inverse of 'new' in the reverse order.
(Except for placement-new, which omits the allocation step.)

...
> The user comes by and writes her own versions of ::new[] and
> ::delete[]. The user has exactly the same problems that the
> library implementors had, and often end up solving those problems
> in similar ways. For instance, it is vitally important for most
> custom ::new[] and ::delete[] implementations to be able to
> determine the size of any arbitrary allocation.

It's obvious that no matter how 'new' is written, or whether it's
provided by the user, it must be able to store off the total
block allocation size *somewhere* so that a subsequent 'delete' can
determine the true block size.  Since the two operations are so
tightly coupled, it's always a bad idea to write one without
writing the other.

...
>     (1) Determine the size of the elements in the array.
>            Get this from sizeof(x), or for polymorphic types
>            get it from x's vtable.

Arrays of polymorphic objects are a no-no.  (Arrays of polymorphic
pointers are fine, though.)  You can't increment polymorphic
pointers to their elements, you can't pass them to functions, you
can't determine their size reliably, and you can't safely delete
them polymorphically.

Thus conforming implementations are not (and should not be) required
to support code like this:

    class Base { ... };
    class Der: public Base { ... };

    Base * p = new Der[N];
    delete[] p;            // Gack

...
> Think about it for a bit; it's hard to accept. Any implementation
> of ::new[] must be able to get the allocation size, but since
> ::new[] can be replaced by user code, and since there's no
> standard name for the allocation-size function, the delete code
> can't use it. So the allocation size (or the number of elements)
> MUST be kept somewhere else.

Again, if you write Foo::new(), you must write Foo::delete().
Where these functions keep the size info (or whether they even
bother to) is up to them, just as long as they cooperate in
this regard.  It's ludicrous to require vendors to supply a
'delete' that works with user-defined 'new' functions, and visa
versa; it's perfectly reasonable, however, to require the user
to supply both if he's going to supply one.

-- David R. Tribble, dtribble@technologist.com --


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Bill Wade" <bill.wade@stoner.com>
Date: 1998/11/13
Raw View

AllanW@my-dejanews.com wrote in message <72as0n$ju6$1@nnrp1.dejanews.com>...
>> Bill Wade wrote:
>> >> I've written at least one allocator where the hidden size was stored
>> >> in a per-page table (every allocated block in any particular page has
>> >> the same hidden size).


>Think about it for a bit; it's hard to accept. Any implementation
>of ::new[] must be able to get the allocation size, but since
>::new[] can be replaced by user code, and since there's no
>standard name for the allocation-size function, the delete code
>can't use it. So the allocation size (or the number of elements)
>MUST be kept somewhere else.


In general that is correct.  I was talking about the thing that grabs M
bytes, not the thing that grabs space for N objects with non-trivial
destructors and initializes them.

However the object count must be stored when using a user-defined new().  If
the delete[] expression can tell which kind of new() (built in or user
defined) was used, it might avoid storing the object count when using
built-in new[].

Consider:

  #include "Bar.h"
  void foo(Bar* bar)
  {
    delete[] bar;
  }

Can the implementation tell if a user-defined allocator was used?  It can
certainly tell if global new[] has been replaced.  It can tell if Bar::new[]
(or a base class) has been replaced.  If the array is really an array of
class_derived_from_bar, behavior is undefined (at least in CD2.  I guess I
need to blow $18 to determine if this is still true).

Obviously at the point of the new[] expression the implementation can tell
whose memory allocator it is using.

So I think an implementation could use David's trick (compute the element
count as a function of the allocation size) when using built-in new[] and
store the element count when using user-defined new[].

I don't expect a real compiler to do this.




[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: AllanW@my-dejanews.com
Date: 1998/11/11
Raw View
> Bill Wade wrote:
> >> I've written at least one allocator where the hidden size was stored
> >> in a per-page table (every allocated block in any particular page has
> >> the same hidden size).

> Paul D. DeRocco wrote:
> > Neat idea. However, the count that the new[] operator typically stores
> > in the beginning of the block returned by ::operator new[] is not the
> > size of the block but the number of elements in the array. This is
> > needed to tell the delete[] operator how many times to call the
> > destructor. Since blocks are generally padded to a multiple of some
> > value greater than one, it is not always possible to deduce the latter
> > from the former.

In article <364762CC.3685@noSPAM.central.beasys.com>,
  dtribble@technologist.com wrote:
> Provided that the block has been padded with N bytes, where N is less
> than the size of the array element type, the delete[] expression can
> deduce the exact number of array elements to destruct.  A delete[]
> expression can deduce how many elements are in the array if it knows
> the size of each element (which it does, since it knows the type of
> the object it's deleting) and the total size of the block (rounded
> down to the nearest multiple of the element size).  So Bill's scheme
> could be implemented with near-zero overhead per allocated object.

I used to think the same thing. But it's been "proved" (at least to
my satisfaction) that this scheme just isn't general-purpose enough
to work in the possible presence of user-defined ::new[].

Why not? Take a look at what happens when we execute this statement:
    delete[] x;
The compiler must do the following things, in order:
    (1) Determine the size of the elemenents in the array.
    (2) Determine how many elements are in the array.
    (3) Call the destructor for each element in the array.
    (4) Call ::delete[], passing it the total size of the array.
Number 4 can cause our vocabulary to get messy. We usually
describe the "delete" keyword by the name "delete operator".
(Technically incorrect; this is the "delete statement." But I
hear it the other way all the time.) And the function that is
invoked in step 4 is almost always called "operator delete." But
I hope that you can see that these two things are NOT the same
thing. One is "the delete code", and the other is "operator
delete." They are different.

Something similar happens for new, except that "the new code"
actually is an operator, in the sense that it can be part of
an expression. But still, "the new code" is distinct from
"operator new" because it also calls constructors, and possibly
other tasks related to steps 1 and 2 in the delete code.

That's the confusing aspect, but once you start getting careful
with your terminology the problem goes away. It's step number 2
that's the real problem.

Step 2 seems simple enough -- as you said, to get the total
number of elements all we have to do is get the total allocation
size and divide it by the element size.

But how does it (the delete code) get the total allocation size?
::new[] and ::delete[] MUST be able to deduce the allocation size
from it's address, somehow -- it's essential for proper memory
management. Either we store it someplace in memory, or we deduce
it from which "page" or "pool" it's in -- it doesn't really matter.
So since the ::new[] and ::delete[] code already "knows" the size,
it's a simple matter to provide a third function that returns the
allocation size. Looking up the name of this function in the C++
standard, we find that ... there is no such function in the C++
standard. Not a problem; we just invent our own hook, calling it
... well, how about __alloc_size()? We start with two underscores
so that we won't clash with any user code.

Let's peek at those 4 steps for
    delete[] x;
again:
    (1) Determine the size of the elemenents in the array.
           Get this from sizeof(*x), or for polymorphic types
           get it from x's vtable.
    (2) Determine how many elements are in the array.
           Get this from __alloc_size(x) / sizeof(x[0])
    (3) Call the destructor for each element in the array.
           We know the element size and count, so this is easy
    (4) Call ::delete[], passing it the total size of the array.
           Simplicity itself

That works pretty well, right? So what's the problem?

The user comes by and writes her own versions of ::new[] and
::delete[]. The user has exactly the same problems that the
library implementors had, and often end up solving those problems
in similar ways. For instance, it is vitally important for most
custom ::new[] and ::delete[] implementations to be able to
determine the size of any arbitrary allocation. Once again, why
not make a hook function to return it to user code? Let's call it
A_Size(). Problem solved.

Let's have one last look at those 4 steps for
    delete[] x;
one last time:
    (1) Determine the size of the elemenents in the array.
           Get this from sizeof(x), or for polymorphic types
           get it from x's vtable.
    (2) Determine how many elements are in the array.
           Call __alloc_size(x)
               *** WHICH ABORTS (or returns gibberish) ***
The standard-library version of __alloc_size knows how to find
the size of any memory block which was allocated by the
standard-library version of ::new[]. But it can't possibly
work if the memory was allocated by a user-defined version of
::new[]!

What's the solution? Simple: have the user call it
__alloc_size() instead of A_Size().

But __alloc_size() isn't in the C++ standard. In fact, any
compiler that ever requires the user to define __alloc_size
isn't a conforming C++ compiler.

Think about it for a bit; it's hard to accept. Any implementation
of ::new[] must be able to get the allocation size, but since
::new[] can be replaced by user code, and since there's no
standard name for the allocation-size function, the delete code
can't use it. So the allocation size (or the number of elements)
MUST be kept somewhere else.

That's what is typically kept in the first few bytes of any
allocation, although it could just as easily be kept in a
map<void*,int>.

--
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: David R Tribble <david.tribble@noSPAM.central.beasys.com>
Date: 1998/11/09
Raw View
David R Tribble wrote in message
>> But new'd objects typically contain a hidden
>> size (so that subsequent deletes work correctly), so ::new probably
>> can't realistically return an area aligned on anything less
>> restrictive that a size_t boundary.)

Bill Wade wrote:
>> I've written at least one allocator where the hidden size was stored
>> in a per-page table (every allocated block in any particular page has
>> the same hidden size).

Paul D. DeRocco wrote:
> Neat idea. However, the count that the new[] operator typically stores
> in the beginning of the block returned by ::operator new[] is not the
> size of the block but the number of elements in the array. This is
> needed to tell the delete[] operator how many times to call the
> destructor. Since blocks are generally padded to a multiple of some
> value greater than one, it is not always possible to deduce the latter
> from the former.

Provided that the block has been padded with N bytes, where N is less
than the size of the array element type, the delete[] expression can
deduce the exact number of array elements to destruct.  A delete[]
expression can deduce how many elements are in the array if it knows
the size of each element (which it does, since it knows the type of
the object it's deleting) and the total size of the block (rounded
down to the nearest multiple of the element size).  So Bill's scheme
could be implemented with near-zero overhead per allocated object.

The drawback to this scheme is that it requires many page pools, up
to as many as one for every possible object size in the program.
But it is clever.

-- David R. Tribble, dtribble@technologist.com --



[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1998/11/10
Raw View
David R Tribble wrote:
>
> Provided that the block has been padded with N bytes, where N is less
> than the size of the array element type, the delete[] expression can
> deduce the exact number of array elements to destruct.

True, but there is always the possibility that someone will invent a
one-byte or two-byte class that has a non-trivial destructor, and use a
dynamically allocated array of them in an environment that allocates on
four-byte boundaries. This could of course be treated as a special case,
but I'm not surprised that compiler writers say to hell with it, and
include the necessary count under all circumstances, instead of trying
to get clever.

--

Ciao,
Paul


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Bill Wade" <bill.wade@stoner.com>
Date: 1998/11/03
Raw View
David R Tribble wrote in message

>But new'd objects typically contain a hidden
>size (so that subsequent deletes work correctly), so ::new probably
>can't realistically return an area aligned on anything less
>restrictive that a size_t boundary.)


I've written at least one allocator where the hidden size was stored in a
per-page table (every allocated block in any particular page has the same
hidden size).  Assuming you can do math on pointers (convert pointer to page
number) such a scheme makes allocating single-byte, or even single-bit
blocks (if you have a concept of bit pointers) reasonable.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1998/11/04
Raw View
Bill Wade wrote:
>
> David R Tribble wrote in message
>
> >But new'd objects typically contain a hidden
> >size (so that subsequent deletes work correctly), so ::new probably
> >can't realistically return an area aligned on anything less
> >restrictive that a size_t boundary.)
>
> I've written at least one allocator where the hidden size was stored
> in a per-page table (every allocated block in any particular page has
> the same hidden size).

Neat idea. However, the count that the new[] operator typically stores
in the beginning of the block returned by ::operator new[] is not the
size of the block but the number of elements in the array. This is
needed to tell the delete[] operator how many times to call the
destructor. Since blocks are generally padded to a multiple of some
value greater than one, it is not always possible to deduce the latter
from the former.

The standard has to specify the alignment requirements for ::operator
new[] because it is common for users to substitute their own versions of
that, which must work with the requirements imposed by the new[]
operator, which they can't rewrite. The only portable way to do this is
to specify an interface to ::operator new[] that will work with any
new[] operator.

While it might be possible to invent an implementation of the new[]
operator that doesn't need to store this count in the beginning of the
block, the bulk of the implementations that do exist require this count.
Therefore, I think the standard should say that the result of ::operator
new[] should be aligned sufficiently strongly for a size_t, even if the
data portion of the block is smaller.

--

Ciao,
Paul


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Email_To::.Hans.Olsson@dna.lth.se (Hans Olsson)
Date: 1998/10/29
Raw View
In article <3637C5A8.231E@noSPAM.central.beasys.com>,
David R Tribble  <dtribble@technologist.com> wrote:
>Mike Crowe <mac@nospam.demon.co.uk> wrote:
>> ...
>>> packet_header *make_packet(int element_count)
>>> {
>>>     packet_header *p;
>>>     p = reinterpret_cast<packet_header *>
>>>             (new char[sizeof(packet_header)
>>>              + sizeof(data_chunk) * element_count]);
>>>     p->count = element_count;
>>>     return p;
>>> }

>Hans Olsson wrote:
>> Apart from the other problems with function I would like to recommend
>> that you use malloc (or operator new) instead of new char[]
>> (and of course appropiate deallocation).
>>
>> The reason is that new char[] only guarantees that the data is aligned
>> for characters and the unsigned long and the data_chunk
>> might thus be incorrectly aligned resulting in either a program crash
>> or very bad performance.

>Uh, no.  Operator ::new is guaranteed to return a pointer to an area
>of memory that is suitably aligned for any object type.

If you examine my message you will see that I in the second paragraph
explained why new char[] should not be used. The fact that
the result of new char[] might be incorrectly aligned is made in
notes 12 and 13 of CD2: 5.3.4.

In the first paragraph I instead recommended malloc or
_operator_ new (which as you point out do not have these limitations).

>(::new could conceivably return an area that was aligned on a
>less-restrictive boundary than worst-case alignment if it's passed
>a size less than the width of the worst-case alignment;
>e.g., if ::new always aligns areas on long boundaries but is passed
>sizeof(short), it could choose to allocate that area on a short
>boundary instead.  But new'd objects typically contain a hidden
>size (so that subsequent deletes work correctly), so ::new probably
>can't realistically return an area aligned on anything less
>restrictive that a size_t boundary.)

It could have a special buffer for shorts and use one bit per item
to indicate if they were in use.

Or it might have a special buffer for all objects of size less
than 65536 and use a short to indicate size in this buffer.

>Also, in general it's a bad idea to use malloc() in C++ code,
>because this mixes the two heap schemes (malloc and new).  ::new() is
>semantically equivalent to malloc, with the added benefit that it
>interoperates with object constructors and destructors, so you
>should prefer it in C++ code.

Neither operator new nor malloc call constructors.

Both return suitably aligned memory, but malloc allows realloc
which in some cases might be useful. Furthermore operator new
might be written in terms of malloc.

Since constructors are not called the variable length array is mostly
used for allocating arrays of built-in datatypes and C-like structs (without
virtual functions).

I prefer malloc in these cases because it sets this
code apart from the usual C++ code with new/delete.
--
// Home page  http://www.dna.lth.se/home/Hans_Olsson/
// Email To..Hans.Olsson@dna.lth.se [Please no junk e-mail]




[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: blargg@flash.net (Gargantua Blargg)
Date: 1998/10/30
Raw View
In article <3637C5A8.231E@noSPAM.central.beasys.com>,
dtribble@technologist.com wrote:

...
> Hans Olsson wrote:
> > Apart from the other problems with function I would like to recommend
> > that you use malloc (or operator new) instead of new char[]
> > (and of course appropiate deallocation).
> >
> > The reason is that new char[] only guarantees that the data is aligned
> > for characters and the unsigned long and the data_chunk
> > might thus be incorrectly aligned resulting in either a program crash
> > or very bad performance.
>
> Uh, no.  Operator ::new is guaranteed to return a pointer to an area
> of memory that is suitably aligned for any object type.

Sorry, the ::new operator is the same as the new operator in this regard.
This is exactly what Hans said above. He is correct.

    ::new char [n]

can evaluate to a pointer that has any alignment.

> This is
> due to the simple fact that ::new has no idea what type of object
> is being allocated (it's only passed a size),

::new is an operator, and is not given a size at all. It is given a type, as in

    ::new T

T can be an object or array or whatever.

> so it must assume worst case alignment, suitable for any object.
...

Perhaps you're thinking of the FUNCTION ::operator new() ? Then all of
your comments apply (assuming we change ::new to ::operator new () in your
text)

--
blarggflash.net | Gargantua Blargg | http://www.flash.net/~blargg/
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1998/11/02
Raw View
blargg@flash.net (Gargantua Blargg) writes:

>    ::new char [n]

>can evaluate to a pointer that has any alignment.

Not so. Section 5.3.4 paragraph 10 in the final standard requires
alignment suitable for any object of size n.

--
Steve Clamage, stephen.clamage@sun.com
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: David R Tribble <david.tribble@noSPAM.central.beasys.com>
Date: 1998/10/29
Raw View
Mike Crowe <mac@nospam.demon.co.uk> wrote:
> ...
>> packet_header *make_packet(int element_count)
>> {
>>     packet_header *p;
>>     p = reinterpret_cast<packet_header *>
>>             (new char[sizeof(packet_header)
>>              + sizeof(data_chunk) * element_count]);
>>     p->count = element_count;
>>     return p;
>> }

Hans Olsson wrote:
> Apart from the other problems with function I would like to recommend
> that you use malloc (or operator new) instead of new char[]
> (and of course appropiate deallocation).
>
> The reason is that new char[] only guarantees that the data is aligned
> for characters and the unsigned long and the data_chunk
> might thus be incorrectly aligned resulting in either a program crash
> or very bad performance.

Uh, no.  Operator ::new is guaranteed to return a pointer to an area
of memory that is suitably aligned for any object type.  This is
due to the simple fact that ::new has no idea what type of object
is being allocated (it's only passed a size), so it must assume
worst case alignment, suitable for any object.

(::new could conceivably return an area that was aligned on a
less-restrictive boundary than worst-case alignment if it's passed
a size less than the width of the worst-case alignment;
e.g., if ::new always aligns areas on long boundaries but is passed
sizeof(short), it could choose to allocate that area on a short
boundary instead.  But new'd objects typically contain a hidden
size (so that subsequent deletes work correctly), so ::new probably
can't realistically return an area aligned on anything less
restrictive that a size_t boundary.)

Also, in general it's a bad idea to use malloc() in C++ code,
because this mixes the two heap schemes (malloc and new).  ::new() is
semantically equivalent to malloc, with the added benefit that it
interoperates with object constructors and destructors, so you
should prefer it in C++ code.

-- David R. Tribble, dtribble@technologist.com --
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: dHarrison@worldnet.att.net (Doug Harrison)
Date: 1998/10/24
Raw View
James Kuyper wrote:

>Mike Crowe wrote:
>>However, given the
>> usefulness of such code in C it is interesting that this is not legal
>> C. Gcc/egcs seem to support zero length arrays as an extension but MS
>> Visual C++ 5 does not.
>
>This technique will become legal with C9x, with only one change: replace
>'data[0]' with 'data[]'. The incomplete array is allowed only at the end
>of a struct.

Which Visual C++ has supported for a long time.

--
Doug Harrison
dHarrison@worldnet.att.net
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Email_To::.Hans.Olsson@dna.lth.se (Hans Olsson)
Date: 1998/10/26
Raw View
In article <slrn72sb2q.qe.mac@iunknown.demon.co.uk>,
Mike Crowe <mac@nospam.demon.co.uk> wrote:
...
>struct packet_header
>{
>    unsigned long count;
>    data_chunk data[0];
>};
>
>packet_header *make_packet(int element_count)
>{
>    packet_header *p;
>    p = reinterpret_cast<packet_header *>
>            (new char[sizeof(packet_header)
>             + sizeof(data_chunk) * element_count]);
>    p->count = element_count;
>    return p;
>}
>

Apart from the other problems with function I would like to recommend
that you use malloc (or operator new) instead of new char[]
(and of course appropiate deallocation).

The reason is that new char[] only guarantees that the data is aligned
for characters and the unsigned long and the data_chunk
might thus be incorrectly aligned resulting in either a program crash
or very bad performance.




--
// Home page  http://www.dna.lth.se/home/Hans_Olsson/
// Email To..Hans.Olsson@dna.lth.se [Please no junk e-mail]
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Jim Cobban" <Jim.Cobban.jcobban@nt.com>
Date: 1998/10/26
Raw View
The original question, as shown in the Subject is "Why are zero-length
arrays not allowed [in C++]?".  The only response has been "Because that's
what the standard says."

In response to the question of why the C++ standard says what it says the
response has been "Because the C standard says the same thing."

Isn't that response a little anal retentive?

Yes there are clearly potential problems if a zero dimension array is
permitted, particularly if it is not the last member in a class.  In
particular that two data members can end up occupying the same storage
without an explicit union definition.  But the alternative is writing code
which does not reflect the intent of the objects.  Such code is inevitably
hard to maintain.

IMHO we have a balance between a potential problem and an actual
problem and the wrong decision was made by the standards bodies.

The point has been made that the next C standard will probably include the
ability to specify an array without a dimension as the last member in a
struct.  This is probably seen as an extension of the ability to declare an
array with an unspecified last dimension.  I can see problems with that in
a C++ context because even if the unspecified array is the last data member
in a class it may be followed by methods, other than virtual methods,
without introducing new problems, but this would be difficult for a compiler
to handle.
--
Jim Cobban   |  jcobban@nortel.ca                   |  Phone: (613) 763-8013
Nortel (MCS) |                                      |  FAX:   (613) 763-5199


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: James Kuyper <kuyper@wizard.net>
Date: 1998/10/26
Raw View
Jim Cobban wrote:
>
> The original question, as shown in the Subject is "Why are zero-length
> arrays not allowed [in C++]?".  The only response has been "Because that's
> what the standard says."
>
> In response to the question of why the C++ standard says what it says the
> response has been "Because the C standard says the same thing."
>
> Isn't that response a little anal retentive?

The first response proves that they weren't reading your question right.
However, the second response is reasonable. There was a deliberate
decision made on both sides to avoid unnecessary incompatibilities
between the two languages. That decision was vague enough to leave
plenty of room for argument, which has occured. However, it still
strikes me as a good idea.

...
> The point has been made that the next C standard will probably include the
> ability to specify an array without a dimension as the last member in a
> struct.  This is probably seen as an extension of the ability to declare an
> array with an unspecified last dimension. ...

That's an unspecified FIRST dimension. You're right about how it's seen;
the same terminology is used as in the other cases where that syntax is
permitted: an incomplete array declaration.

> ...  I can see problems with that in
> a C++ context because even if the unspecified array is the last data member
> in a class it may be followed by methods, other than virtual methods,
> without introducing new problems, but this would be difficult for a compiler
> to handle.

The space taken up by methods is a completely different issue from that
taken up by data members. The relative order of the two types of members
is irrelevant.



Author: mac@nospam.demon.co.uk (Mike Crowe)
Date: 1998/10/22
Raw View
In article <361AFACA.7B3A8908@ix.netcom.com>, Paul D. DeRocco wrote:

>The reason objects have to have nonzero size is so that they all have
>different addresses. This need, I would think, might also apply to
>arrays. On the other hand, I could imagine wanting to write something
>like this:
>
> struct foo {
>     unsigned char rep[0];
>     int blat;
>     float zap;
>     ...
> };
>
>and have rep be a "label" on the beginning of the structure that can be
>used to view the rest of the structure as a bunch of bytes. However,
>nowadays that's more correctly done with an anonymous union.

In my experience it is more common to have the zero length array at
the end of the structure. This can be useful representing variable
length data that needs to be handled in a low-level way, such as
written to a file. Ofcourse, you can get round this by using an array
of length one but this makes any sizeof calculations a little
cumbersome.

For example:

struct data_chunk
{
    char name[20];
    int age;
};

struct packet_header
{
    unsigned long count;
    data_chunk data[0];
};

packet_header *make_packet(int element_count)
{
    packet_header *p;
    p = reinterpret_cast<packet_header *>
            (new char[sizeof(packet_header)
             + sizeof(data_chunk) * element_count]);
    p->count = element_count;
    return p;
}

It may be possible to achieve this using templates instead (along with
a suitable specialization for the zero case) but I'm not sure how this
would deal with reading a packet from a file. However, given the
usefulness of such code in C it is interesting that this is not legal
C. Gcc/egcs seem to support zero length arrays as an extension but MS
Visual C++ 5 does not.

--
Mike Crowe <mac at fysh.org>
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1998/10/23
Raw View
Mike Crowe wrote:
>
> In my experience it is more common to have the zero length array at
> the end of the structure. This can be useful representing variable
> length data that needs to be handled in a low-level way, such as
> written to a file. Ofcourse, you can get round this by using an array
> of length one but this makes any sizeof calculations a little
> cumbersome.

In a structure, there's no guarantee that changing the dimension of a
char array at the end will change the sizeof the structure anyway:

 struct foo {
     int x;
     bool y;
     char z[1];
     };

On many machines, sizeof(foo) would be eight, for array sizes 0 to 3,
because of the alignment requirement for ints.

That said, I still do stuff like that, in intrinsically non-portable
situations.

--

Ciao,
Paul
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: James Kuyper <kuyper@wizard.net>
Date: 1998/10/23
Raw View
Mike Crowe wrote:
...
> In my experience it is more common to have the zero length array at
> the end of the structure. This can be useful representing variable
> length data that needs to be handled in a low-level way, such as
> written to a file. Ofcourse, you can get round this by using an array
> of length one but this makes any sizeof calculations a little
> cumbersome.
>
> For example:
>
> struct data_chunk
> {
>     char name[20];
>     int age;
> };
>
> struct packet_header
> {
>     unsigned long count;
>     data_chunk data[0];
> };
>
> packet_header *make_packet(int element_count)
> {
>     packet_header *p;
>     p = reinterpret_cast<packet_header *>
>             (new char[sizeof(packet_header)
>              + sizeof(data_chunk) * element_count]);
>     p->count = element_count;
>     return p;
> }
>
> It may be possible to achieve this using templates instead (along with
> a suitable specialization for the zero case) but I'm not sure how this
> would deal with reading a packet from a file. However, given the
> usefulness of such code in C it is interesting that this is not legal
> C. Gcc/egcs seem to support zero length arrays as an extension but MS
> Visual C++ 5 does not.

This technique will become legal with C9x, with only one change: replace
'data[0]' with 'data[]'. The incomplete array is allowed only at the end
of a struct.

Of course, to compile the make_header() code with C9X you'd have to
replace reinterpret_cast<> with the C-style cast, and replace new with
malloc().
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: blargg@flash.net (Gargantua Blargg)
Date: 1998/10/23
Raw View
In article <slrn72sb2q.qe.mac@iunknown.demon.co.uk>,
mac@nospam.demon.co.uk (Mike Crowe) wrote:

...
> In my experience it is more common to have the zero length array at
> the end of the structure. This can be useful representing variable
> length data that needs to be handled in a low-level way, such as
> written to a file. Of course, you can get round this by using an array
> of length one but this makes any sizeof calculations a little
> cumbersome.
>
> For example:
>
> struct data_chunk
> {
>     char name[20];
>     int age;
> };
>
> struct packet_header
> {
>     unsigned long count;
>     data_chunk data[0];
> };
>
> packet_header *make_packet(int element_count)
> {
>     packet_header *p;
>     p = reinterpret_cast<packet_header *>
>             (new char[sizeof(packet_header)
>              + sizeof(data_chunk) * element_count]);
>     p->count = element_count;
>     return p;
> }

I see this so often. Why not use offsetof()?

    struct packet_header
    {
        unsigned long count;
        data_chunk data [1]; // or 99999 or some large value; doesn't matter
    };

...

    packet_header *make_packet(int element_count)
    {
        packet_header *p;
        p = static_cast<packet_header *> operator new (
                offsetof (packet_header,data [element_count]) );
        p->count = element_count;
        return p;
    }

It is much less error-prone (you don't have to repeat the type of the
array, eliminating mismatch errors), and easier to use. It must be pointed
out that offsetof () is only defined for PODs, and of course that trying
to get the offset of or access a non-existent element of the array is also
not defined, though all this stuff is generally portable.

I also made a correction to your code regarding using new char [] to
allocate space for something with a stricter alignment requirement than
char. operator new () has to align for the strictest type, so it is the
best to use in this case. new char [] has no alignment requirements (the
address could be odd, or anything).

...
> However, given the
> usefulness of such code in C it is interesting that this is not legal
> C. Gcc/egcs seem to support zero length arrays as an extension but MS
> Visual C++ 5 does not.

Nor C++ (zero-length array). This extension isn't necessary if offsetof ()
is used.

--
blarggflash.net | Gargantua Blargg | http://www.flash.net/~blargg/


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: James Kuyper <kuyper@wizard.net>
Date: 1998/10/05
Raw View
=?UNKNOWN-8BIT?Q?Marc-Andr=E9?= lafortune wrote:

> I was surprised to learn that zero-length arrays are not allowed. I looked in
> D&E but couldn't find any discussion about it. I guess it makes sense in C to
> forbid it (since it's pretty unclear why would someone want it anyways), but
> with C++ templates having numerical arguments, it may appear in pretty natural
> contexts... I wanted to do something like:

> template<int _nb_exception, int _nb_symmetry>
> struct Behaviour {
>         enum { NbException = _nb_exception, NbSymmetry = _nb_symmetry };

>         Exception m_Exception[_nb_exception];
>         Symmetry m_Symmetry[_nb_symmetry];
>
> //...
> };

> but was stuck when I arrived at a 'behaviour' with no symmetry. I know I could
> use vectors, but in this case, all those classes will be completely static,
> initialized once using the very convenient { a, b, c...} initializer list,
> etc... I guess I'll make two separate templates and join them together, but to
> me it would be more natural to talk about class/template with 0 symmetry than
> without the concept of symmetry. This probably isn't clear, but the question
> should: why disallow zero-length arrays?

Without answering your fundamental question, I'd like to suggest that
you partially specialize your template for the 0 cases. You'll find that
a lot of special-case code will be simpler for those cases anyway. If
you find any if(_nb_symmetry==0) statements in your code, that's a
strong hint that you should specialize.


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: brownsta@concentric.net (Stan Brown)
Date: 1998/10/05
Raw View
=?UNKNOWN-8BIT?Q?"Marc-Andr=E9?= lafortune" <lafortne@math.princeton.edu>
(=?UNKNOWN-8BIT?Q?"Marc-Andr=E9?= lafortune"
<lafortne@math.princeton.edu>) wrote:
>I was surprised to learn that zero-length arrays are not allowed.

With an array, say int a[15], a[0] is the address of the first element.
You are guaranteed to be able to create a pointer (int *p in this case),
and use it to traverse the array, and you are guaranteed that p = &a[0],
p+1 = &a[1], through p+14 = &a[14]. You are also guaranteed that a
pointer value one element beyond the end of the array is meaningful and
can be used for comparisons (though not dereferenced, of course).

How would that behavior work with a zero-length array? Since there is not
even an element a[0], how could you create p as above? For that matter,
what would be the value of a (which is normally the same as &a[0])?

"I can see all sorts of unforeseen problems."
"Such as?"
"If I could foresee them, they wouldn't be unforeseen!"
 -- Yes, Prime Minister

--
Stan Brown, Oak Road Systems, Cleveland, Ohio, USA
                      http://www.concentric.net/%7eBrownsta/
My reply address is correct as is. The courtesy of providing a correct
reply address is more important to me than time spent deleting spam.


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1998/10/06
Raw View
Stan Brown wrote:
>
> With an array, say int a[15], a[0] is the address of the first
> element. You are guaranteed to be able to create a pointer (int *p in
> this case), and use it to traverse the array, and you are guaranteed
> that p = &a[0], p+1 = &a[1], through p+14 = &a[14]. You are also
> guaranteed that a pointer value one element beyond the end of the
> array is meaningful and can be used for comparisons (though not
> dereferenced, of course).
>
> How would that behavior work with a zero-length array? Since there is
> not even an element a[0], how could you create p as above? For that
> matter, what would be the value of a (which is normally the same as
> &a[0])?

On the contrary, the fact that pointers to one-past-the-end are
precisely what would in theory allow a zero-length array to work.
Specifically, the way you create a pointer to the beginning (and end) of
a zero-length array, without violating the standard, isn't with
"p=&a[0];" but with "p=a;".

--

Ciao,
Paul
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1998/10/06
Raw View
The subject says it all...

There is a requirement in C++ that every object have a nonzero size.
However, there is also a requirement that the sizeof an array of n
elements be n times the sizeof an element. These two conflict if the
array has zero elements. I think the primary reason for prohibiting zero
sized objects is to guarantee that every object be at a different
address. So a zero-length array would still have to take up at least a
byte, even if sizeof reports zero. Empty structs or classes also waste
at least a byte, but sizeof an empty struct or class is always one, not
zero. Thus, allowing empty arrays would introduce an exception wherein
something takes up more space than is suggested by its sizeof (plus any
padding needed by whatever follows it).

Do I make myself perfectly vague?

Anyway, if you're getting into trouble with zero template parameters,
one approach is simply to make the arrays n+1 elements long and waste an
element. Obviously, that's not always possible, but sometimes it's a
perfectly good hack.

--

Ciao,
Paul


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: sbnaran@localhost.localdomain.COM (Siemel Naran)
Date: 1998/10/06
Raw View
On 06 Oct 98 06:07:30 GMT, Paul D. DeRocco <pderocco@ix.netcom.com> wrote:

>On the contrary, the fact that pointers to one-past-the-end are
>precisely what would in theory allow a zero-length array to work.
>Specifically, the way you create a pointer to the beginning (and end) of
>a zero-length array, without violating the standard, isn't with
>"p=&a[0];" but with "p=a;".

Well, here's another reason.  In C++, all objects must have size 1 or
more.  This is so that you can make an array of such objects, or make
pointers to such objects.  So empty classes must have size 1 or more.
Fixed size arrays are also objects, so they must have size 1 or more
too.  But the size of a fixed size array is N*sizeof(T).  So we have
two choices: either the size of a fixed size array is
max(N*sizeof(T),1) or we require N>=1.  Requiring N>=1 makes more
sense.  But note that a dynamic array can have size==0.  This is
because of the subtle difference between an array and pointer.

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Biju Thomas <bijuthom@ibm.net>
Date: 1998/10/07
Raw View
Siemel Naran wrote:
>
> Well, here's another reason.  In C++, all objects must have size 1 or
> more.  This is so that you can make an array of such objects, or make
> pointers to such objects.  So empty classes must have size 1 or more.
> Fixed size arrays are also objects, so they must have size 1 or more
> too.

Arrays are not objects. (At least that's what I understood about C++ all
this time. Correct me if I am wrong.) So, the rule that objects should
have non-zero size doesn't apply to arrays.

Regards,
Biju Thomas
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: markw65@my-dejanews.com
Date: 1998/10/07
Raw View
In article <3616539B.B54F5DC3@math.princeton.edu>,
  lafortne@math.princeton.edu wrote:

> I was surprised to learn that zero-length arrays are not allowed. I looked in
> D&E but couldn't find any discussion about it. I guess it makes sense in C to
> forbid it (since it's pretty unclear why would someone want it anyways), but
> with C++ templates having numerical arguments, it may appear in pretty natural
> contexts... I wanted to do something like:

> template<int _nb_exception, int _nb_symmetry>
> struct Behaviour {
>  enum { NbException = _nb_exception, NbSymmetry = _nb_symmetry };

>  Exception m_Exception[_nb_exception];
>  Symmetry m_Symmetry[_nb_symmetry];

> //...
> };

> but was stuck when I arrived at a 'behaviour' with no symmetry. I know I could
> use vectors, but in this case, all those classes will be completely static,
> initialized once using the very convenient { a, b, c...} initializer list,
> etc...

So either specialize on _nb_symmetry = 0, or waste a bit of storage in the
zero case by writing:

  Symmetry m_Symmetry[_nb_symmetry + !_nb_symmetry];

Mark Williams

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1998/10/07
Raw View
Biju Thomas wrote:
>
> Arrays are not objects. (At least that's what I understood about C++
> all this time. Correct me if I am wrong.) So, the rule that objects
> should have non-zero size doesn't apply to arrays.

The reason objects have to have nonzero size is so that they all have
different addresses. This need, I would think, might also apply to
arrays. On the other hand, I could imagine wanting to write something
like this:

 struct foo {
     unsigned char rep[0];
     int blat;
     float zap;
     ...
 };

and have rep be a "label" on the beginning of the structure that can be
used to view the rest of the structure as a bunch of bytes. However,
nowadays that's more correctly done with an anonymous union.

--

Ciao,
Paul


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: James Kuyper <kuyper@wizard.net>
Date: 1998/10/08
Raw View
Biju Thomas wrote:
...
> Arrays are not objects. (At least that's what I understood about C++ all
> this time. Correct me if I am wrong.) So, the rule that objects should
> have non-zero size doesn't apply to arrays.

An object is defined as "a region of storage". Objects can contains
sub-objects, and one of the listed types of sub-objects is "an array
element", which implies to me that the parent object was an array. See
section 1.8 "The C++ object model".


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: =?UNKNOWN-8BIT?Q?"Marc-Andr=E9?= lafortune" <lafortne@math.princeton.edu>
Date: 1998/10/05
Raw View
Hi.
I was surprised to learn that zero-length arrays are not allowed. I looked in
D&E but couldn't find any discussion about it. I guess it makes sense in C to
forbid it (since it's pretty unclear why would someone want it anyways), but
with C++ templates having numerical arguments, it may appear in pretty natural
contexts... I wanted to do something like:

template<int _nb_exception, int _nb_symmetry>
struct Behaviour {
 enum { NbException = _nb_exception, NbSymmetry = _nb_symmetry };

 Exception m_Exception[_nb_exception];
 Symmetry m_Symmetry[_nb_symmetry];

//...
};

but was stuck when I arrived at a 'behaviour' with no symmetry. I know I could
use vectors, but in this case, all those classes will be completely static,
initialized once using the very convenient { a, b, c...} initializer list,
etc... I guess I'll make two separate templates and join them together, but to
me it would be more natural to talk about class/template with 0 symmetry than
without the concept of symmetry. This probably isn't clear, but the question
should: why disallow zero-length arrays?

Thanks,

- Marc
lafortne@princeton.edu
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]