Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Const char array with template argument size vs. char pointer

I saw a construct of the following type in some code today:

template<unsigned int N> unsigned int f(const char (&a)[N]);

Is there any reasonable point in having this over having:

unsigned int f(const char *a);

I vaguely understand the pointer sharing implications of the latter, but is it really so bad, that it needs to be replaced by an obscure code twice the size?

(Unfortunately, I cannot ask the author of the code, otherwise I would)

like image 546
stanm Avatar asked Oct 01 '14 10:10

stanm


People also ask

What is the difference between char array and char pointer?

char[] is a character array whereas char* is a pointer reference. char[] is a specific section of memory in which we can do things like indexing, whereas char* is the pointer that points to the memory location.

Can a char * be passed as const * argument?

char* can be safely cast to const char*, so you can pass a char* to that method just like you would a const char*. The compiler will just do the cast for you.

What is the size of char pointer in C?

Size of Character Pointer Output: The size of the character pointer is 8 bytes. Note: This code is executed on a 64-bit processor.

What is the difference between char const and const char?

The difference is that const char * is a pointer to a const char , while char * const is a constant pointer to a char . The first, the value being pointed to can't be changed but the pointer can be. The second, the value being pointed at can change but the pointer can't (similar to a reference).


4 Answers

The intent of passing any raw pointer to a function is the caller has some idea of:

  • What it points to.
  • How many it points to.

C-style strings as input parameters have the latter inferred, as the assumption is that "how many" is deemed by arrival at the null char terminator.

But what if you're not passing a C-style string? What if it is simply a sequence of zero-or-more char values? Well, if that is the case, then:

void f(const char *s)
{
    // how many char does s refer to?
}

The logical deduction would be to do this:

void f(const char *s, std::size_t N)
{
    // Now the caller is telling us there are N chars at s
}

and this is not uncommon at all, albeit a potential point of error if the caller passes us the wrong length (never say never).

But what if there were a way to slurp that data from the actual variable type being passed to the function using deduction via non-type template parameter? What if the caller is invoking us with a fixed array?

template<std::size_t N>
void f(const char(&ar)[N])
{
    // we know the caller is passing a const-reference to a
    // char array declared of size N. The value N can be used
    // in this function.
}

Now we know both items in our list: the "what" and the "how many". Furthermore, we can now provide both a template function and an overload and have both worlds available to us:

// length specified implementation
void f(const char *s, std::size_t N)
{
    // caller passed N
}

// fixed buffer template wrapper
template<std::size_t N>
void f(const char(&ar)[N])
{
    f(ar,N); // invokes length-specified implementation from above.
}

And both of the following will work:

int main()
{
    char buff[3];

    f(buff,3);
    f(buff);

}

So how is this good? Because the following will flag a compiler error, as no matching implementation can be found:

int main()
{
    char buff[3];
    const char *ptr = buff;
    f(ptr); // ERROR: no matching function f(const char *)
}

In summary it is a common technique to assist in providing both items in our bullet list to the callee: the "what" and the "how much", without having to long-hand sizeof(ar)/sizeof(*ar) every time you use a fixed-length native array as your input parameter.

Best of luck.

like image 178
WhozCraig Avatar answered Oct 24 '22 01:10

WhozCraig


Pointers do not keep information whether they point to a single object or the first object of an array. So if you for example write within the body of function

unsigned int f(const char *a);

expression

sizeof( a ) / sizeof( *a )

you will get only the size of the pointer itself. While if you would use the same expression within the body of function

template<unsigned int N> unsigned int f(const char (&a)[N]);

you will get the size of the array (of course you could use simply value of N).

So when the second approach is used then usually such functions are declared with two parameters where the second parameter specifies the size of the array

unsigned int f(const char *a, size_t n);

Consider a situation when you need to append the character array passed as an argument with other string (let's assume that qualifier const is absent). When you use the second declaration then even function strlen applied to the pointer will not help you to determine whether the original string is large enough that could be appended. In the first approach you can use expression N - strlen( a ) that to determine whether there is enough space in the array.

like image 30
Vlad from Moscow Avatar answered Oct 24 '22 01:10

Vlad from Moscow


template<unsigned int N> unsigned int f(const char (&a)[N]);

This function template takes in the array by reference, which means when used directly with statically allocated C-Arrays the function knows the array size at compile.

unsigned int f(const char *a);

Here all the function knows is that it has been given some pointer to a const char. So if you wanted to use it to manipulate an array it would have to take in the array size as an additional argument since there is no way to retrieve this information from the pointer alone.

like image 30
Lionel Avatar answered Oct 24 '22 02:10

Lionel


The template version takes an array of a certain size. The pointer version takes only a pointer, without any concept of the size of the data pointed to by the pointer.

like image 31
Matthew Iselin Avatar answered Oct 24 '22 03:10

Matthew Iselin