在C中返回二维数组?

时间:2014-01-27 21:08:01

标签: 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

7 个答案:

答案 0 :(得分:6)

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

除非它是sizeof或一元&运算符的操作数,或者是用于初始化声明中的另一个数组的字符串文字,否则表达式为类型“{元素数组T”将被转换(“衰减”)为“指向T的指针”类型的表达式,表达式的值将是第一个元素的地址数组。这个结果不是左值;它不能是赋值的目标,也不能成为++--运算符的操作数。

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

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

auto arr[N];

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

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

他想为节点留出2个字节,紧接着是14个字节的name元素。并且他想要布置一系列这样的结构,使得你有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)

答案 1 :(得分:4)

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

答案 2 :(得分:4)

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

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

将指针传递给数组并进行修改通常是可行的方法。

答案 3 :(得分:3)

要返回(指向)编译时已知的新创建的维数组,您可以这样做:

#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确实支持可变长度数组,但不支持返回类型的函数。您可以通过参数返回指向可变长度数组的指针。这需要使用一个参数,该参数是指向可变长度数组的指针,因此它有点混乱。

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

答案 4 :(得分:2)

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

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

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

void new_array (int x[n][n]);你在这里真正做的是获取一个指向n个整数数组的指针;腐朽的类型是int (*x)[n]。发生这种情况的原因通常是arrays decay into pointers。如果您在编译时知道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);

答案 5 :(得分:2)

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

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

答案 6 :(得分:1)

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

const int n = 3;

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

#define n 3