在C中返回二维数组?

Mat*_*zen 7 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;
}
Run Code Online (Sandbox Code Playgroud)

用法:

int x[n][n];
new_array(x);
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?应该提到的n是具有该值的常量3.

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

Joh*_*ode 7

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

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

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

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

auto arr[N];
Run Code Online (Sandbox Code Playgroud)

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

struct {
  int node;
  char name[14];
};
Run Code Online (Sandbox Code Playgroud)

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

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

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

T (*func1(int rows))[N]
{
  T (*ap)[N] = malloc( sizeof *ap * rows );
  return ap;
}
Run Code Online (Sandbox Code Playgroud)

这种方法的缺点是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
  ...
 }
Run Code Online (Sandbox Code Playgroud)

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

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

您可以将内存零碎分配到类似于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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

请注意,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 );
Run Code Online (Sandbox Code Playgroud)

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

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