Zero-size array as struct member

M

Michael Angelo Ravera

Hi, I need your help.

----------
struct SvrList{
    unsigned int uNum;
    GameSvr  svr[0];            //line A};

---------

Once I declared a struct like this to store server list info.
It's supposed to be used like this.

----------
SvrList* pList = (SvrList*)malloc(sizeof(
SvrList) + svrNum*sizeof(GameSvr));
pList->uNum, pList->svr[0], pList->svr[1].... blabla..
---------

The vs2005 compiler gives me the "nonstandard extension used : zero-
sized array in struct/union" warning though.

I may keep my eye closed to the warning since everything looks fine.
But I don't know whether anything bad may happen to me some day due to
running environment changes(porting to different platforms, or any
other conditions).

One work-around way is to declare a one-sized array as struct member,
but I didn't see any substantial changes except no warning in this
case.

Any body give some suggestions?

It is a useful nonstandard feature that gives you a warning. If you
allocate your struct big enough to hold your data, nothing bad will
happen. The extension is non-standard. If you use a pointer as has
been suggested elsewhere, you have to do two allocations.

On the other hand, this is very useful technique to point into a an
already allocated variable array of the GameSvr structs with the
number of them preceding.
 
A

Andrey Tarasevich

Pete said:
Hi, I need your help.
----------
struct SvrList{
unsigned int uNum;
GameSvr svr[0]; //line A
};
---------
Once I declared a struct like this to store server list info.
It's supposed to be used like this.
----------
SvrList* pList = (SvrList*)malloc(sizeof(
SvrList) + svrNum*sizeof(GameSvr));
pList->uNum, pList->svr[0], pList->svr[1].... blabla..
I wouldn't call this fine. Even
pList->svr[0]
is accessing the element that is out of array's bounds, and
that is UB. How come your program is not crashing, or at
least going crazy? Maybe you are just unlucky to have a bug
hidden.
It's an old C programmers hack. I've come across this idiom in
lot's old C code, particularly driver and os code. Microsoft
Win32 is rife with it.
Except that it's not legal C, either.

Which is why it's referred to above as a "hack". Quite a common one, too.

Usually we use the term "hack" when the code relies on a specific
manifestation of undefined (or unspecified) behavior, but otherwise is
well-formed.

In this case the code is ill-formed, since 0-size array declaration is
illegal in C++ (as well as in C). In other words, arguing about the "out
of bounds" access here doesn't make much sense, since the code is
formally non-compilable.

Declaring the array with size 1 formally turns that code into a "hack"
by making it well-formed.

Of course, if one uses the `offsetof` method for calculating the memory
block size

SvrList* pList = (SvrList*) malloc(offsetof(SvrList, svr) +
svrNum * sizeof(GameSvr));

one can declare the array with arbitrarily large size, thus potentially
completely eliminating the chances of "out of bounds" access.

I wonder if it still a "hack" in that latter case. I.e. is the behavior
defined if we allocate less raw memory than `sizeof` of the object for
an object with a trivial constructor/destructor (assuming we never
access the parts of the object that fall beyond the boundary of the
allocated memory block)?
 
J

Juha Nieminen

Vladimir Jovic said:
If the array's size is zero, how can you access even one element?

Simple: By indexing.
The example similar to the original example

"Similar" is not the same as "identical". You have a radical difference:
You are not allocating any memory for the array.

Perhaps you missed the point that in the original example memory is being
"overallocated" with malloc() and the array indexing is done inside that
allocated memory.

If you have a memory allocated with malloc(), you can do whatever you
want within that memory block. That's like the very definition of memory
allocation.
 
K

Keith H Duggar

Vladimir said:
thomas wrote:
Hi, I need your help.
----------
struct SvrList{
    unsigned int uNum;
    GameSvr  svr[0];            //line A
};
---------
Once I declared a struct like this to store server list info.
It's supposed to be used like this.
----------
SvrList* pList = (SvrList*)malloc(sizeof(
SvrList) + svrNum*sizeof(GameSvr));
pList->uNum, pList->svr[0], pList->svr[1].... blabla..
I wouldn't call this fine. Even
pList->svr[0]
is accessing the element that is out of array's bounds, and
that is UB.  How come your program is not crashing, or at
least going crazy? Maybe you are just unlucky to have a bug
hidden.
It's an old C programmers hack. I've come across this idiom in
lot's old C code, particularly driver and os code. Microsoft
Win32 is rife with it.

Except that it's not legal C, either.  The C language was
carefully specified so that a bounds checking implementation
would be legal, and the above will fail in such cases.

In C++, of course, the declaration he's looking for is
std::vector<SvrList>, without any manual new.

You mean

std::vector<GameSvr>

right?

KHD
 
J

Juha Nieminen

Vladimir Jovic said:
What bothers me is how can you use an array of size zero.

You see, there's this operator which uses the [ and ] symbols...
This looks like a hack. Why declare an array of size 0?

What size would you want to use, given that the size is determined
dynamically at runtime?
Why not use a pointer (like someone suggested)?

Because indexing a pointer would access the memory pointed by it.
Where would you make the pointer point to?
 
J

Juha Nieminen

James Kanze said:
In C++, of course, the declaration he's looking for is
std::vector<SvrList>, without any manual new.

Except that it doesn't do the same thing.

It's a bit like the difference between a two-dimensional array and an
array of pointers (all of which point to separately allocated arrays of
their own). Accessing them might use the same syntax, but they are not
the same thing.
 
I

Ian Collins

Vladimir Jovic wrote:
I wouldn't call this fine. Even
pList->svr[0]
is accessing the element that is out of array's bounds, and
that is UB. How come your program is not crashing, or at
least going crazy? Maybe you are just unlucky to have a bug
hidden.
It's an old C programmers hack. I've come across this idiom in
lot's old C code, particularly driver and os code. Microsoft
Win32 is rife with it.

Except that it's not legal C, either.

Which is why it's referred to above as a "hack". Quite a common one, too.

So common it was standardised (in a slightly different form) in C99
(6.7.2.1 p16). "struct hack" even appears in the standard's index!
 
H

Helge Kruse

Hi, I need your help.
Any body give some suggestions?

1)

If this is a compiler-specific warning, you could suppress it compiler-
specific:

#ifdef _MSCVER
#pragma warning(push)
#pragma warning(disable:4200)
// define your sruct
#pragma warning(pop)
#endif

2)
What about a placement new?
 
V

Vladimir Jovic

Juha said:
Vladimir Jovic said:
What bothers me is how can you use an array of size zero.

You see, there's this operator which uses the [ and ] symbols...
This looks like a hack. Why declare an array of size 0?

What size would you want to use, given that the size is determined
dynamically at runtime?
Why not use a pointer (like someone suggested)?

Because indexing a pointer would access the memory pointed by it.
Where would you make the pointer point to?

What is wrong with next code?


struct SvrList{
unsigned int uNum;
GameSvr *svr;
};

....
SvrList list;
list.uNum = 5;
list.svr = (GameSvr*) malloc( list.uNum * sizeof( GameSvr ) );
....


Since someone pointed out that the original code is used for
performances reasons, how is this worse then the code in the first post?
 
T

thomas

Vladimir said:
thomas wrote:
Hi, I need your help.
----------
struct SvrList{
    unsigned int uNum;
    GameSvr  svr[0];            //line A
};
---------
Once I declared a struct like this to store server list info.
It's supposed to be used like this.
----------
SvrList* pList = (SvrList*)malloc(sizeof(
SvrList) + svrNum*sizeof(GameSvr));
pList->uNum, pList->svr[0], pList->svr[1].... blabla..
I wouldn't call this fine. Even
pList->svr[0]
is accessing the element that is out of array's bounds, and
that is UB.  How come your program is not crashing, or at
least going crazy? Maybe you are just unlucky to have a bug
hidden.
It's an old C programmers hack. I've come across this idiom in
lot's old C code, particularly driver and os code. Microsoft
Win32 is rife with it.

Except that it's not legal C, either.  The C language was
carefully specified so that a bounds checking implementation
would be legal, and the above will fail in such cases.

In C++, of course, the declaration he's looking for is
std::vector<SvrList>, without any manual new.

But I hate vectors. Different distributors may have different
implementations. When I pass vectors between dlls, things unexpected
may happen.
 
T

thomas

Pete said:
Hi, I need your help.
----------
struct SvrList{
unsigned int uNum;
GameSvr  svr[0];            //line A
};
---------
Once I declared a struct like this to store server list info.
It's supposed to be used like this.
----------
SvrList* pList = (SvrList*)malloc(sizeof(
SvrList) + svrNum*sizeof(GameSvr));
pList->uNum, pList->svr[0], pList->svr[1].... blabla..
I wouldn't call this fine. Even
pList->svr[0]
is accessing the element that is out of array's bounds, and
that is UB.  How come your program is not crashing, or at
least going crazy? Maybe you are just unlucky to have a bug
hidden.
It's an old C programmers hack. I've come across this idiom in
lot's old C code, particularly driver and os code. Microsoft
Win32 is rife with it.
Except that it's not legal C, either.
Which is why it's referred to above as a "hack". Quite a common one, too.

Usually we use the term "hack" when the code relies on a specific
manifestation of undefined (or unspecified) behavior, but otherwise is
well-formed.

In this case the code is ill-formed, since 0-size array declaration is
illegal in C++ (as well as in C). In other words, arguing about the "out
of bounds" access here doesn't make much sense, since the code is
formally non-compilable.

Wait.. I don't think it's illegal in C++. At least I will definitely
object making it illegal by the standard community.
It can be dangerous but it can also do good. It depends on whether we
are using it correctly.
Will you guys getting crazy if we do things like this

-------code-----
struct A{
int num;
int p[0];
};
A *pA = (A*)malloc(sizeof(A)+sizeof(int)*10);
printf("%d\n", &pA->p[10] - &pA->p[0]); //accessing out of
bounds.
------code---
 
V

Vladimir Jovic

Juha said:
Simple: By indexing.


"Similar" is not the same as "identical". You have a radical difference:
You are not allocating any memory for the array.

Perhaps you missed the point that in the original example memory is being
"overallocated" with malloc() and the array indexing is done inside that
allocated memory.

If you have a memory allocated with malloc(), you can do whatever you
want within that memory block. That's like the very definition of memory
allocation.

Yes, I must admit I missed that when I posted that code, but later I
realised what was done. The question is still valid : why was it
declared like array of zero size, and not like pointer?

Your point with operator[] doesn't stand (else thread), as you can use
it for pointers as well.
 
T

thomas

  Simple: By indexing.
  "Similar" is not the same as "identical". You have a radical difference:
You are not allocating any memory for the array.
  Perhaps you missed the point that in the original example memory is being
"overallocated" with malloc() and the array indexing is done inside that
allocated memory.
  If you have a memory allocated with malloc(), you can do whatever you
want within that memory block. That's like the very definition of memory
allocation.

Yes, I must admit I missed that when I posted that code, but later I
realised what was done. The question is still valid : why was it
declared like array of zero size, and not like pointer?

Your point with operator[] doesn't stand (else thread), as you can use
it for pointers as well.

Different styles. It definitely works if defining it as a pointer.
But I like the other way, why not?
 
Ö

Öö Tiib

Pete said:
Hi, I need your help.
----------
struct SvrList{
unsigned int uNum;
GameSvr  svr[0];            //line A
};
---------
Once I declared a struct like this to store server list info.
It's supposed to be used like this.
----------
SvrList* pList = (SvrList*)malloc(sizeof(
SvrList) + svrNum*sizeof(GameSvr));
pList->uNum, pList->svr[0], pList->svr[1].... blabla..
I wouldn't call this fine. Even
pList->svr[0]
is accessing the element that is out of array's bounds, and
that is UB.  How come your program is not crashing, or at
least going crazy? Maybe you are just unlucky to have a bug
hidden.
It's an old C programmers hack. I've come across this idiom in
lot's old C code, particularly driver and os code. Microsoft
Win32 is rife with it.
Except that it's not legal C, either.
Which is why it's referred to above as a "hack". Quite a common one, too.
Usually we use the term "hack" when the code relies on a specific
manifestation of undefined (or unspecified) behavior, but otherwise is
well-formed.
In this case the code is ill-formed, since 0-size array declaration is
illegal in C++ (as well as in C). In other words, arguing about the "out
of bounds" access here doesn't make much sense, since the code is
formally non-compilable.

Wait.. I don't think it's illegal in C++. At least I will definitely
object making it illegal by the standard community.
It can be dangerous but it can also do good. It depends on whether we
are using it correctly.
Will you guys getting crazy if we do things like this

-------code-----
struct A{
    int num;
    int p[0];};

A *pA = (A*)malloc(sizeof(A)+sizeof(int)*10);
printf("%d\n", &pA->p[10] - &pA->p[0]);           //accessing out of
bounds.
------code---

Crazy? Why? C++ compilers should not simply compile it by language
rules that does not allow zero-sized arrays. If a compiler compiles it
then it is non-standard extension.

I have seen similar C code, but that used "int p[1];". If it was
compiled with C++ compiler and run-time bounds-checking on then it did
fault when accessing anything but p[0].

In C++ you should use vector<int> instead, it is about as fast.
 
T

thomas

On Aug 20, 2:01 am, Andrey Tarasevich <[email protected]>
wrote:
Pete Becker wrote:
Hi, I need your help.
----------
struct SvrList{
unsigned int uNum;
GameSvr  svr[0];            //line A
};
---------
Once I declared a struct like this to store server list info.
It's supposed to be used like this.
----------
SvrList* pList = (SvrList*)malloc(sizeof(
SvrList) + svrNum*sizeof(GameSvr));
pList->uNum, pList->svr[0], pList->svr[1].... blabla..
I wouldn't call this fine. Even
pList->svr[0]
is accessing the element that is out of array's bounds, and
that is UB.  How come your program is not crashing, or at
least going crazy? Maybe you are just unlucky to have a bug
hidden.
It's an old C programmers hack. I've come across this idiom in
lot's old C code, particularly driver and os code. Microsoft
Win32 is rife with it.
Except that it's not legal C, either.
Which is why it's referred to above as a "hack". Quite a common one, too.
Usually we use the term "hack" when the code relies on a specific
manifestation of undefined (or unspecified) behavior, but otherwise is
well-formed.
In this case the code is ill-formed, since 0-size array declaration is
illegal in C++ (as well as in C). In other words, arguing about the "out
of bounds" access here doesn't make much sense, since the code is
formally non-compilable.
Wait.. I don't think it's illegal in C++. At least I will definitely
object making it illegal by the standard community.
It can be dangerous but it can also do good. It depends on whether we
are using it correctly.
Will you guys getting crazy if we do things like this
-------code-----
struct A{
    int num;
    int p[0];};
A *pA = (A*)malloc(sizeof(A)+sizeof(int)*10);
printf("%d\n", &pA->p[10] - &pA->p[0]);           //accessing out of
bounds.
------code---

Crazy? Why? C++ compilers should not simply compile it by language
rules that does not allow zero-sized arrays. If a compiler compiles it
then it is non-standard extension.

I have seen similar C code, but that used "int p[1];". If it was
compiled with C++ compiler and run-time bounds-checking on then it did
fault when accessing anything but p[0].

In C++ you should use vector<int> instead, it is about as fast.

I hate vectors, again. C++ is not portable as binary code.
Passing vectors as arguments out of a dll in windows may crash the
system when accessing it.
 
Ö

Öö Tiib

  You see, there's this operator which uses the [ and ] symbols...
This looks like a hack. Why declare an array of size 0?
  What size would you want to use, given that the size is determined
dynamically at runtime?
  Because indexing a pointer would access the memory pointed by it.
Where would you make the pointer point to?

What is wrong with next code?

struct SvrList{
     unsigned int uNum;
     GameSvr  *svr;

};

...
SvrList list;
list.uNum = 5;
list.svr = (GameSvr*) malloc( list.uNum * sizeof( GameSvr ) );
...

Since someone pointed out that the original code is used for
performances reasons, how is this worse then the code in the first post?

Because you create SvrList with automatic storage type and so pretend
its costless. Actual code equal to original should allocate SvrList
itself too dynamically together with place for GameSvr it contains.
That makes two mallocs and malloc may be one of the slowest things on
some crappy platforms.
 
T

tni

Wait.. I don't think it's illegal in C++. At least I will definitely
object making it illegal by the standard community.

None of the C or C++ standards allow an array size of 0.

C99 has flexible arrays (which do basically the same thing), but they
are not included in C++03. Unfortunately, it doesn't look like C99
flexible arrays are part of C++0x either. The somewhat good news is that
GCC, Visual C++ 2010 (and presumably Intel C++) do support them.

(GCC and Visual C++ support 0-sized arrays as extension.)
 
V

Vladimir Jovic

thomas said:
Pete said:
Hi, I need your help.
----------
struct SvrList{
unsigned int uNum;
GameSvr svr[0]; //line A
};
---------
Once I declared a struct like this to store server list info.
It's supposed to be used like this.
----------
SvrList* pList = (SvrList*)malloc(sizeof(
SvrList) + svrNum*sizeof(GameSvr));
pList->uNum, pList->svr[0], pList->svr[1].... blabla..
I wouldn't call this fine. Even
pList->svr[0]
is accessing the element that is out of array's bounds, and
that is UB. How come your program is not crashing, or at
least going crazy? Maybe you are just unlucky to have a bug
hidden.
It's an old C programmers hack. I've come across this idiom in
lot's old C code, particularly driver and os code. Microsoft
Win32 is rife with it.
Except that it's not legal C, either.
Which is why it's referred to above as a "hack". Quite a common one, too.
Usually we use the term "hack" when the code relies on a specific
manifestation of undefined (or unspecified) behavior, but otherwise is
well-formed.

In this case the code is ill-formed, since 0-size array declaration is
illegal in C++ (as well as in C). In other words, arguing about the "out
of bounds" access here doesn't make much sense, since the code is
formally non-compilable.

Wait.. I don't think it's illegal in C++. At least I will definitely
object making it illegal by the standard community.

In the c++ standard, see 8.3.4.1 , this part :

.... If the constant-expression (5.19) is present, it shall be an
integral constant expression and its value shall be greater than zero...

therefore it is illegal c++ code.
It can be dangerous but it can also do good. It depends on whether we
are using it correctly.

Can't use it correctly. It is illegal, therefore undefined behaviour.
Will you guys getting crazy if we do things like this

-------code-----
struct A{
int num;
int p[0];
};
A *pA = (A*)malloc(sizeof(A)+sizeof(int)*10);
printf("%d\n", &pA->p[10] - &pA->p[0]); //accessing out of
bounds.
------code---

No, but compiler will go crazy (thanks gcc for -Werror flag :) )
 
G

Goran Pusic

But I hate vectors. Different distributors may have different
implementations. When I pass vectors between dlls, things unexpected
may happen.

C++ language knows nothing of DLLs. Your first error there is trying
to expose C++ - specific (and not only that, but potentially
particular compiler and standard library version version specific)
interface out of your module.

This is such a massive no-no that it's not even funny. You can only do
that if all modules will be compiled with same compiler (version,
too), and if interface uses standard library, if modules will use same
standard library version and even compilation options. For example,
with STL shipped with VS, compiling a DLL exposing standard containers
in it's interface with/without SECURE_STL breaks low-level binary
compatibility for any container.

It's not a question of liking at all. This sort of interoperability
works only under very specific circumstances (e.g. if you control, or
at least mandate, the build process and the deployment process
__entirely__).

Goran.
 
F

Francesco S. Carta

Vladimir Jovic wrote:
thomas wrote:
Hi, I need your help.
----------
struct SvrList{
unsigned int uNum;
GameSvr svr[0]; //line A
};
---------
Once I declared a struct like this to store server list info.
It's supposed to be used like this.
----------
SvrList* pList = (SvrList*)malloc(sizeof(
SvrList) + svrNum*sizeof(GameSvr));
pList->uNum, pList->svr[0], pList->svr[1].... blabla..
I wouldn't call this fine. Even
pList->svr[0]
is accessing the element that is out of array's bounds, and
that is UB. How come your program is not crashing, or at
least going crazy? Maybe you are just unlucky to have a bug
hidden.
It's an old C programmers hack. I've come across this idiom in
lot's old C code, particularly driver and os code. Microsoft
Win32 is rife with it.

Except that it's not legal C, either. The C language was
carefully specified so that a bounds checking implementation
would be legal, and the above will fail in such cases.

In C++, of course, the declaration he's looking for is
std::vector<SvrList>, without any manual new.

But I hate vectors. Different distributors may have different
implementations. When I pass vectors between dlls, things unexpected
may happen.

You could define interfaces using "C" linkage and send out vectors as
arrays by simply passing the address of the first element and the size
of the vector, just to make the communication layer happy and compatible.
 

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,777
Messages
2,569,604
Members
45,219
Latest member
KristieKoh

Latest Threads

Top