If you've never seen this before, I think it's transformative to how you read C/C++ declarations and clearer up a lot of confusion for me when I was learning.
Yeah, and I'm pointing to your pointer pointer and I'm a pointer pointer pointer. Or more accurately but less funny is that I'm pointing to your pointer-to-pointer and I'm a pointer-to-pointer-to-pointer.
In CUDA, the corresponding malloc cannot return the pointer to the allocated memory as runtime CUDA functions all return error codes instead. So the only way to "return" the pointer then without a return statement is to have a pointer given to that function by address, which means that you'll have a pointer-to-pointer among its arguments.
So that you can have an array of strings. It's useful to remember that in C arrays and pointers are exactly the same thing, just syntax sugar for however you want to look at it. There are a few exceptions where this isn't true however:
Argument of the & operator
Argument of sizeof
C11 has alignof which decay is a no-no
When it's a string literal of char[] or wide literal of wchar_t[], so like char str[] = "yo mama";
But int** is just an array of int*, which likewise int* can just be an array of int. In the picture here, we have int** anya that is an array of int* with a size of 1, int* anya that is an array of int with a size of 1, and then of course our int there being pointed to by int* anya.
I guess this is beating a dead horse but you can have pointers to pointers for 2D arrays.
The first pointer tells you which coulm you're on. The second pointer tells you which is the first object of each column. That way you can iterate the columns without loosing a reference to the current column you're standing on.
Quick example in straight C would be a cell in a matrix. The first pointer points to the row and the second pointer points to the cell in that row. This is am over simplification.