sizeof static array member returns the size of the pointer and not the array

1.7k Views Asked by At

So I have a C++ struct, which has a static array as a member, and I want to ask for it's size in the constructor. As I understood from this article http://msdn.microsoft.com/en-us/library/4s7x1k91(VS.71).aspx, sizeof can be applied to a static array to find the size (in bytes) of the entire array, not just it's type. However, when I do sizeof() on the member, it gives me 4 (the size of the pointer) instead of the size of the array. Here's the context (trivialized):

struct A
{
    char descirption[128];
    int value;

    A(const char desc[], int val)
    {
        size_t charsToCopy = std::min(sizeof(description), sizeof(desc));
        memcpy(description, desc, charsToCopy);
        value = val;
    }
}

int main()
{
    A instance("A description string", 1);
    //now instance has a description string that says "A des" followed by garbage characters
}

So how do I get the size of a member char array?

Edit

When I put a breakpoint in the compiler and inspect the two values sizeof(description) and sizeof(desc) I see sizeof(description) == 4, and sizeof(desc) == 21. Hence my confusion. Because I'm passing a string literal into the constructor, the compiler seems perfectly happy to tell me the real size of the string passed in. Maybe that wouldn't be the case if I assigned it to a variable somewhere, but right now I'm trying to track down the fundamental problem: sizeof( some-member-static-array ) gives me something (relatively) meaningless.

Is it possible that sizeof is doing some sort of string length measurement because it's an array of chars?

3

There are 3 best solutions below

6
On

It's not the member that's giving you a size of 4, it's the argument. Array type parameters in C++ are transformed to pointers, so your constructor is equivalent to:

A(const char *desc, int val)

That's why you're getting the size of a pointer. If you really want to use memcpy, you'll have to pass the length of your array to the constructor. The alternative is to use strncpy with a maximum number of characters set to 128.

Of course, if you were using std::string, you wouldn't have this problem.

0
On

Despite the syntax, you can't pass an array by value to a function. It decays to a pointer, leaving no way to find the size; your function is equivalent to

A(const char * desc, int val)

so that sizeof(desc) is the size of a pointer.

You could pass the array by reference, templating the function by the array's size:

template <size_t desc_size>
A(const char (&desc)[desc_size], int val)

The size argument will be inferred automatically from the function argument, so your code passing a string literal will work as it is.

However, this only works when the argument really is an array of known size; if you want to deal with more general strings, then you'll need something more complicated. Usually, it's more convenient to use std::string to manage strings - it handles memory automatically, and keeps track of the size for you.

0
On

In this constructor

A(const char desc[], int val)
{
    size_t charsToCopy = std::min(sizeof(description), sizeof(desc));
    memcpy(description, desc, charsToCopy);
    value = val;
}

parameter desc is implicitly converted by the compiler to the pointer to its first character. As you are using function std::min then it is obvious that sizeof( const char * ) is less than sizeof( description ) and the function will always return 4 provided that the size of pointer is equal to 4.

Moreover it can occur that the resulted string will not contain the terminating zero after the call of memcpy. I would define the constructor the following way

A(const char desc[], int val)
{
    strncpy(description, desc, 128);
    description[127] = '\0';
    value = val;
}

Also it is a good idea to define a name for the magic number 128 either using an enymerator or static constant.

Or at least you can write the following way

A(const char desc[], int val)
{
    const size_t N = sizeof( description );
    strncpy(description, desc, N);
    description[N-1] = '\0';
    value = val;
}