在 C编程语言中,他们说只有数组的第一维(下标)可以自由指定;必须指定第二个下标:
如果要将二维数组传递给函数,则 函数中的参数声明必须包含数量 列;行数是无关紧要的,因为传递的是,as 之前,指向行数组的指针,其中每行是5的数组
int
秒。在这种特殊情况下,它是指向对象的指针 5int
s的数组。因此,如果要将数组daytab
传递给a 函数f
,f
的声明将是:f(int daytab[][5]) { ... }
更一般地说,只有第一个维度 数组的(下标)是免费的;所有其他都必须指定。
但是在我的程序中,当我更改第二个下标(列)的值时,程序仍然可以正常工作。 例如,程序
#include"stdio.h"
void arf(int[][2]); // I wrote 2 instead of 5
main() {
int a[][5] = {{1,2,3,4,5}, {9,29,39,49,59}};
arf(a);
}
void arf(int arr[][2]) { //here too, changed the 2nd subscript
size_t i;
printf("%d", arr[0][4]); //a[0][4] is 5
}
打印输出5
,而不是垃圾值(我的期望)。
答案 0 :(得分:1)
我有两个想法,讨论最终未定义的行为是否可取。这是问题中代码的最低修改版本:
#include <stdio.h>
void arf(int arr[][2]);
int main(void)
{
int a[][5] = { { 1, 2, 3, 4, 5 }, { 0, 9, 8, 7, 6 } };
arf(a);
}
void arf(int arr[][2])
{
printf("%d\n", arr[0][4]);
printf("%d\n", arr[1][4]);
}
在Mac上使用GCC 7.3.0编译时,我得到:
$ gcc -o arr2d-13 arr2d-13.c
arr2d-13.c: In function ‘main’:
arr2d-13.c:8:9: warning: passing argument 1 of ‘arf’ from incompatible pointer type [-Wincompatible-pointer-types]
arf(a);
^
arr2d-13.c:3:6: note: expected ‘int (*)[2]’ but argument is of type ‘int (*)[5]’
void arf(int arr[][2]);
^~~
$
仅此一点就足以告诉你,你做错了。我不得不压制我常用的编译标志;我无法接受代码,因为它有编译器警告。但是,运行时,输出为:
5
9
为什么呢?嗯,这是官方未定义的行为,但是......
内存中数组的布局如下:
╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗
║ 1 ║ 2 ║ 3 ║ 4 ║ 5 ║ 0 ║ 9 ║ 8 ║ 7 ║ 6 ║
╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝
尽管有警告,但数组传递给arf()
,该函数认为输入数组的第0行以包含1
的元素开头,输入数组的第1行以包含3
的元素,依此类推 - 因为您告诉编译器该函数采用每行包含2个元素的数组。
当您滥用下标4(参数的声明表明有效的第二个下标为0
和1
)时,它会将4添加到行的起始地址,并最终打印第一种情况5
;它在第二种情况下打印9
,因为计数从包含3的元素开始(之后的0,1,2,3,4个元素是9
)。
如果您的编译器没有警告您类型不匹配,则需要获得更好的编译器。如果编译器警告您类型不匹配,则需要注意编译器。在编程生涯的这个阶段,如果编译器需要警告你的代码,它就会发现你需要修复的bug。我仍然认为编译器警告是这样的 - 我通常用选项来编译来强制执行我的规则(gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes
) - 但我只用C编写了将近35年的编码,所以我知道我还有更多要学习的东西。
您还需要确保在C11模式(当前标准)或C99(旧标准)中进行编译 - 在C90(古老标准)或预标准模式下编译没有真正的借口。
C99说main()
本身已经过时,因为没有返回类型。始终明确指定返回类型,最好在忽略参数列表时使用int main(void)
,或者如果坚持则使用int main()
(标准中有使用该表示法的示例)。
答案 1 :(得分:1)
将多维数组转换为具有相同大小但具有不同几何的另一个数组的行为并非未定义:标准保证多维数组的元素将按行主顺序连续布局。
如果要让编译器更改数组的几何图形,可以转换为所需尺寸的指针,然后取消引用,如下所示:
int main(void)
{
const int a[][5] = { { 1, 2, 3, 4, 5 }, { 0, 9, 8, 7, 6 } };
arf(*(const int (*const)[][2])a);
}
使用其他下标,即转换为维度[5][2]
(或[sizeof(a)/sizeof(int[2])][2]
数组以获得更高的弹性)是告诉编译器有多少行,因此它可能在编译时捕获边界错误。在这个特定的例子中,信息只是在接收端抛弃,这对你没有任何好处,但C也允许你声明像void arf( const ptrdiff_t m, const ptrdiff_t n, const int a[m][n])
这样的函数原型。 C ++没有,但仍允许您声明void arf(const int a[rows][cols])
,其中rows
和cols
为constexpr
值。
请注意,您应始终始终, 始终 检查数组边界是否存在C和C ++中的溢出。