There is a difference between a pointer to a character (L1) and an array of char (L2 and L3)
In L3 d is a pointer to double but in the other 2 lines d is a double type variable.
The first two are not declarations as the (;) is missing. So the third and only prototype is the only one which can be expanded into a valid definition. It must be the fastest!

Yes Peter87 but in the case of the last two, str operate as pointers to the first element of each of their respective arrays. In the first case *ptr is a pointer to a unknown char (but existing to be legal!)... and in my view there-in lies the difference.
Please excuse my pedantisism

Yes Peter87 but in the case of the last two, str operate as pointers to the first element of each of their respective arrays. In the first case *ptr is a pointer to a unknown char (but existing to be legal!)...

They don't just operate as pointers. They literally are pointers. The array isn't copied and passed to the function. You can't use sizeof() on the parameter and get the size of the original array. Assigning to the pointer doesn't affect the original array. (And then there's the fact that you can assign to the pointer and that's something you couldn't do to an array...)

func( nullptr )

is just as legal for void func(char* c);
as it is forvoid func(char c[]);

What's more, if the two were different types you could overload the function based on the types. The types are identical, though, so that's impossible.