LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

To Tell the Truth

  typedef struct Point
  {
    double Xpoint;
    double Ypoint;
  } Point;
  typedef struct Box
  {
    double Xmin;
    double Ymin;
    double Xmax;
    double Ymax;
  } Box;
  typedef struct MultiPointMRecord_A
  {
    int    ShapeType;
    Box    Bounds; // Bounding box
    int    NumPoints;
    Vertex Parray[0]; // Array of Point's
  } MultiPointMRecord_A;

  A0 = sizeof (Vertex);
  A1 = sizeof (Box);
  A2 = sizeof (MultiPointMRecord_A);

I'm trying to create a variable length structure (MultiPointMRecord_A), but the CVI C compiler kepps telling lies. The values reported for A0, A1 and A2 are 16, 32 and 48 respectively. A0 and A1 are correct, but A2
is in error and should be 40.
With the array size set to 0, the C compiler reports via A2 that the size is 48, and 64 with the array size set to 1. This aspect of the behaviour is correct.
If I add another int to the structure, A2 does NOT change. I suspect that a pragma or compiler macro is neccessary to get the
compiler To Tell the Truth, but I haven't found one yet. 
 
I am running LabWindows/CVI V7.0 on XP.
 
Help,
Hurst
 
0 Kudos
Message 1 of 8
(4,464 Views)
You're forgetting about data structure alignment. Search the CVI help for "structure packing" and you'll find some topics on the subject. I recommend you review the "Structure Packing", "Structure Packing Pragma", and "Compatibility Issues in DLLs" for some good details.

Basically the default alignment is 8 bytes, meaning that every member of a structure will start on a 8-byte aligned address. If you have members that are less than 8 bytes (e.g. int), you'll have some extra padding bytes which contribute to the total size of the structure.

You can use #pragma pack (4) to use 4-byte alignment, which in your case would result in the numbers you expect.

Mert A.
National Instruments
Message 2 of 8
(4,450 Views)

That's not quite correct. Using the default (8 byte) alignment, according to your explanation where every member is 8-byte aligned, then a simple structure with 2 ints would have a size of 16 bytes. A simple experiment shows that this is not the case. The specified alignment is actually on the overall structure itself: in the case of a structure also being a member (of another structure) then yes, that particular member is aligned to 8 bytes, but this is not a general rule for all members.

JR

Message 3 of 8
(4,428 Views)

Thanks,

I found the pack pragma and it works just fine.

But this exposed another oddity. I calloc'ed the struct, accounting for the array at the end (2124 elements). I passed this to fwrite with the same size I had passed to calloc, however, fwrite reported that the array was too small 34000 bytes versus 34024 bytes (2124 * 16 + 40).

Hurst

0 Kudos
Message 4 of 8
(4,418 Views)
@JR:
Looks like I need to review some structure alignment reference material myself. 🙂 Though, it's a little more complicated than just aligning the "overall structure" by padding the end of a structure to ensure the size is a multiple of the alignment. Consider this structure:

struct T {
    double d1;
    char c1;      // 7 bytes of padding follow (8-byte alignment)
    double d2;
    char c2;      // 7 bytes of padding follow (8-byte alignment)
    double d3;
}

Under 8-byte alignment, the size of this structure is 40, even though the declared members only require 26 bytes. The reason this structure's size isn't 32 bytes (the smallest accommodating multiple of 😎 is the ordering of the members. Each member of a structure has a default alignment requirement that is equal to its size (e.g. 4 bytes for ints, 8 bytes for doubles, etc). The data alignment value (8 by default) that is overridden by the pack pragma defines the maximum alignment requirement of any data structure members. So even when the data alignment value is 8, ints still only need to align at 4-byte boundaries (like in the example you cited). On the other hand, when pragma pack(4) is specified, this reduces the alignment required by doubles from 8-byte to 4-byte. This would bring the size of the above structure down from 40 bytes to 32 bytes.

@hurst:
Could you post the specific lines of code for this?

Mert A.
National Instruments


0 Kudos
Message 5 of 8
(4,396 Views)

First of all, I used pack(1)

Here is the code that has the fwrite problem (error line has '>' marker at start of line):

  typedef struct MultiPointMRecord_A
  {
    int    ShapeType;
    Box    Bounds; // Bounding box
    int    NumPoints;
    Vertex Parray[]; // Array of Point's
  } MultiPointMRecord_A;
  typedef struct MultiPointMRecord_B
  {
    double Mmin;
    double Mmax;
    double Marray[]; // Array of M's
  } MultiPointMRecord_B;

  int                  i, j, row, col;
  int                  arraySize;
  int                  recordSize_A;
  int                  recordSize_B;
  long                 shpPos = ftell (shpStream);
  float                height = 0;
  float                minM, maxM;
  MultiPointMRecord_A *pRecord_A = NULL;
  MultiPointMRecord_B *pRecord_B = NULL;
  MainRecordHeader     mainHeader;
  IndexRecordHeader    indexHeader;

  // Calculate the array sizes
  arraySize    = pExt->imgWidth * pExt->imgHeight;
  recordSize_A = sizeof (MultiPointMRecord_A) + arraySize * sizeof (Vertex);
  recordSize_B = sizeof (MultiPointMRecord_B) + arraySize * sizeof (double);

  // Allocate memory for the max size records
  pRecord_A = calloc (1, recordSize_A);
  pRecord_B = calloc (1, recordSize_B);
 
  // Fill out the MultiPointMRecord_A
  StoreShapeInteger (SE_MultiPointM, LittleEndian, &pRecord_A->ShapeType);
  StoreShapeDouble  (pExt->limWest,  LittleEndian, &pRecord_A->Bounds.Xmin);
  StoreShapeDouble  (pExt->limSouth, LittleEndian, &pRecord_A->Bounds.Ymin);
  StoreShapeDouble  (pExt->limEast,  LittleEndian, &pRecord_A->Bounds.Xmax);
  StoreShapeDouble  (pExt->limNorth, LittleEndian, &pRecord_A->Bounds.Ymax);
  for (row = i = j = 0; row < pExt->imgHeight; row++)
  {
    for (col = 0; col < pExt->imgWidth; col++, i++)
    {
      if (pExt->extImage[row][col])
      {
        int status = SearchDataBase (NULL, pExt->limWest  + col, pExt->limSouth, pExt->zone, &height);
 
        StoreShapeDouble (pExt->limWest  + col, LittleEndian, &pRecord_A->Parray[i].Xpoint);
        StoreShapeDouble (pExt->limSouth + row, LittleEndian, &pRecord_A->Parray[i].Ypoint);

 if (status < 0)
   height = 0.0;
 
        StoreShapeDouble ((double)height, LittleEndian, &pRecord_B->Marray[i]);
  
 if (0 == j++)
 {
   minM = height;
   maxM = height;
 }
 
 else
 {
   if (height < minM)
     minM = height;
   else if (height > maxM)
     maxM = height;
 }
      }
    }
  }
  StoreShapeInteger (arraySize,      LittleEndian, &pRecord_A->NumPoints);

  // Fill out the MultiPointMRecord_B
  StoreShapeDouble  ((double)minM,   LittleEndian, &pRecord_B->Mmin);
  StoreShapeDouble  ((double)maxM,   LittleEndian, &pRecord_B->Mmax);

  // Create the shp Record Header
  StoreShapeInteger (++shpRecordCount,                  BigEndian, &mainHeader.RecordNumber);
  StoreShapeInteger ((recordSize_A + recordSize_B) / 2, BigEndian, &mainHeader.ContentLength);

  // Create the shx Record Header
  StoreShapeInteger (shpPos / 2,                        BigEndian, &indexHeader.RecordOffset);
  StoreShapeInteger ((recordSize_A + recordSize_B) / 2, BigEndian, &indexHeader.ContentLength);

  // Write the records to the shape files
  fwrite (&mainHeader, sizeof (MainRecordHeader), 1, shpStream);
>  fwrite (pRecord_A,   recordSize_A,              1, shpStream);
  fwrite (pRecord_B,   recordSize_B,              1, shpStream);
  shpSize += (sizeof (MainRecordHeader) + recordSize_A + recordSize_B) / 2;

  fwrite (&indexHeader, sizeof (IndexRecordHeader), 1, shxStream);
  shxSize += sizeof (IndexRecordHeader) / 2;

 

0 Kudos
Message 6 of 8
(4,384 Views)
I did a little digging and found that this is indeed a bug that occurs with structures containing incomplete (or zero-length) array members. We had already found and fixed this bug in CVI 8.5.0, so one solution would be to download and install a newer runtime engine. Another workaround is to select "No run-time checking" from the Debugging Level option on the Build Options dialog. Of course, this disables all runtime checking, which may not be desirable. Another option is to do the following:

#define DISABLE_RUNTIME_CHECKING(ptr) ((ptr) = (void *)((unsigned)(ptr)))
...
DISABLE_RUNTIME_CHECKING (pRecord_A);
DISABLE_RUNTIME_CHECKING (pRecord_B);
...
This little trick is described in the help topic "Disabling Protection for Individual Pointer". Essentially, all it does is throw away pointer information by temporarily casting the pointer to an unsigned int.

Mert A.
National Instruments
0 Kudos
Message 7 of 8
(4,377 Views)
In C89 there is no such thing as a variable length arrray (VLA).  This was added to C99, but this feature is not in the list of C99 extensions supported by CVI.  Also, I don't think that VLA's work the way you're trying to use them.  Take this with a grain of salt because I don't have a C99 compliant complier to try this on, but my understanding of VLA's is that you can pass the size of an array as an argument to a function, then declare that array locally to that funciton.  The compiler simply moves the stack pointer down the specified amount to make room for the array on the stack.  I don't think this will work with statically declared arrays since the compiler has to know how much memory to allocate for those at compile time.
 
I think you can accomplish what you want by using a pointer to the type instead of a VLA.  You're already allocating and freeing the memory anyway (I assume you free it somewhere later on), so trying to use a VLA doesn't really buy you anything.


Message Edited by TonyG 614 on 04-30-2008 04:20 PM
0 Kudos
Message 8 of 8
(4,360 Views)