Coffee Space


Listen:

Array Pointer Decay

Preview Image

Preview Image

TL;DR

Arrays are weird and sizeof() may not always work how you expect. An array is not the same as a pointer and sometimes you will come unstuck. There are several C-based solutions to this problem depending on your specific needs, each with it's role to play.

Problem

So we have some array, let's say s - and we want to determine the size in C. A pretty standard thing to do. So let's do it:

0001 int main(){
0002   char s[] = {"Hello World!"};
0003   printf("Size of 's' is %u\n", sizeof(s));
0004 }

This then outputs Size of 's' is 13, so all is good. But we don't plan to use it here, we plan to use it in some function, like so:

0005 void test(char* s){
0006   printf("Size of 's' is %u\n", sizeof(s));
0007 }
0008 
0009 int main(){
0010   char s[] = {"Hello World!"};
0011   test(s);
0012 }

The output is now Size of 's' is 8. Hang on a second! What? sizeof() now fails to return the correct size because I passed it to a function? That "8" seems familiar some how... It doesn't feel like a coincidence that it is the same size as a pointer...

This isn't the first time I've run into this, my previous solution was to supply the length as an additional parameter, curl up into a ball and forget the problem exists. But not this time. This time I will grapple with the oddity (because I'm putting off doing more important work).

What?

Okay, so apparently it is some kind of array pointer decay. I never formally got taught C or C++, so this was interesting to learn of indeed. I was under the impression that things wouldn't do strange things in C, this is why I use it after all. But it turns out, this is not so much the case. Why do you do this to me?!

It turns out an array is a special thing, you can declare:

0013 char s[] = {"Hello World!"};

And this is a proper array. But if I pass this to a function normally (by pointer), information about size is lost.

It turns out there are several representations of the array:

Without going into details that others have, only functions that receive arrays by reference can use sizeof() to determine array size.

Potential Solutions

So here we have a comparison of potential solutions (including the ones that don't work):

0014 void test_ptr(char* a){
0015   printf("(ptr) Size of 'a' is %u\n", sizeof(a));
0016 }
0017 
0018 void test_val(char a[]){
0019   printf("(val) Size of 'a' is %u\n", sizeof(a));
0020 }
0021 
0022 struct Structure{
0023   char* a;
0024   unsigned int len;
0025 };
0026 
0027 void test_str(struct Structure s){
0028   printf("(str) Size of 'a' is %u\n", s.len);
0029 }
0030 
0031 void test_cnt(char* a){
0032   int len = 0;
0033   while(a[len++] != '\0');
0034   printf("(cnt) Size of 'a' is %u\n", len);
0035 }
0036 
0037 void test_par(char* a, int len){
0038   printf("(par) Size of 'a' is %u\n", len);
0039 }
0040 
0041 int main(){
0042   char s[] = {"Hello World!"};
0043   printf("Size of 's' is %u\n", sizeof(s));
0044   // by pointer
0045   test_ptr(s);
0046   // by value
0047   test_val(s);
0048   // structure method
0049   struct Structure z;
0050   z.a = s;
0051   z.len = sizeof(s);
0052   test_str(z);
0053   // count method
0054   test_cnt(s);
0055   // param method
0056   test_par(s, sizeof(s));
0057 }

This results in:

0058 Size of 's' is 13
0059 (ptr) Size of 'a' is 8
0060 (val) Size of 'a' is 8
0061 (str) Size of 'a' is 13
0062 (cnt) Size of 'a' is 13
0063 (par) Size of 'a' is 13

The counting method (test_cnt()) is really only appropriate for simple array types that you know their terminating byte. This is not reliable and is quite slow.

The structure method (test_str()) is fine enough, but it could add complexity to your code, when all you really want is a simple array. Inventing data types is all fine and dandy until your code grows and is used by others.

The parameter method (test_par()) is probably ideal, as long as the number of parameters you have is quite small. This is the one I have been using for years as a workaround anyway.