简体   繁体   English

将二维数组的第二个下标传递给函数时,它有什么用?

[英]What is the use of the second subscript of 2-D array, when it is passed to a function?

In The C Programming Language , they have said that only the first dimension (subscript) of an array is free to be specified; C编程语言中 ,他们说过只能自由指定数组的第一个维(下标);它可以自由指定。 second subscript must be specified: 必须指定第二个下标:

If a two-dimensional array is to be passed to a function, the parameter declaration in the function must include the number of columns; 如果要将二维数组传递给函数,则函数中的参数声明必须包含列数; the number of rows is irrelevant, since what is passed is, as before, a pointer to an array of rows, where each row is an array of 5 int s. 行的数量无关紧要,因为像以前一样传递的是指向行数组的指针,其中每行都是5个int的数组。 In this particular case, it is a pointer to objects that are arrays of 5 int s. 在这种特殊情况下,它是一个指向5 int数组的对象的指针。 Thus if the array daytab is to be passed to a function f , the declaration of f would be: 因此,如果阵列daytab要被传递给函数f ,声明f将是:

 f(int daytab[][5]) { ... } 

More generally, only the first dimension (subscript) of an array is free; 更一般地,只有数组的第一维(下标)是自由的。 all the others have to be specified. 所有其他必须指定。

But in my programme, when I change the value of the second subscript (column), the programme still works the same. 但是在我的程序中,当我更改第二个下标(列) 的值时,该程序仍然可以正常工作。 For instance, the programme 例如程序

#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
}

prints the output 5 , instead of a garbage value (my expectation). 打印输出5 ,而不是垃圾值(我的期望)。

I'm in two minds about whether it's advisable to discuss what is ultimately undefined behaviour. 对于讨论什么是最终未定义的行为,是否明智,我有两种想法。 Here's a minimally modified version of the code in the question: 这是问题代码的最小修改版本:

#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]);
}

When compiled with GCC 7.3.0 on a Mac, I get: 在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]);
      ^~~
$

That alone should be enough to tell you that you're doing it wrong. 仅此一项就足以告诉您您做错了。 I had to suppress my usual compilation flags; 我不得不取消通常的编译标志。 the code is not acceptable to me because it has that compiler warning. 该代码对我来说是不可接受的,因为它具有该编译器警告。 However, when run, the output is: 但是,运行时,输出为:

5
9

Why? 为什么? Well, it is officially undefined behaviour, but … 好吧,这是官方未定义的行为,但是...

The layout of the array in memory looks like: 数组在内存中的布局如下:

╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗
║ 1 ║ 2 ║ 3 ║ 4 ║ 5 ║ 0 ║ 9 ║ 8 ║ 7 ║ 6 ║
╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝

When, despite its warnings, the array is passed to arf() , the function thinks that row 0 of the input array starts with the element containing 1 , and row 1 of the input array starts with the element containing 3 , and so on — because you told the compiler that the function takes an array with 2 elements per row. 尽管有警告,但仍将数组传递给arf() ,函数认为输入数组的第0行从包含1的元素开始,输入数组的第1行从包含3的元素开始,依此类推—因为您告诉编译器该函数采用一个数组,每行包含2个元素。

When you misuse subscript 4 (the declaration of the argument says that the valid second subscripts are 0 and 1 ), then it adds 4 to the start address of the row, and ends up printing 5 in the first case; 当您滥用下标4时(该参数的声明说第二个有效的下标为01 ),则它将4添加到该行的起始地址,并在第一种情况下最终打印5 it prints 9 in the second case because the counting starts from the element containing 3 (0, 1, 2, 3, 4 elements later is the 9 ). 它在第二种情况下输出9 ,因为计数从包含3的元素开始(0、1、2、3、4个元素之后是9 )。

If your compiler wasn't warning you about the type mismatch, you need to get a better compiler. 如果您的编译器没有警告您类型不匹配,则需要获得更好的编译器。 If your compiler was warning you about the type mismatch, you need to pay attention to your compiler. 如果您的编译器警告您类型不匹配,则需要注意您的编译器。 At this stage in your programming career, if the compiler deigns to warn you about your code, it has spotted a bug you need to fix. 在您编程生涯的现阶段,如果编译器发出警告您代码的警告,则表明您需要修复一个错误。 I still regard compiler warnings like that — and I normally compile with options to enforce my rule ( gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes ) — but I've only been coding in C for nearly 35 years, so I know there's more for me to learn. 我仍然认为像这样的编译器警告-而且我通常使用选项来执行我的规则( gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes ),但我只是使用C语言进行编码已有将近35年的时间,因此我知道还有很多东西需要学习。

You also need to ensure you're compiling in C11 mode (the current standard), or in C99 (the old standard) — there is no real excuse for compiling in C90 (the archaic standard) or pre-standard modes. 您还需要确保以C11模式(当前标准)或C99(旧标准)进行编译-在C90(陈旧标准)或预标准模式下进行编译没有真正的借口。 C99 said main() on its own is outdated because there is no return type. C99说main()本身已经过时,因为没有返回类型。 Always specify the return type explicitly, preferably using int main(void) when you ignore the argument list, or int main() if you insist (there are examples in the standard that use that notation). 始终明确指定返回类型,最好在忽略参数列表时使用int main(void) ;如果您坚持要使用int main() (标准中有使用该表示法的示例)。

The behavior of casting a multidimensional array to another array the same size but with different geometry is not undefined: the standard guarantees that the elements of a multidimensional array will be laid out contiguously, in row-major order. 将多维数组转换为相同大小但具有不同几何形状的另一个数组的行为并非没有定义:该标准保证多维数组的元素将按行优先顺序连续放置。

If you want to get the compiler to change the geometry of the array, you can cast to a pointer of the desired dimensions and then dereference, as in: 如果要让编译器更改数组的几何形状,可以将其强制转换为所需尺寸的指针,然后取消引用,如下所示:

int main(void)
{
  const int a[][5] = { { 1, 2, 3, 4, 5 }, { 0, 9, 8, 7, 6 } };
  arf(*(const int (*const)[][2])a);
}

The use of the other subscript, ie casting to an array of dimensions [5][2] (or [sizeof(a)/sizeof(int[2])][2] for a bit more resiliency) is to tell the compiler how many rows there are, so it can potentially catch bounds errors at compile time. 使用其他下标,即强制转换为尺寸为[5][2]的数组(或[sizeof(a)/sizeof(int[2])][2]以获得更大的弹性)是为了告诉编译器有多少行,因此它有可能在编译时捕获边界错误。 In this specific example, where the information is just thrown away on the receiving end, that would not do you any good, but C also lets you declare a function prototype like void arf( const ptrdiff_t m, const ptrdiff_t n, const int a[m][n]) . 在此特定示例中,信息只是在接收端丢弃,这对您没有任何好处,但是C还可让您声明一个函数原型,例如void arf( const ptrdiff_t m, const ptrdiff_t n, const int a[m][n]) C++ does not, but still lets you declare void arf(const int a[rows][cols]) , where rows and cols are constexpr values. C ++没有,但是仍然允许您声明void arf(const int a[rows][cols]) ,其中rowscolsconstexpr值。

Note that you should always, always , always check your array bounds for overflows in C and C++. 请注意,您应该始终, 总是 始终检查数组边界,以了解C和C ++中的溢出情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM