返回一个二维数组(在 C# 和 C++ 中)

Posted

技术标签:

【中文标题】返回一个二维数组(在 C# 和 C++ 中)【英文标题】:returning a 2D array (in C# and in C++) 【发布时间】:2018-01-17 01:05:46 【问题描述】:

我正在编写一个返回二维数组的函数。这让我想到了这在 C#(带有垃圾收集)和 C++(无 GC)中的含义

(为什么两者兼有——你可能会问:我现在使用 C# 在 Windows 平台上编写它,但几个月后我将使用 C++ 在嵌入式设备上实现我的算法)

所以基本上我有一个二维数组说表,并通过一个函数分配给它返回值。我的问题是:保存原始表的原始内存会发生什么?

现在看代码: 在 C# 中

    using System;

    public class Test
    
        public static void Main()
        
            int[,] table= new int [10,10];  //Here some memory is separated for table 
            int[,] table= createTable(10,10); //Here the return value of createTable is assigned to the original value 

//WHAT HAPPENED TO THE ORIGINAL MEMORY THAT table HAD?

            printTable(table,10,10); //disregard this. Not that important
        

        public static int[,] createTable(int rows, int columns)
        
            int[,] t = new int[rows,columns];
            for(int i=0;i<rows;i++)
              for(int j=0;j<columns;j++)
                 t[i,j]=(i+j);

             return t;    
        

        public static void printTable(int[,]t, int rows, int columns)
        
            for(int i=0;i<rows;i++)
              for(int j=0;j<columns;j++)
                Console.WriteLine(t[i,j]);

             foreach( var im in t)
               Console.WriteLine(im);
        
    

(请不要告诉我第一个new int不是必须的,等问题是必须的,可以通过两次调用createTable来代替)

我猜在 C# 中,垃圾收集器会处理这个问题,我不必担心?


现在在 C++ 中

#include <cstdio>
#include <cstdlib>


int** createTable(int rows, int columns)
    int** table = new int*[rows];
    for(int i = 0; i < rows; i++) 
        table[i] = new int[columns]; 
        for(int j = 0; j < columns; j++) table[i][j] = (i+j); // sample set value;    
    
    return table;

void freeTable(int** table, int rows)
    if(table)
        for(int i = 0; i < rows; i++) if(table[i]) delete[] table[i];  
        delete[] table;    
    

void printTable(int** table, int rows, int columns)
    for(int i = 0; i < rows; i++)
        for(int j = 0; j < columns; j++)
            printf("(%d,%d) -> %d\n", i, j, table[i][j]);
            
    

int main(int argc, char** argv)

    int** table = createTable(5, 5);
    table = createTable(10,10);
    printTable(table, 10, 10);
    freeTable(table, 10);
    return 0;

table (5x5) 保留的原始内存发生了什么变化。当表被分配 10x10 表时,它会造成内存泄漏吗?如何避免这种情况?

【问题讨论】:

请记住,指针不是数组,指向指针的指针也不是“二维数组”。而且你不能在 C++ 中返回数组。这是语言的限制。 这看起来更像是一种 C 风格的方法,而不是 C++。也许在 C++ 中使用智能指针会解决你的问题。 使用 Raii 对象(如vector&lt;vector&lt;int&gt;&gt;)来避免担心内存释放。 createTable 函数本身已经存在潜在的泄漏,因为每个new 都可能抛出。请不要基于观察到一些类似的语法在另一种语言中工作的观察来尝试编写 C++,这会使您得到非常糟糕的结果。如果您想编写 C++,请从一本好书系统地学习该语言。 附带说明,如果这应该实现矩阵而不是数组数组,请不要使用嵌套向量,而是编写适当的矩阵类来正确保证不变量,例如矩阵的矩形形式。在 eigen 等库中也有许多很好的免费矩阵实现。 【参考方案1】:

在 C# 中,垃圾收集器将处理不再可访问的内存。

我用 gcc 编译了你的 C++ 程序(版本:(Debian 6.4.0-1)6.4.0 20170704 ) 在 Debian Buster 上。

valgrind 是一个非常有用的检查各种内存问题的工具,它给了我下面的valgrind --leak-check=full ./test 输出:

Memcheck, a memory error detector
Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
Command: ./test

[... program output ...]

HEAP SUMMARY:
    in use at exit: 140 bytes in 6 blocks
  total heap usage: 19 allocs, 13 frees, 74,348 bytes allocated

140 (40 direct, 100 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 2
   at 0x4C2C97F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
   by 0x10883E: createTable(int, int) (in /home/user/code/test/test)
   by 0x108A26: main (in /home/user/code/test/test)

LEAK SUMMARY:
   definitely lost: 40 bytes in 1 blocks
   indirectly lost: 100 bytes in 5 blocks
     possibly lost: 0 bytes in 0 blocks
   still reachable: 0 bytes in 0 blocks
        suppressed: 0 bytes in 0 blocks

For counts of detected and suppressed errors, rerun with: -v
ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

如您所见,您肯定会失去记忆。 要么先调用你的 freeTable() 函数,然后重新分配你的指针,要么使用smart pointers 来解决这个问题。

【讨论】:

【参考方案2】:

对于 C++ 部分,是的,您会造成内存泄漏。您必须调用 freeTable:

int** table = createTable(5, 5);
freeTable(table, 5);
table = createTable(10,10);
printTable(table, 10, 10);
freeTable(table, 10);

最好将 Raii 对象用作vector&lt;vector&lt;int&gt;&gt;(或专用类Matrix)以避免担心内存释放(您也可以在类中添加大小信息)。

所以它只是:

auto table = createTable(5, 5);
table = createTable(10,10);
printTable(table);

【讨论】:

vector&lt;vector&lt;int&gt;&gt; 是一个糟糕的矩阵数据结构,即使您根本不关心性能。它甚至不保证所有行都具有相同的长度。 为什么使用向量可以避免我造成泄漏? @BaummitAugen:我同意,但 std 没有 Matrix 类,所以如果不实现一个(或使用外部库),更快/更简单的方法是向量的向量。 @KansaiRobot:因为vector是一个Raii对象,它可以正确地释放内部内存。 @Jarod42 嗯。要么自己写一个小程序,这并不难,也不需要太多工作,或者,如果你需要任何重要的东西,只需使用一个库。像vector&lt;vector&lt;int&gt;&gt; 这样的半途而废的解决方案并不构成良好且可维护的代码库。【参考方案3】:

在这一行:

//WHAT HAPPENED TO THE ORIGINAL MEMORY THAT table HAD?

您正在覆盖它的引用。由于原表不再被引用,垃圾回收一旦运行就会清理使用的空间。

我无法回答你关于 C++ 的问题,很抱歉。

【讨论】:

以上是关于返回一个二维数组(在 C# 和 C++ 中)的主要内容,如果未能解决你的问题,请参考以下文章

将二维数组从 C# 传递到 C++

C# 中锯齿状数组的内存分配与 C++ 中的二维数组内存分配

如何将二维数组从 C# 传递到 C++?

c++返回二维数组的最大元素和它所在的行、列号?

使用 C++/CLI 包装器将二维数组从 C# 传递到非托管 C++

将二维双数组从 C# 传递到 C++ 函数