Wednesday, November 12, 2014

"sizeof", is it an Operator or a Function ?

What do you expect the result of sizeof(a) in the following code should be?

struct D
{
    char *a;
    char *b;
    char *c;
};


static struct D a[] =
{
    {
        "1a",
        "1b",
        "1c"
     },
    {
        "2a",
        "2b",
        "2c"
    }
};


First, you need to know that "sizeof" is a unary (single parameter) operator (not a function):
  • Usually, the parentheses are used because they're needed as part of a type cast expression, besides, sizeof has a very high precedence, so "sizeof a + b" isn't the same as "sizeof (a+b)". But generally "sizeof a" is perfectly fine.
  • sizeof a++” does not modify a. As the operand of sizeof is not evaluated at runtime.
  • sizeof is a compile-time operator that, in order to calculate the size of an object, requires type information that is only available at compile-time. You can for example write something like this:
    #define N    (sizeof a);
    int array1[N];
Going back to our original question, sizeof(a) will result into 48. If you need the result to be 2, which is the logic answer, use:
sizeof a / sizeof a[0];
instead of :
sizeof a;
Another interesting fact about using sizeof, that you should take care of the memory padding problems that may make it results into unexpected results.
For example, let's consider the following piece of code:
typedef struct struct1
{
  UCHAR x;
  USHORT y[3];
  UCHAR z;
}struct1;

struct1 struct1_instance = {
   (UCHAR)0x00,
  {(USHORT)0xFFFF,(USHORT)0xFFFF,(USHORT)0xFFFF},
  (UCHAR)13
}


"sizeof(struct1_instance)" will result into 10 (bytes). While you will most probably expect it to return 8 (bytes). This strange result is due to memory padding in aligned machines. The largest dataytype element in the structure is USHORT, which takes two bytes. So, the structure data will be allocated padding for 2 byte alignment. So, the memory will look like this:

Bytes
1
2
3
4
5
6
7
8
9
10
Member
x
Padding
y[1]
y[2]
y[3]
z
padding
If you want to disable the memory padding, you should use "#pragma pack"; which instructs the compiler to pack structure members with a particular alignment.
With #pragma pack(1), the struct above would be laid out like this:
Bytes
1
2
3
4
5
6
7
8
Member
x
y[1]
y[2]
y[3]
z

And sizeof(struct1_instance) would be 8 instead of 10.
In this case, the code will look like the following:

# pragma pack (1)
typedef struct struct1
{
UCHAR
x;
USHORT y[3];
UCHAR z;
}struct1;
# pragma pack ()

No comments: