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

Discussion in 'C++' started by haijin.biz@gmail.com, Apr 14, 2007.

  1. Guest

    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;
    }
    , Apr 14, 2007
    #1
    1. Advertising

  2. Bo Persson Guest

    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
    Bo Persson, Apr 14, 2007
    #2
    1. Advertising

  3. Guest


    > :: 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.
    , Apr 14, 2007
    #3
  4. Bo Persson Guest

    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
    Bo Persson, Apr 14, 2007
    #4
  5. James Kanze Guest

    On Apr 14, 8:07 am, "Bo Persson" <> wrote:
    > 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.)

    --
    James Kanze (Gabi Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Apr 14, 2007
    #5
  6. zeppe Guest

    James Kanze wrote:

    >> 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
    zeppe, Apr 14, 2007
    #6
  7. James Kanze Guest

    On Apr 14, 10:26 pm, zeppe <>
    wrote:
    > James Kanze wrote:
    > >> 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,


    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.

    > > 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.


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

    --
    --
    James Kanze (Gabi Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Apr 15, 2007
    #7
  8. Zeppe Guest

    James Kanze wrote:

    >> int b[100];

    >
    >> and

    >
    >> 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
    Zeppe, Apr 16, 2007
    #8
  9. Mike Smith Guest

    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.


    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.

    --
    Mike Smith
    Mike Smith, Apr 16, 2007
    #9
  10. James Kanze Guest

    On Apr 16, 12:19 pm, Zeppe
    <> wrote:
    > 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).

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Apr 17, 2007
    #10
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Andy
    Replies:
    2
    Views:
    1,382
    laniik
    May 3, 2005
  2. Mr. SweatyFinger
    Replies:
    2
    Views:
    1,804
    Smokey Grindel
    Dec 2, 2006
  3. Replies:
    12
    Views:
    500
    Skarmander
    Jun 24, 2006
  4. aling
    Replies:
    8
    Views:
    942
    Jim Langston
    Oct 20, 2005
  5. Iñaki Baz Castillo
    Replies:
    13
    Views:
    495
    Iñaki Baz Castillo
    May 1, 2011
Loading...

Share This Page