返回指向数组的指针的函数

3

我设法在C中成功使用可变长度数组,现在我有以下内容:

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

int (*foo(size_t row, size_t col))[3];

int main(void){
    size_t row, col;


    printf("Give the ROW: ");
    if ( scanf("%zu",&row) != 1){
        printf("Error, scanf ROW\n");
        exit(1);
    }

    printf("Give the COL: ");
    if ( scanf("%zu",&col) != 1){
        printf("Error, scanf COL\n");
        exit(2);
    }

    int (*arr)[col] = foo(row, col);

    for ( size_t i = 0; i < row; i++){
        for( size_t j = 0; j < col; j++){
            printf("%d ",*(*(arr+i)+j));
        }
    }

    free(arr);
}


int (*foo(size_t row, size_t col))[3]{
    int (*arr)[col] = malloc(row * col * sizeof(int));
    int l=0;

    if (arr == NULL){
        printf("Error, malloc\n");
        exit(3);
    }

    for ( size_t i = 0; i < row; i++){
        for( size_t j = 0; j < col; j++){
            *(*(arr+i)+j) = l;
            l++;
        }
    }

    return arr;
}

输出:

Give the ROW: 2
Give the COL: 5
0 1 2 3 4 5 6 7 8 9

现在是这样的:
int (*foo(size_t row, size_t col))[3]{ /* code */ }

意思是,如果我理解正确,这将声明foo为一个带有两个参数(size_t row,size_t col)的函数,该函数返回指向int数组3的指针。
我不完全能理解这种函数,现在使用变长数组时更加复杂,因为大小只在运行时知道,但我认为这是一件好事。我仅使用C11标准。
无论如何,在这里,int (*foo(size_t row, size_t col))[3]中,我有这个[3],我不清楚它是如何工作的,以及如何可以在运行时使其成为可能(当然只有在可能的情况下),例如int (*foo(size_t row, size_t col))[SIZE]
我读了一些关于C的书,但没有确切的解释关于这种情况,Google也没有帮助,所以我有两个问题:
1)是否可以实现int (*foo(size_t row, size_t col))[SIZE],其中SIZE必须是参数?或者我应该用另一种方式声明此函数?
2)这是我尝试的正确方法,还是有其他替代方案?
我只是尝试返回指向大小在运行时而不是编译时知道大小的数组的指针。一次调用malloc和free,这是一个很好的方法,因为每当调用malloc时,我们的程序会干扰内核来分配内存并标记页面为可写。因此,这种方法在内核上的负载较小。它可以用一个使用VLA的malloc编写。
编辑:
@ChronoKitsune说我应该尝试使用[](未指定大小的数组),在这种情况下,函数将变成这样:
int (*foo(size_t row, size_t col))[]{ /* code */ }

这是我应该使用的吗?


  1. 不是,但可以尝试使用[](未指定大小的数组)。
  2. 是的,您的语法是正确的。最相似的替代方案是返回一个普通指针。然后,您可以将arr声明为int *arr = foo(row, col);并将其用作arr[i * col + j](或*(arr + i * col + j)),或者您可以将返回值强制转换为与arr的声明匹配,如int (*arr)[col] = (int (*)[col])foo(row, col);以便能够使用arr[i][j](或*(*(arr + i) + j),因为您喜欢使用指针符号而不是更易读的等效数组符号)。
- user539810
@ChronoKitsune 我试过了,但不确定是否可以,所以才问这个问题。我也可以打 [0]。 :) 2) 我需要这个语法 int (*arr)[col] - Michi
@ChronoKitsune,我已经有了这个解决方案,但这不是我需要的。 - Michi
语法 [] 正式表示 不完整类型数组(C11 第6.7.6.2p4节)。返回指向该数组的指针是被允许的。但是,在指针赋值为具有完整类型的数组之前,不能解引用该指针。例如,如果您声明了 int (*arr)[] = foo(row, col);,则不能执行 *arrarr[...]int (*arr)[col] = foo(row, col); 是被允许的,您可以按照需要成功使用指针,因为 int (*arr)[col] 是一个指向完整类型的指针。 - user539810
@ChronoKitsune 这在我的情况下是可以的。 - Michi
1个回答

5

在这里,int (*foo(size_t row, size_t col))[3],我不明白[3]是如何工作的。

类型声明最容易从内向外阅读。我将从简单的开始,并逐步构建到您的示例。如果您写下:

int foo[3];

或者

int (foo)[3];

您在声明foo是一个由3个int组成的数组。这里的括号起到优先级分组的作用,就像表达式中一样,但在这两种情况下类型的解释方式相同,因此它们是不必要的。

如果您写

int (*foo)[3];

你正在声明foo是一个指向包含3个int的数组的指针。同样地,你可以理解为foo所指向的东西是一个包含3个int的数组,隐含着foo是一个指针。在这种情况下,括号是必要的;如果没有它们,你将声明foo是一个包含3个int *的数组。
如果你写:
int (*foo(size_t row, size_t col))[3];

你声明了foo是一个函数,它有两个类型为size_t的参数,并且返回一个指向一个由3个int组成的数组的指针。

如果可能,我该如何在运行时实现这一点(当然只有在可能的情况下),类似于int (*foo(size_t row, size_t col))[SIZE]

如果SIZE不是编译时常量,那么你不能这样做。根据C2011规定:

如果标识符被声明为可变形式类型,则应该[...]没有链接,并具有块作用域或函数原型作用域。[...]

换句话说,因为所有函数都具有文件作用域和内部或外部链接,函数返回类型不能是可变长度数组、可变长度数组的指针或任何此类类型(这些是“可变形式”类型)。
通常,在这种情况下,人们会返回一个指向数组的第一个元素的指针,而不是整个数组的指针。虽然指向的地址是相同的,但类型是不同的:
int *foo(size_t row, size_t col);

返回类型不包含指向数组的长度信息,但如果C允许函数返回可变修改类型,则无论如何都要依赖于一种机制,以便代码可以在任何特定上下文中知道变量维度。换句话说,如果您不知道预期的数组长度独立于函数的返回类型,则无论如何都无法使用可变修改的返回类型。

如果确实需要函数返回指向运行时确定数量元素的指针,并且还需要返回元素数量,则可以返回包含两者的结构体,或者可以通过指针参数返回一个或两个值。

更新

还可以声明函数返回指向未指定大小的数组的指针,如@ChronoKitsune所建议的。语法将是

int (*bar(size_t row, size_t col))[];

但是你会发现,几乎在所有方面都更难使用返回类型。例如,声明可以保存返回值的变量更加棘手和丑陋:

int (*array_ptr)[] = bar(x, y);

访问指向数组的元素:

int z = (*array_ptr)[1];

与此形成对比的是
int *ptr = foo(x, y);
int w = ptr[2];

你的意思是类似于这个吗?如果是,那不是我需要的。如果不是,能否请您提供一个演示,其中包含一个指向主函数中数组的指针和一个执行该操作的函数? - Michi
或者只使用(例如@ChronoKitsune指针)这个[](未指定大小的数组)是否可以?==> int (*func(size_t row, size_t col))[]; - Michi
@Michi,是的,你提供的IdeaOne示例就是我所描述的。是的,你可以使用ChronoKitsune建议的[]形式,但我认为你会发现在几乎所有方面,这种方式更难处理。 - John Bollinger
谢谢,我需要知道那是否合法。请提供您的答案。 - Michi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接