0

I know the usage of zero length arrays and I want to know if the following is an acceptable use

struct foo {

    int id_num;
    bool is_person;
    char person_name[0];
    char product_name[0];

}foo;

 and allocating as follows

#define NAME_SIZE 100

struct foo *data = (struct foo\*)malloc(sizeof(struct foo) + NAME_SIZE);

I have the above structure which is used to represent a Person. Now I want to use the same structure to represent a product. The structure will either refer a product or person based on the is_person flag. I don't want to put the last member inside a union as that will involve lot of code change in the existing code base. Therefore I modified it in the above way and it got compiled. I was wondering if this is a correct usage given the fact that I either use it as person or product and not both and I don't want to change the variable name to mean something generic like char person_or_product_name[0]. I am also assuming that person_name and product_name acts as identifiers to the same region of memory.

user881300
  • 212
  • 1
  • 9
  • This is certainly nonstandard, but you can use `sizeof` to see if the effect is really the same on your platform. – Potatoswatter May 16 '13 at 00:46
  • 2
    Whats wrong with wrapping the two arrays in an anonymous union? as far as I can tell you can then access the two arrays in the same way as you would above, and what you are doing would at least look clearer. Also, your code doesn't compile in VS2010, but wrapping it in a union does: struct foo { int id_num; bool is_person; union{ char person_name[0]; char product_name[0]; }; }; – matt May 16 '13 at 01:40
  • 2
    Why not keep things simple since you only want to store one kind of name at a time anyway: `struct foo { int id_num; bool is_person; char name[]; }` – Michael Burr May 16 '13 at 05:33
  • Zero-length arrays are a GCC extension that was superceded by the flexible array member notation in C99. In C99, you can only have one flexible array member at the end of a structure, not two. – Jonathan Leffler May 16 '13 at 13:38
  • @MichaelBurr but I don't want to change the variable name. That will result in lot of changes to existing code. – user881300 May 17 '13 at 00:19
  • @matt Your solution is perfect, I was wondering if I could get away with anonymous union but still have the same effect but seeing the answers I understand that it makes the code non-portable – user881300 May 17 '13 at 00:19
  • 1
    Just so I understand, the existing code uses a struct that looks like `struct foo { int id_num; char person_name[0]; };` and you want to add the `is_person` and the `product_name` fields? How big is the project? Unless it's absolutely humongous (and even then, how many modules would be accessing the `person_name` field?), I'd probably take the hit on changing the name of the field rather than rely on a hack that the next guy looking at the code will be scratching his head over and may not give a lot of confidence that it really works everywhere. – Michael Burr May 17 '13 at 00:35

2 Answers2

1

I am also assuming that person_name and product_name acts as identifiers to the same region of memory.

Exactly. That's the actual question here: whether a zero-length array occupies zero space. If it doesn't, you are hosed.

The C++ standard #9.2(12) says that 'later members have higher addresses within a class object', and #9(4) says 'a struct is a class ...', so, unless the C standard is different in this respect, it doesn't occupy zero space: there is padding after person_name, probably to the next 4-byte boundary (compiler- and #pragma- and option-dependent).

You could have tested that via

struct foo f;
assert(&f.person_name == &f.product_name);
user207421
  • 305,947
  • 44
  • 307
  • 483
  • hmm, #9.2(12) could just be referring to the order of members in memory though. also, this is another good reason to use an anonymous union. the whole idea behind a union is that the stuff inside occupies the same space. another thing is, I think his program would still work and be safe even if the two arrays didn't occupy the same memory :P – matt May 16 '13 at 01:57
  • @matt 'Higher address' => 'different address', no two ways about it. I agree entirely with your suggestion about the anonymous union. – user207421 May 16 '13 at 02:06
0

Ok, so to answer your question "I was wondering if this is a correct usage", its hard to tell because zero sized arrays are non standard(especially with members after them). in c99 they are standard using 'flexible array members' where you simply omit the number and go product_name[];

Array of zero length

However that ties your code to c99, the best standard way to do it is to simply have your array have a length of 1. and adjust your memory allocation size calculation. obviously having person_name and product_name both have a size of 1 would put them in different places in memory, however anonymous unions are standard in c and c++ so wrapping the two arrays in an anonymous union would put the two arrays on top of each other in memory. and should mean you don't have to change code, as the arrays would be accessed in the same way as they are in your example.

Why does C++ disallow anonymous structs and unions? (note comments suggest unions are legal)

struct foo {

    int id_num;
    bool is_person;
    union
    {
        char person_name[1];
        char product_name[1];
    };

}foo;

struct foo *data = (struct foo*)malloc(sizeof(struct foo) - sizeof(char) + NAME_SIZE * sizeof(char));
Community
  • 1
  • 1
matt
  • 4,042
  • 5
  • 32
  • 50
  • The 'array of size 1' is called 'the struct hack' and is not portable, which is why flexible array members were added to C99. However, there are few machines where the struct hack won't work; there's one important class of machine where flexible array members won't work — Windows machines using MSVC as the compiler (because that's a C89 compiler without support for most of C99). – Jonathan Leffler May 16 '13 at 13:40
  • @JonathanLeffler: I tried the flexible array member syntax in MSVC just the other day in response to another question about the struct hack, and I was surprised to find that MSVC supports it (since at least VC6). – Michael Burr May 17 '13 at 00:39
  • @MichaelBurr:That's interesting - it doesn't make knowing what is supported by MSVC any easier, but there's a limit to how worried I am about that. – Jonathan Leffler May 17 '13 at 00:42