why is int a[0] not allowed, but int* a = new int[0] is?

H

haijin.biz

I tried the following code and found that the form

#include <iostream>
using namespace std;

int main(int argc, char** argv)
{
//int a[0]; // error C2466: cannot allocate an array of constant size
0
int* b = new int[0];

b[0]=123; // why we can play with b[0] and b[1] as if we had
allocated space for 2 ints?
b[1]=456;
/// b[2]=789; // error

cout<<b[0]<<endl;
cout<<b[1]<<endl;

delete [] b;

return 0;
}
 
B

Bo Persson

(e-mail address removed) wrote:
:: I tried the following code and found that the form
::
:: #include <iostream>
:: using namespace std;
::
:: int main(int argc, char** argv)
:: {
:: //int a[0]; // error C2466: cannot allocate an array of constant size
:: 0

Zero size objects are not allowed. This is from the C part of the language.

:: int* b = new int[0];

This is from the C++ only part of the language. It don't know why, but it
was decided that this should work.

We know that it was intentional, because the standard says that if you do

int* b = new int[0];
int* c = new int[0];

the pointers b and c have different values.

::
:: b[0]=123; // why we can play with b[0] and b[1] as if we had
:: allocated space for 2 ints?
:: b[1]=456;
:: /// b[2]=789; // error

They are all errors!

You are not allowed to "play" with anything outside the size of the object.
None in this case.

::
:: cout<<b[0]<<endl;
:: cout<<b[1]<<endl;

Can't do that either, there are no elements in b.

::
:: delete [] b;
::
:: return 0;
:: }


Bo Persson
 
H

haijin.biz

:: b[0]=123; // why we can play with b[0] and b[1] as if we had
:: allocated space for 2 ints?
:: b[1]=456;
:: /// b[2]=789; // error

They are all errors!

You are not allowed to "play" with anything outside the size of the object.
None in this case.

::
:: cout<<b[0]<<endl;
:: cout<<b[1]<<endl;

Can't do that either, there are no elements in b.
Bo Persson


Thanks Bo. I understand that it may be the problem of a particular c++
complier for allowing

b[0] = 1;
b[1] = 2;

when all we did before that is int* b = new int[0].

I am using VS 2005 and two assignments are okay, although I think it
must crash the program.
 
B

Bo Persson

(e-mail address removed) wrote:
::::: b[0]=123; // why we can play with b[0] and b[1] as if we had
::::: allocated space for 2 ints?
::::: b[1]=456;
::::: /// b[2]=789; // error
:::
::: They are all errors!
:::
::: You are not allowed to "play" with anything outside the size of the
::: object. None in this case.
:::
:::::
::::: cout<<b[0]<<endl;
::::: cout<<b[1]<<endl;
:::
::: Can't do that either, there are no elements in b.
::
::: Bo Persson
::
::
:: Thanks Bo. I understand that it may be the problem of a particular
:: c++ complier for allowing
::
:: b[0] = 1;
:: b[1] = 2;
::
:: when all we did before that is int* b = new int[0].
::
:: I am using VS 2005 and two assignments are okay, although I think it
:: must crash the program.

It doesn't have to. Assigning outside the size of an array is "undefined
behaviour" in C++ speak. That means that it might crash, or seems to work,
or something else. The language standard doesn't say what must happen, just
that you shouldn't do it.


Bo Persson
 
J

James Kanze

(e-mail address removed) wrote:
:: I tried the following code and found that the form
:: #include <iostream>
:: using namespace std;
:: int main(int argc, char** argv)
:: {
:: //int a[0]; // error C2466: cannot allocate an array of constant size
:: 0
Zero size objects are not allowed. This is from the C part of
the language.

It's not just a question of size. The standard explicitly says
that using 0 here is forbidden.
:: int* b = new int[0];
This is from the C++ only part of the language. It don't know
why, but it was decided that this should work.

Because it's sometimes useful. Not "new int[0]", of course, but
"new int[n]", where n may sometimes take the value of 0. It's
not allowed for arrays not allocated dynamically, because the
dimension there must be a constant, and it's not really useful
to have an array whose size it *always* 0.

Note that this corresponds exactly to the situation in C, where
you can malloc 0 bytes.
We know that it was intentional, because the standard says that if you do
int* b = new int[0];
int* c = new int[0];
the pointers b and c have different values.

They're different arrays (objects), and so must have different
addresses.
:: b[0]=123; // why we can play with b[0] and b[1] as if we had
:: allocated space for 2 ints?
:: b[1]=456;
:: /// b[2]=789; // error
They are all errors!
You are not allowed to "play" with anything outside the size
of the object. None in this case.

You're not even allowed to create a pointer to more than one
past the end---the expression b+2 is illegal.

Of course, it's undefined behavior, so it might seem to work.
(In my experience, undefined behavior usually works perfectly in
all my tests, and then fails in the most visually possible
manner in the demo in front of the important customer.)
 
Z

zeppe

James said:
This is from the C++ only part of the language. It don't know
why, but it was decided that this should work.

Because it's sometimes useful. Not "new int[0]", of course, but
"new int[n]", where n may sometimes take the value of 0. It's
not allowed for arrays not allocated dynamically, because the
dimension there must be a constant, and it's not really useful
to have an array whose size it *always* 0.

Yes, and particularly it has to be highlighted the difference in meaning
between

int b[100];

and

int* b = new int[100];

the first is a statically allocated object into the stack, and his type
is "int [100]", so of course a type of "int [0]" would not have any
meaning. In the second case, on the other hand, we are just allocating
some memory through the operator new for the double. The syntax is
similar, but the behaviour is really different, being 100 the argument
of a function, and being evaluated at run time. Basically, there is no
way to check the size at compilation time in general :)
Note that this corresponds exactly to the situation in C, where
you can malloc 0 bytes.

Because there is no way to avoid it. In the C standard, however, the
result is implementation defined.


Regards,

Zeppe
 
J

James Kanze

James said:
This is from the C++ only part of the language. It don't know
why, but it was decided that this should work.
Because it's sometimes useful. Not "new int[0]", of course, but
"new int[n]", where n may sometimes take the value of 0. It's
not allowed for arrays not allocated dynamically, because the
dimension there must be a constant, and it's not really useful
to have an array whose size it *always* 0.
Yes, and particularly it has to be highlighted the difference in meaning
between
int b[100];

int* b = new int[100];
the first is a statically allocated object into the stack,

If it's on the stack, it's not statically allocated. The
important difference in this case is that the first requires a
compile time constant; if it is 0 once, it will be zero every
time, and that doesn't make sense. In the second, the dimension
can be a variable, the result of an expression, and it's useful
in such cases to not have to treat 0 as a special case.
and his type is "int [100]", so of course a type of "int [0]"
would not have any meaning. In the second case, on the other
hand, we are just allocating some memory through the operator
new for the double.

And the type of the memory we allocate is int[0].
The syntax is
similar, but the behaviour is really different, being 100 the argument
of a function, and being evaluated at run time. Basically, there is no
way to check the size at compilation time in general :)

No. And because the type is unsigned, there's no way to check
for accidentally negative values, ever.
Because there is no way to avoid it. In the C standard, however, the
result is implementation defined.

Yes, and the C++ standard corrected this bug.

--
 
Z

Zeppe

James said:
int b[100];

int* b = new int[100];
the first is a statically allocated object into the stack,

If it's on the stack, it's not statically allocated.

Ops! I wanted to say "allocated in the static memory" :)
The
important difference in this case is that the first requires a
compile time constant; if it is 0 once, it will be zero every
time, and that doesn't make sense.

that's because, being allocated into the stack, the type is resolved at
compile time.
In the second, the dimension
can be a variable, the result of an expression, and it's useful
in such cases to not have to treat 0 as a special case.

Which is not possible at compile time, anyway.
and his type is "int [100]", so of course a type of "int [0]"
would not have any meaning. In the second case, on the other
hand, we are just allocating some memory through the operator
new for the double.

And the type of the memory we allocate is int[0].

Well, that is not so clear according to me. I mean, probably the most
rigorous way to see the problem is that one, but you can't access to
that type if it is in the dynamic memory, so basically the real type is
hidden. You will have just a pointer. Additionally, we don't have any
dynamic type for anything but virtual classes, so there is no int [n]
type if n is not constant. Of course IMHO.

Regards,

Zeppe
 
M

Mike Smith

:: b[0]=123; // why we can play with b[0] and b[1] as if we had
:: allocated space for 2 ints?
:: b[1]=456;
:: /// b[2]=789; // error

They are all errors!

You are not allowed to "play" with anything outside the size of the object.
None in this case.

::
:: cout<<b[0]<<endl;
:: cout<<b[1]<<endl;

Can't do that either, there are no elements in b.
Bo Persson


Thanks Bo. I understand that it may be the problem of a particular c++
complier for allowing

b[0] = 1;
b[1] = 2;

when all we did before that is int* b = new int[0].

I am using VS 2005 and two assignments are okay, although I think it
must crash the program.

Just because the program does not happen to crash, doesn't mean you're
not doing something illegal. b[0] and b[1] and both out-of-range
references in your program; it's just dumb luck that your program
doesn't crash.
 
J

James Kanze

James Kanze wrote:

[...]
and his type is "int [100]", so of course a type of "int [0]"
would not have any meaning. In the second case, on the other
hand, we are just allocating some memory through the operator
new for the double.
And the type of the memory we allocate is int[0].
Well, that is not so clear according to me. I mean, probably the most
rigorous way to see the problem is that one, but you can't access to
that type if it is in the dynamic memory, so basically the real type is
hidden.

The fact that you cannot access it is linked to this type. If
the type were int[1], then p[0] would be legal. Since the type
is int[0], it's not.

Of course, the result of the new expression is type int*, so
some vital type information has been lost.
You will have just a pointer.

The new expression returns a pointer. The memory which it
allocates has a type, however, which is not the type of the
pointer. (This is how new differs from malloc, for example.)
Additionally, we don't have any
dynamic type for anything but virtual classes, so there is no int [n]
type if n is not constant. Of course IMHO.

That's an interesting observation. The problem is that "dynamic
type" can mean different things. In this case, if I do
something like:

int* p = new int[ i ] ;

each invocation will construct an array with a specific size; an
array with a specific size has a type which includes that size.
Similarly, every object has a type, even if you cannot always
access the information. The case where i is 0 is interesting,
because arguably, you don't have an object.

I still find it cleaner to think of it as having a specific
type. In the general case, it is the type of the actual array
which determines the legal bounds, and I don't see why int[0]
should be an exception: the legal bounds for a T[N] are [0,N)
(and if N is 0, of course, [0,N) is empty).
 

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,780
Messages
2,569,608
Members
45,241
Latest member
Lisa1997

Latest Threads

Top