繁体   English   中英

在C中返回二维数组?

[英]Returning a two-dimensional array in C?

我最近开始编程C只是为了好玩。 我是桌面领域中C# .NETJava的非常熟练的程序员,但这对我来说有点太过挑战了。

我试图做一些“简单”的事情,如从函数返回一个二维数组。 我已经尝试过在网上进行研究,但我很难找到有用的东西。

这是我到目前为止所拥有的。 它没有完全返回数组,它只填充一个。 但即使这样也无法编译(我相信如果你是一名熟练的C程序员,原因必须是显而易见的)。

void new_array (int x[n][n]) {
  int i,o;

  for (i=0; i<n; i++) {
      for (o=0; o<n; o++) {
        x[i][o]=(rand() % n)-n/2;
      }
  }

  return x;
}

用法:

int x[n][n];
new_array(x);

我究竟做错了什么? 应该提到的是, n是一个值为3的常量。

编辑 :尝试定义常量时,这是一个编译器错误: http//i.imgur.com/sa4JkXs.png

C不像大多数语言那样处理数组; 如果要在C中使用数组,则需要了解以下概念。

除了当它是的操作数sizeof或一元&操作员,或者是一个字符串被用来初始化一个声明另一个数组,类型“的N元件阵列的表达 T将被转换(“衰变”)”,以表达式“指向T指针”,表达式的值将是数组的第一个元素的地址。 这个结果不是左值; 它不能是赋值的目标,也不能是++--运算符的操作数。

这就是为什么你不能定义一个函数来返回一个数组类型; 数组表达式将转换为指针类型作为return语句的一部分,此外,无论如何都无法将结果分配给另一个数组表达式。

信不信由你,这有一个坚实的技术原因; 当他最初开发C语言时,Dennis Ritchie从B编程语言中借用了很多概念。 B是一种“无类型”的语言; 一切都存储为无符号字或“单元格”。 记忆被视为“细胞”的线性阵列。 当您将数组声明为

auto arr[N];

B将为数组内容留出N个“单元”,以及绑定到arr的附加单元以将偏移存储到第一个元素(基本上是指针,但没有任何类型语义)。 数组访问定义为*(arr+i) ; 你从存储在a的地址偏移i单元格并取消引用结果。 这对C来说很有用,直到Ritchie开始在语言中添加结构类型。 他希望结构的内容不仅用抽象术语描述数据,而且用于物理地表示位。 他使用的例子是这样的

struct {
  int node;
  char name[14];
};

他想为节点留出2个字节,紧接着是name元素的14个字节。 并且他想要布置一系列这样的结构,使得你有2个字节后跟14个字节,接着是2个字节,接着是14个字节,等等。他无法找到一个处理数组指针的好方法,所以他彻底摆脱了它。 C不是为指针设置存储空间,而是从数组表达式本身计算它。 这就是为什么你不能为数组表达式赋值; 没有什么分配

那么,如何从函数返回2D数组?

你没有。 可以返回指向 2D数组的指针 ,例如:

T (*func1(int rows))[N]
{
  T (*ap)[N] = malloc( sizeof *ap * rows );
  return ap;
}

这种方法的缺点是必须在编译时知道N

如果您使用的是支持可变长度数组的C99编译器或C2011编译器,则可以执行以下操作:

void func2( size_t rows, size_t cols, int (**app)[cols] ) 
{
  *app = malloc( sizeof **app * rows );
  (*app)[i][j] = ...;                   // the parens are necessary
  ...
 }

如果没有可用的可变长度数组,那么至少列维必须是编译时常量:

#define COLS ...
...
void func3( size_t rows, int (**app)[COLS] )
{ 
  *app = malloc( sizeof **app * rows );
  (*app)[i][j] = ...;
}

您可以将内存零碎分配到类似于2D数组的内容中,但行不一定是连续的:

int **func4( size_t rows, size_t cols )
{
  int **p = malloc( sizeof *p * rows );
  if ( p )
  {
    for ( size_t i = 0; i < rows; i++ )
    {
      p[i] = malloc( sizeof *p[i] * cols );
    }
  }
  return p;
}

p 不是数组; 它指向一系列指向int的指针。 出于所有实际目的,您可以像使用2D数组一样使用它:

 int **arr = foo( rows, cols );
 ...
 arr[i][j] = ...;
 printf( "value = %d\n", arr[k][l] );

请注意,C没有任何垃圾回收; 你有责任清理你自己的混乱。 在前三种情况下,它很简单:

int (*arr1)[N] = func(rows);
// use arr[i][j];
...
free( arr1 );

int (*arr2)[cols];
func2( rows, cols, &arr2 );
...
free( arr2 );

int (*arr3)[N];
func3( rows, &arr3 );
...
free( arr3 );

在最后一种情况下,由于您执行了两步分配,因此需要执行两步重新分配:

int **arr4 = func4( rows, cols );
...
for (i = 0; i < rows; i++ )
  free( arr4[i] )
free( arr4)

你的函数返回void ,所以return x; 线是多余的。 除此之外,您的代码看起来很好。 也就是说,假设你在某个地方有#define n 3而不是const int n = 3;

您不能以C,多维或其他方式返回数组。

主要原因是语言说你做不到。 另一个原因是通常本地数组在堆栈上分配,并因此在函数返回时解除分配,因此返回它们没有意义。

将指针传递给数组并修改它通常是要走的路。

要返回(指向)编译时已知的新创建的维数组,可以执行以下操作:

#define n 10 // Or other size.

int (*new_array(void))[n]
{
    int (*x)[n] = malloc(n * sizeof *x);
    if (!result)
        HandleErrorHere;

    for (int i = 0; i < n; ++i)
        for (int o = 0; i < n; ++o)
            x[i][o] = InitialValues;

    return x;
}

…
// In the calling function:
int (*x)[n] = new_array();

…
// When done with the array:
free(x);

如果在编译时未知大小,则甚至无法返回指向数组的指针。 C确实支持可变长度数组,但不支持返回类型的函数。 您可以通过参数返回指向可变长度数组的指针。 这需要使用一个参数,该参数是指向可变长度数组的指针,因此它有点混乱。

此外,在动态调用调用程序中的数组,在调用程序中自动分配数组,在被调用函数中动态分配数组以及使用可变长度数组或固定长度数组甚至是带有手动索引的一维数组之间的首选取决于上下文,包括数组的大小,它将存在多长时间以及您打算使用它的操作。 因此,您需要在提出具体建议之前提供其他指导。

在C中,只有通过值/返回值(没有通过引用传递)。 因此,传递数组(通过值)的唯一方法是将其地址传递给函数,以便它可以通过指针操作它。

但是,按值返回数组的地址是不可能的,因为当时间控制到达调用者时,函数超出范围并且其自动变量也随之下降。 因此,如果你真的需要,你可以动态分配数组,填充并返回它,但首选的方法是传递数组并留下维护数组给调用者的责任。

至于错误,我在GCC中得到的唯一警告就是warning: 'return' with a value, in function returning void ,这只是意味着你不应该从void函数返回任何东西。

void new_array (int x[n][n]); 你在这里真正做的是获取一个指向n个整数数组的指针; 衰减的类型是int (*x)[n] 这是因为数组通常会衰减为指针 如果你在编译时知道n ,也许最好的方法是:

#define n 3
void new_array (int (*x)[n][n]) {
  int i,o;

  for (i=0; i<n; i++) {
    for (o=0; o<n; o++) {
      x[i][o]=(rand() % n)-n/2;
    }
  }
}

并称之为

int arr[n][n];
new_array(&arr);

如果将它们包装在结构中,您可以像任何其他变量一样传递任意维度数组:

#include <stdio.h>

#define n 3

struct S {
  int a[n][n];
};


static struct S make_s(void)
{
  struct S s;

  int i, j;
  for (i = 0; i < n; i++) {
    for (j = 0; j < n; j++)
      s.a[i][j] = i + j;
  }

  return s;
}

static void print_s(struct S s)
{
  int i, j;
  for (i = 0; i < n; i++) {
    for (j = 0; j < n; j++)
      printf(" %d", s.a[i][j]);
    printf("\n");
  }
}

int main(void) {
  struct S s;

  s = make_s();
  print_s(s);

  return 0;
}

您可能将n声明为常量整数:

const int n = 3;

相反,您应该将n定义为预处理器定义:

#define n 3

暂无
暂无

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

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