Yes...but...it's not necessarily a good idea.
In essence, all memory is just "chunks" of RAM, and the various structures we put in our code are ultimately "mapped" to those chunks.
So while
int arr2p[4][4];
and
int arr1p[16];
Will probably occupy the same amount of memory, the second is not a 2D array, although (as CPalini as shown) it can be accessed in a similar way.
But it's not a good idea. Partly because the code is a lot less obvious to the next person to have to modify it, and partly because the compiler is probably a lot better at optimising the code for the array access than you are! :laugh:
And...some systems can do bounds checking to ensure that you don't "run off the end" of a row in a 2D array by mistake, which would have to be manually done in a 1D array treated as a 2D.
Generally, if you need a 2D array, declare it as a 2D array so that the code is more maintainable. If you need to "run" from the end of one row to the beginning of the next for performance reasons, then use pointers instead of indexes anyway.