Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Zero-size array as struct member

Reply
Thread Tools

Zero-size array as struct member

 
 
Andrey Tarasevich
Guest
Posts: n/a
 
      08-18-2010
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..
> ---------
>
> The vs2005 compiler gives me the "nonstandard extension used : zero-
> sized array in struct/union" warning though.
> ...
> 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.


Well, declaring a 1-sized array is the way it is normally done. The
memory allocation is supposed to be done as follows

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

As you can notice, you can declare the array with any non-zero size in
this case, since the above total size calculation approach is not
affected by the declared array size.

--
Best regards,
Andrey Tarasevich
 
Reply With Quote
 
 
 
 
Vladimir Jovic
Guest
Posts: n/a
 
      08-19-2010
Joe Greer wrote:
> Vladimir Jovic <> wrote in news:i4gueb$vqv$1
> @news.albasani.net:
>
>> If the array's size is zero, how can you access even one element?

>
> What's your point? Why would you do that? If b were a vector, you would
> get similar behavior. That is, it is just as undefined if you don't
> bother to check the size of the vector before assigning data. This is a


Will next assertion pass or fail :
int a[0];
assert( sizeof( a ) == 0 );
?
What bothers me is how can you use an array of size zero.

> idiom used by device drivers to return information requiring a minimum
> number of allocations and a minimum number of frees. It is always used
> with malloc and the size of the array is always embedded in the struct
> somewhere so that you don't go out of bounds. So, to fix up your code
> below.
>
>> The example similar to the original example, except this one compiles :
>>
>> #include <iostream>
>> using namespace std;
>> struct A
>> {
>> int a;

> int b_len;
>> int b[0];
>> };

>
> A * AFactory(int sz)
> {
> A * p = (A *)malloc(sizeof(A) + sz * sizeof(b[0]));
> p->b_len = sz;
> return p
> }


This looks like a hack. Why declare an array of size 0? Why not use a
pointer (like someone suggested)? Is it another (now obsolete and
depreciated) way of declaring pointers?
 
Reply With Quote
 
 
 
 
James Kanze
Guest
Posts: n/a
 
      08-19-2010
On Aug 18, 1:36 pm, Jeff Flinn <TriumphSprint2...@hotmail.com> wrote:
> 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.

--
James Kanze
 
Reply With Quote
 
Bart van Ingen Schenau
Guest
Posts: n/a
 
      08-19-2010
On Aug 19, 11:50*am, Vladimir Jovic <vladasp...@gmail.com> wrote:
> Joe Greer wrote:
> > Vladimir Jovic <vladasp...@gmail.com> wrote in news:i4gueb$vqv$1
> > @news.albasani.net:

>
> >> If the array's size is zero, how can you access even one element?

>
> > What's your point? *Why would you do that? *If b were a vector, you would
> > get similar behavior. *That is, it is just as undefined if you don't
> > bother to check the size of the vector before assigning data. This is a

>
> Will next assertion pass or fail :
> * * * * int a[0];
> * * * * assert( sizeof( a ) == 0 );
> ?


That snippet should not compile, because it is not allowed to define
objects with size 0.

> What bothers me is how can you use an array of size zero.


You can't, except as a non-standard predecessor of the 'flexible array
member' of C99.
Some compilers will allow a zero-sized array as the last member of a
structure as a way to let the structure contain an array of
unspecified size. It is the full responsibility of the programmer to
ensure enough space gets allocated or the number of array members that
is actually needed.

<snip>
>
> > A * AFactory(int sz)
> > {
> > * *A * p = (A *)malloc(sizeof(A) + sz * sizeof(b[0]));
> > * *p->b_len = sz;
> > * *return p
> > }

>
> This looks like a hack. Why declare an array of size 0? Why not use a
> pointer (like someone suggested)? Is it another (now obsolete and
> depreciated) way of declaring pointers?


It is a hack, and it is commonly known as the 'struct hack'.
The reasons for not using a pointer can be very diverse.
In the (C-)code that I am currently working with, this hack is used in
the definition of the messages that are passed between different parts
of the system.
As the system consists of multiple (embedded) processors, it is a
requirement that a message is always fully contained within a single
contiguous buffer, so that it can be easily transported to a different
processor. This also rules out the use of internal pointers within the
buffer.

Bart v Ingen Schenau
 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      08-19-2010
On Aug 18, 3:30 pm, Juha Nieminen <nos...@thanks.invalid> wrote:
> Vladimir Jovic <vladasp...@gmail.com> 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?


> pList->svr[0] is accessing memory allocated by the malloc()
> call, hence it can't crash (well, at least if 'GameSvr' is
> a POD type).


pList->svr[0] is accessing beyond the end of the array, hence,
the code is undefined behavior, both in C and in C++. The
C language has been carefully specified to allow bounds checking
implementations---there has been (and maybe still is) one.

In C99, flexible array members were introduced to support this
idiom. In C++, std::vector does the job a lot cleaner.

--
James Kanze
 
Reply With Quote
 
Jeff Flinn
Guest
Posts: n/a
 
      08-19-2010
James Kanze wrote:
> On Aug 18, 1:36 pm, Jeff Flinn <TriumphSprint2...@hotmail.com> wrote:
>> 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.


Hence the term 'hack'.

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


Exactly, unless the op is dealing with some of that legacy Hacked code.

Jeff
 
Reply With Quote
 
Goran Pusic
Guest
Posts: n/a
 
      08-19-2010
On Aug 18, 1:30*pm, thomas <freshtho...@gmail.com> 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..
> ---------
>
> 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?


If you ever plan to change actual number of elements in your object,
just drop this crap and use a vector. If not, ponder this (I am
presuming that your uNum is number of elements you have there):

template<typename T>
// hbvla: Heap Based Variable Length Array
class hbvla : public boost::noncopyable // Can't assign, nor copy-
construct.
{
public:
void operator delete(void* p)
{
delete reinterpret_cast<char*>(p);
}
void operator delete(void* p, size_t /*cElements*/)
{ // Compiler calls this in case of exception in ctor
// that gets called through overloaded operator new (see ^^^).
// (note matched size_t cElements argument in two cases)
delete reinterpret_cast<char*>(p);
}

static hbvla* create(size_t cElements) { return new (cElements)
hbvla(cElements); }

~hbvla()
{
destroy_elements();
}

// Trivial helper methods.
size_t size() const { return _size; }
T& operator[](size_t i) { assert(i < size()); return _elements[i]; }
const T& operator[](size_t i) const { assert(i < size()); return
_elements[i]; }
T* begin() { return _elements; }
const T* begin() const { return _elements; }
T* end() { return _elements+size(); }
const T* end() const { return _elements+size(); }

private:
size_t _size;
T _elements[1]; // Standard does not allow 0.
// 1 is OK, but I could have put anything due to particular design.

// ^^^
void* operator new(size_t objectSize, size_t cElements)
{ // Only this operator new shall be used.

// Handle overflow.
const size_t MaxElements = (SIZE_MAX-objectSize)/cElements;
if (cElements > MaxElements)
throw std::bad_alloc();

return new char[objectSize + cElements*sizeof(T)];
}

/*
Using "standard" operators new with hbvla is does not work, so hide
them
(and don't implement them).

The [] form is not really needed in this case because default ctor
is hidden,
but I am leaving it here for informative purposes.

operator delete[] does not work either.
*/
void* operator new[](size_t objectSize);
void* operator new(size_t objectSize);
void operator delete[](void*);

typedef hbvla<T> this_type; // helper typedef.

// ctor: we must empty-construct our _elements from 1 to
(cElements-1).
// Empty ctor is dangerous. But since we have a ctor, we don't need
to write and hide anything.
hbvla(size_t cElements) : _size(0)
{
if (!cElements)
return; // Strange but possible.

// Construct elements from 1 to (cElements-1).
// Use ScopeGuard to destruct partially-constructed array of
elements.
_size = 1;
ScopeGuard elementsGuard = MakeObjGuard(*this,
&this_type::destroy_elements);
while (_size<cElements)
{
T* p = &_elements[_size];
new (p) T;
_size++;
}
elementsGuard.Dismiss();
}

void destroy_elements()
{ // destroy elements that we create "by hand"
// (C++ runtime destroys _element[0]).
std::for_each(begin()+1, end(), &this_type::destroy);
}

static void destroy(const T& element)
{
element.~T();
}
};

Goran.
 
Reply With Quote
 
Jorgen Grahn
Guest
Posts: n/a
 
      08-19-2010
On Wed, 2010-08-18, thomas wrote:
> Hi, I need your help.
>
> ----------
> struct SvrList{
> unsigned int uNum;
> GameSvr svr[0]; //line A
> };

....

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


That's a substantial change, IMHO. Take gcc for example. There you
have (last time I checked) no way to disable that warning without
disabling C++98 conformance. That can be *really* annoying, when you
have seen the warning daily for a year or two ...

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
 
Reply With Quote
 
Joe Greer
Guest
Posts: n/a
 
      08-19-2010
Vladimir Jovic <> wrote in
news:i4iuof$s5j$:

>
> This looks like a hack. Why declare an array of size 0? Why not use a
> pointer (like someone suggested)? Is it another (now obsolete and
> depreciated) way of declaring pointers?


Indeed, it is a hack, but a useful one. Usually the array is declared of
size 1 and not 0, but that doesn't make it any prettier. You can't use a
pointer because that would require 2 allocations and doesn't pass through
the network near as well. This is a mechanism for creating a struct with
some dynamically sized data using only 1 buffer. Memory allocation is not
in anyway cheap (not only to you have the usual overhead, but there is also
a mutex in there somewhere and possibly a kernel transistion) and in some
cases, one needs to have only one. This sort of thing can be the
difference between passing your data in hours instead of days for some
kinds of applications. You certainly would only use this technique if
profiling indicated that it was required, but it is useful to understand
it.

joe
 
Reply With Quote
 
Joe Greer
Guest
Posts: n/a
 
      08-19-2010
James Kanze <> wrote in news:19c5cd18-a44b-4b5f-b8f8-
:

>
> In C99, flexible array members were introduced to support this
> idiom. In C++, std::vector does the job a lot cleaner.
>
> --
> James Kanze
>


Not really. This hack is used to reduce memory allocations. Using a
vector doesn't really do that.

joe
 
Reply With Quote
 
 
 
Reply

Thread Tools

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

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


Similar Threads
Thread Thread Starter Forum Replies Last Post
Can *common* struct-members of 2 different struct-types, that are thesame for the first common members, be accessed via pointer cast to either struct-type? John Reye C Programming 28 05-08-2012 12:24 AM
Using an instance of a struct as a member of that struct dutchgoldtony C Programming 15 11-16-2005 11:24 PM
length of an array in a struct in an array of structs in a struct in an array of structs Tuan Bui Perl Misc 14 07-29-2005 02:39 PM
struct my_struct *p = (struct my_struct *)malloc(sizeof(struct my_struct)); Chris Fogelklou C Programming 36 04-20-2004 08:27 AM
How would I use qsort to sort a struct with a char* member and a long member - I want to sort in order of the long member Angus Comber C Programming 7 02-05-2004 06:41 PM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57