简体   繁体   中英

Alternative to zero-sized array in embedded C++

The Multiboot Specification has structures like this:

struct multiboot_tag_mmap
{
  multiboot_uint32_t type;
  multiboot_uint32_t size;
  multiboot_uint32_t entry_size;
  multiboot_uint32_t entry_version;
  struct multiboot_mmap_entry entries[0];  
};

The intent seems to be that the array size can vary. The information is not known until passed along by the boot loader. In hosted C++, the suggested advice is to "use vector ". Well, I can't do that. The alternative is to use dynamic allocation, but that would require implementing a significant chunk of the kernel (paging, MMU, etc.) before I even have the memory map information. A bit of a chicken or egg problem.

The "hack" is to just enable extensions with gnu++11 . But I try to avoid using extensions as much as possible to avoid C-ish code or code that could potentially lead to undefined behavior. The more portable the code is, the less chance of bugs in my opinion.

Finally, you iterate over the memory map like this:

for (mmap = ((struct multiboot_tag_mmap *) tag)->entries;
    (multiboot_uint8_t *) mmap
    < (multiboot_uint8_t *) tag + tag->size;
    mmap = (multiboot_memory_map_t *)
    ((unsigned long) mmap
    + ((struct multiboot_tag_mmap *) tag)->entry_size))

So the size of the structure is tag->size .

I can modify the multiboot header so long as the semantics are the same. The point is how it looks to the bootloader. What can I do?

Instead of 0-sized array, you can use 1-sized array:

struct multiboot_tag_mmap
{
    ...
    struct multiboot_mmap_entry entries[1]; 
};

This will change only result of sizeof(struct multiboot_tag_mmap) , but it shouldn't be used in any case: size of allocated structure should be computed as

offsetof(struct multiboot_tag_mmap, entries) + <num-of-entries> * sizeof(struct multiboot_mmap_entry)

Alignment of the map structure doesn't depends on the number of elements in the entries array, but on the entry type .

Strictly confirming alternative : If there is known bounary for array size, one can use this boundary for type declaration:

struct multiboot_tag_mmap
{
    ...
    struct multiboot_mmap_entry entries[<UPPER-BOUNDARY>]; 
};

For such declaration all possible issues described below are not applied.

NOTE about elements accessing: For accessing elements (above the first one) in such flexible array one need to declare new pointer variable:

struct multiboot_mmap_entry* entries = tag->entries;
entries[index] = ...; // This is OK.

instead of using entries field directly:

tag->entries[index] = ...; // WRONG! May spuriously fail!

The thing is that compiler, knowing that the only one element exists in the entries field array, may optimize last case it to:

tag->entries[0] = ...; // Compiler is in its rights to assume index to have the only allowed value

Issues about standard confirmance : With flexible array approach, there is no object of type struct multiboot_tag_mmap in the memory(in the heap or on the stack). All we have is a pointer of this type, which is never dereferenced (eg for making full copy of the object). Similarly, there is no object of the array type struct multiboot_mmap_entry[1] , corresponded to the entries field of the structure, this field is used only for conversion to generic pointer of type struct multiboot_mmap_entry* .

So, phrase in the C standard, which denotes Undefine Behavior

An object is assigned to an inexactly overlapping object or to an exactly overlapping object with incompatible type

is inapplicable for accessing entries array field using generic pointer: there is no overlapping object here.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM