C语言 函数中int* a和int(*a)[N]的区别?

cclgggtu  于 2022-12-11  发布在  其他
关注(0)|答案(3)|浏览(186)

我可以将int数组传递给这两个函数(一维数组和二维数组)。它们之间的区别是什么?对于第二个函数,你需要指定数组的大小。为什么?

void foo(int *a, int cols) 
void bar(int (*a)[N])

我有一个程序,我想把二维整型数组传递给函数。哪一个更好用,有关系吗?

0h4hbjxa

0h4hbjxa1#

  • Difference between int* a and int (*a)[N] in functions?*

While int* a is a pointer to int , int (*b)[N] is a pointer to an array of Nint s. These are different types, and are not compatible.
b can only point to an array of Nint s while a can only point to an int wether it's an array or not, and can't (souldn't) point to b . When you pass an array as argument you are really passing a pointer to its first element. Another difference is that if you increment b it will point to the next block of Nint s whereas if you increment a it will just point to the next int .

  • I have a program where I want to pass 2d int arrays to functions. Which is the better one to use and does it matter?*

It matters, for foo , it hints to a flat array with cols width, of course with some arithmetic you can treat it as a 2D array, bar is a more natural use for a 2D array.
The memory layout will probably be the similar, and using a flat array to store elements in such a way that you can use it as a 2D array is perfectly fine. I personally find it less messy and more clear to use a pointer to array, instead of pointer to int , when I need a 2D array.
Example:

#include <stdio.h>
#include <stdlib.h>
#define N 5
#define M 5
void bar(int (*a)[N]) {
    
    // prints 20, 4 bytes * 5 (can be different deppending on the size of int)
    printf("Size of a is %zu\n\n", sizeof *a);

    // populate the array
    int c = 1;
    for (int i = 0; i < N; i++)
        for (int j = 0; j < M; j++) 
            a[i][j] = c++;

    // will print the initial element on each array line
    for(int i = 0; i < N; i++){ 
        printf("a[%d][0] = %2d\n", i, **a);   
        a++;
    } 
    putchar('\n');     
}
int main() {

    int(*a)[N] = malloc(sizeof *a * M);
    bar(a);

    // prints the complete array
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            printf("%2d ", a[i][j]);
        }
        putchar('\n');
    }
    free(a);
}

In this code a is a 2D array N x M which is passed to bar to be populated. I added some handy prints and comments for clarification. See it live: https://godbolt.org/z/zhKcb96ob
Output:

Size of a is 20

a[0][0] =  1
a[1][0] =  6
a[2][0] = 11
a[3][0] = 16
a[4][0] = 21

 1  2  3  4  5 
 6  7  8  9 10 
11 12 13 14 15 
16 17 18 19 20 
21 22 23 24 25
hmae6n7t

hmae6n7t2#

对于二维矩阵,这两种方法之间存在显著差异

  • 在第一种方法中,您传递了一个指向int的指针,即使在隐式衰减之后,它也不是矩阵的正确类型。
  • cols在第一个原型中是int参数,而在第二个原型中是常数N。在这两种情况下,都没有指定列数,所以它必须是隐含的,因为它是常数或因为矩阵是方形的。
  • 在第一种情况下,您需要显式地编写索引计算,而在第二种情况下,您可以使用a[row][col],这将更简单、更可读,并编译为更高效的代码。

请注意,从C99开始,还有第三种可能性允许您使用数组语法,即使是对于可变数量的列也是如此。下面是一个示例:

#include <stdio.h>
#include <stdlib.h>

void init_matrix(int rows, int cols, int (*a)[cols]) {
    int x = 0;
    for (int r = 0; r < rows; r++) {
        for (int c = 0; c < cols; c++) {
            a[r][c] = x++;
        }
    }
}

void print_matrix(int rows, int cols, const int (*a)[cols]) {
    for (int r = 0; r < rows; r++) {
        for (int c = 0; c < cols; c++) {
            printf("%3d%c", a[r][c], " \n"[c == cols - 1]);
        }
    }
}

int main() {
    int cols = 4, rows = 3;
    int (*a)[cols] = malloc(sizeof(*a) * rows);
    init_matrix(rows, cols, a);
    print_matrix(rows, cols, a);
    return 0;
}
ki1q1bka

ki1q1bka3#

第二个函数需要指定数组的大小。为什么?
您不必指定整个2D数组的整个大小,但必须指定列数,即子数组的大小。
编译器必须知道子数组大小的原因是偏移量计算需要此信息。
例如,如果定义一个2D数组,如下所示

int arr[2][4];

则数组元素将按以下顺序存储在存储器中:

arr[0][0]
arr[0][1]
arr[0][2]
arr[0][3]
arr[1][0]
arr[1][1]
arr[1][2]
arr[1][3]

如果编译器想要例如访问arr[1][2],则它将需要知道每行有多少列,即每个子数组有多大。如果编译器不知道每行有4列,则编译器没有办法知道在哪里找到arr[1][2]。然而,如果编译器知道每行有4列,它将知道2D阵列的第5个元素是第二行的开始,因此它将知道arr[1][2]是2D阵列的第7个元素。
我可以将int数组传递给这两个函数(一维和二维数组)。
虽然在大多数编译器上可以将2D数组作为1D数组访问,但根据ISO C标准,它是undefined behavior。有关详细信息,请参阅以下问题:
One-dimensional access to a multidimensional array: is it well-defined behaviour?
哪一个更好用,有关系吗?
第一种方法更好,因为它允许您在运行时指定列数,而第二种方法必须在编译时设置列数,因此灵活性较差。
但是,如上所述,根据您的编译器,将2D数组作为1D数组访问可能不安全。因此,最好将1D数组传递给第一个函数,将2D数组传递给第二个函数。
但是,第二个函数如何知道二维数组包含多少行并不清楚,因为该信息没有作为参数传递给函数。也许函数假设的行数是固定的,或者它根本不用于二维数组。您在问题中提供的信息不足以回答这个问题。

相关问题