在 C99 或 C11 中,你会这样做:
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");
}
}
请注意,大小在数组之前而不是之后。此功能将在以下情况下正常工作:
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);
}
有人问我:
你的例子确实看起来好多了,但它定义明确吗?真的n
保证被评估过int arr[n][n]
吗?函数参数的评估顺序不是未指定的行为吗?
旧标准 (ISO/IEC 9899:1999) 在 §6.7.5.2* Array declarators * 中说:
¶5如果 size 是一个不是整数常量表达式的表达式:如果它出现在函数原型范围的声明中,则将其视为被替换为*
; 否则,每次对其进行评估时,它的值都应大于零。可变长度数组类型的每个实例的大小在其生命周期内不会改变。如果大小表达式是运算sizeof
符操作数的一部分,并且更改大小表达式的值不会影响运算符的结果,则未指定是否计算大小表达式。
它给出了一个例子(它是非规范性文本,因为它是一个例子,但强烈地表明了预期):
示例 4 所有可变修改 (VM) 类型的声明必须在块范围或函数原型范围内。static
使用or存储类说明符声明的数组对象extern
不能具有可变长度数组 (VLA) 类型。但是,使用static
存储类说明符声明的对象可以具有 VM 类型(即,指向 VLA 类型的指针)。最后,使用 VM 类型声明的所有标识符都必须是普通标识符,因此不能是结构或联合的成员。
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
}
还有其他示例显示了可变修改的函数参数。
此外,在 §6.9.10 Function definitions中,它说:
¶10 在进入函数时,每个可变修改参数的大小表达式都会被评估,并且每个参数表达式的值都被转换为相应参数的类型,就像通过赋值一样。(作为参数的数组表达式和函数指示符在调用之前被转换为指针。)