In C99 or C11, you would do it like this:
void myfunc(int n, int arr[n][n])
{
    for (int i = 0; i < n; ++i)
    {
        for (int j = 0; j < n; ++j)
            printf("%d,", arr[i][j]);
        printf("\n");
    }
}
Note that the size precedes, not follows, the array.  This function will work correctly with:
int main(void)
{
    int seqs[8][8] =
    {
        { 0, 32, 36, 52, 48, 16, 20, 4 },
        { 0, 16, 20, 52, 48, 32, 36, 4 },
        { 0, 32, 36, 44, 40,  8, 12, 4 },
        { 0,  8, 12, 44, 40, 32, 36, 4 },
        { 0, 32, 36, 38, 34,  2,  6, 4 },
        { 0,  2,  6, 38, 34, 32, 36, 4 },
        { 0, 32, 36, 37, 33,  1,  5, 4 },
        { 0,  1,  5, 37, 33, 32, 36, 4 },
    };
    myfunc(8, seqs);
    int matrix3x3[3][3] = { { 1, 2, 3 }, { 2, 4, 6 }, { 3, 6, 9 } };
    myfunc(3, matrix3x3);
}
I was asked:
Your example does look much better indeed, but is it well-defined? Is n really guaranteed to be evaluated before int arr[n][n]? Wouldn't the order of evaluation of function parameters be unspecified behavior? 
The old standard (ISO/IEC 9899:1999) says in §6.7.5.2*Array declarators*:
¶5 If the size is an expression that is not an integer constant expression: if it occurs in a
  declaration at function prototype scope, it is treated as if it were replaced by *; otherwise,
  each time it is evaluated it shall have a value greater than zero. The size of each instance
  of a variable length array type does not change during its lifetime. Where a size
  expression is part of the operand of a sizeof operator and changing the value of the
  size expression would not affect the result of the operator, it is unspecified whether or not
  the size expression is evaluated.
And it gives an example (it is non-normative text because it is an example, but strongly indicative of what is expected):
EXAMPLE 4 All declarations of variably modified (VM) types have to be at either block scope or
  function prototype scope. Array objects declared with the static or extern storage-class specifier
  cannot have a variable length array (VLA) type. However, an object declared with the static storage class
  specifier can have a VM type (that is, a pointer to a VLA type). Finally, all identifiers declared with a
  VM type have to be ordinary identifiers and cannot, therefore, be members of structures or unions.
extern int n;
int A[n];                       // invalid: file scope VLA
extern int (*p2)[n];            // invalid: file scope VM
int B[100];                     // valid: file scope but not VM
void fvla(int m, int C[m][m]);  // valid: VLA with prototype scope
void fvla(int m, int C[m][m])   // valid: adjusted to auto pointer to VLA
{
    typedef int VLA[m][m];      // valid: block scope typedef VLA
    struct tag {
        int (*y)[n];            // invalid: y not ordinary identifier
        int z[n];               // invalid: z not ordinary identifier
    };
    int D[m];                   // valid: auto VLA
    static int E[m];            // invalid: static block scope VLA
    extern int F[m];            // invalid: F has linkage and is VLA
    int (*s)[m];                // valid: auto pointer to VLA
    extern int (*r)[m];         // invalid: r has linkage and points to VLA
    static int (*q)[m] = &B;    // valid: q is a static block pointer to VLA
}
There are other examples showing variably modified function parameters.
Also, in §6.9.10 Function definitions, it says:
¶10 On entry to the function, the size expressions of each variably modified parameter are
  evaluated and the value of each argument expression is converted to the type of the
  corresponding parameter as if by assignment. (Array expressions and function
  designators as arguments were converted to pointers before the call.)