views:

422

answers:

4

Hello everyone.

I´m struggling to understand this concept: I have a fixed size definition:

(from http://msdn.microsoft.com/pt-br/library/aa931918.aspx)

typedef struct _FlashRegion {
  REGION_TYPE regionType;
  DWORD dwStartPhysBlock;
  DWORD dwNumPhysBlocks;
  DWORD dwNumLogicalBlocks;
  DWORD dwSectorsPerBlock;
  DWORD dwBytesPerBlock;
  DWORD dwCompactBlocks;
} FlashRegion, *PFlashRegion;

this FlashRegion struct, is used in this another struct: (from: http://msdn.microsoft.com/pt-br/library/aa932688.aspx)

typedef struct _FlashInfoEx {
  DWORD cbSize;
  FLASH_TYPEflashType;
  DWORD dwNumBlocks;
  WORD dwDataBytesPerSector;
   DWORD dwNumRegions;
  FlashRegion region[1]; 
} FlashInfoEx, *PFlashInfoEx;

The problem is, I can have a variable number of FlashRegions inside a FlashInfoEx. The function that I´m debugging does this somewhere in the code:

 memcpy (pFlashInfoEx->region,  g_pStorageDesc->pRegionTable,
         g_pStorageDesc->dwNumRegions *  sizeof(FlashRegion));

That means that it copies an amount of regions to pFlashInfoEx (that I pass in the call of the function);

So, the code will overwrite memory if dwNumRegions is bigger than one. If that is the case, Should I create a FlashRegion [FIXED_SIZE] in my code and somehow place/overwrite in FlashInfoEx->region? How do I do that?

Thanks, Marcelo

+6  A: 

The concept is while FlashRegion looks like a fixed size structure, it is actually dynamically sized. The magic is done when allocating the structure - instead of calling (FlashRegion*)malloc(sizeof(FlashInfoEx)) or new FlashRegion, you call something like (FlashRegion*)malloc(sizeof(FlashInfoEx)+sizeof(FlashRegion)*(numRegions-1))

Suma
Wow, you guys are fast here!So, that implies that region should always be the last member of the struct right? Depending of the compiler, isn't this a risk?
Marcelo
When defining structs to be able to interact with any API you need to be able to define exact memory layout of the structure. I think standard requires struct members to be ordered in the order specified, but it does not specify alignment. In practice all compilers provide alignment control as well.
Suma
Yes, the region must be last. Risk? Only in the sense that C itself is risky. This is in fact how the language was intended to be used. There is a reason that modern languages make this impossible, but that doesn't retroactively rip the design pattern out of the C89 and C90 toolkit.
DigitalRoss
In C++, it is very risky, yes. It doesn't play nice with non-POD types, copy constructors and such. But in C, it's a fairly common trick.
jalf
+2  A: 

This is a fairly common C idiom for variably sized structures. In order to make this work, you need to ensure you allocate enough memory for the array size you want, usually something like

pFlashInfoEx = malloc(offsetof(PFlashInfoEx, region) + g_pStorageDesc->dwNumRegions *  sizeof(FlashRegion));

this interacts badly with trying to use 'new' in C++; you have to allocate the memory manually:

void *mem = ::operator new(offsetof(PFlashInfoEx, region) + g_pStorageDesc->dwNumRegions *  sizeof(FlashRegion));
pFlashInfoEx = new(mem) PFlashInfoEx;
for (int i = 1; i < g_pStorageDesc->dwNumRegions; i++)
    new (&pFlashInfoEx->region[i]) FlashRegion;
Chris Dodd
so, simply FlashInfoEx flashInfo will not work then. Thanks.
Marcelo
+1  A: 

The interace that you are using is using the struct hack. This means that you need to manually dynamically allocate enough storage for the structure as if it was declared with region being an array of more than 1 FlashRegion.

For this interface, it needs to be enough space for at least and array of dwNumRegions FlashRegions.

Something like offsetof(FlashInfoEx, region) + sizeof FlashRegion * n bytes where n is a number that you need to subsequently pass to FMD_GetInfoEx or whatever function you are using.

Charles Bailey
A: 

...see this answer on legacy design patterns using a simple example in a previous question.

DigitalRoss