【问题标题】:How to deallocate 2d array?如何释放二维数组?
【发布时间】:2014-09-24 04:11:19
【问题描述】:

这方面已经有很多东西了。他们似乎都暗示你需要做的就是每次调用 malloc 时都调用 free ,并且可能最后释放“指针的指针” - 所以你最终不会在释放后引用该内存

例如,如果我有

int** myPtr

那我只需要

free(myPtr[j])

对于每一个j,在最后释放指针的指针之前

free(myPtr)

酷。我在我的程序中已经这样做了几次,它似乎工作得很好——除了一种情况。我试图弄清楚这个案例有什么不同,并且我能够在一个最小的程序中复制这个症状。

#include "stdafx.h"
#include <stdlib.h>

signed char** getPtr()
{
    unsigned char n = 10;
    unsigned char k = 2;

    signed char** myPtr = (signed char**)malloc(n*sizeof(signed char));
    if (myPtr == NULL) {return NULL;}

    for (unsigned char i = 0; i < n; i++)
    {
        myPtr[i] = (signed char*)malloc(k*sizeof(signed char)); 
        if (myPtr[i] == NULL) {return NULL;}
    }

    for (unsigned char j = 0; j < n; j++)
    {
        for (unsigned char k = 0; k <= 1; k++)
        {
            myPtr[j][k] = 42; 
        }
    }

    return myPtr; 

}

int _tmain(int argc, _TCHAR* argv[])
{
    signed char** potato = getPtr();
    if (potato != NULL)
    {
        for (unsigned char j = 0; j < 10; j++)
        {
            free(potato[j]);
        }
        free(potato); 
    }
    return 0;
}

【问题讨论】:

  • 这是一个 C 问题,还是一个 C++ 问题。这是两种不同的语言,正确答案取决于这种差异。
  • 我目前正在处理一个由 C++ 文件组成的项目,但如果 C 的答案不同,我也想知道。
  • 在 C++ 中最好不要使用 malloc 系列函数,有更好的选择。
  • 选择一种语言并坚持下去。如果您使用的是 C++,那么 malloc 系列函数或数组就是完全错误的。获得一本好的 C++ 书籍。我想知道人们从哪里不断获得这样的想法,即在 C++ 中使用数组或 malloc 是可以的,或者“C/C++”是一个可以大声呼喊的实体。它是由非法街头经销商兜售的还是什么?
  • @n.m.蹩脚和过时的教育系统是我的猜测

标签: c arrays pointers


【解决方案1】:

您的直接问题如下:

signed char** myPtr = (signed char**)malloc(n*sizeof(signed char));

这是错误的,因为它为n 字符分配了足够的空间。相反,它应该是:

signed char** myPtr = (signed char**)malloc(n*sizeof(signed char*));
//                                                              ^
//                                                      char POINTER

char 的大小通常小于指针的大小,但即使不是,您也应该使用正确的类型。事实上,我更喜欢下面的形式,这样我就不用重复类型了(重复有可能我将来可能会改变一个,而忘记另一个):

signed char** myPtr = (signed char**) malloc (n * sizeof (*myPtr));

除此之外,您还应该考虑您实际编码的语言。虽然 C 和 C++ 共享相当多的基本内容,但它们绝不是同一种语言,您如何编写代码取决于您的目标是哪一种。

让我们首先介绍 C。在那种语言中,您不应该从malloc 转换返回值,因为它可以隐藏某些调试起来很有趣的细微错误。放弃演员会给你:

signed char **myPtr = malloc (n * sizeof (*myPtr));

此外,由于sizeof (char)(裸、有符号和无符号)始终为一,因此,每当您看到自己乘以它时,放弃乘法会更容易。将其与之前的指南相结合,您可以转向:

myPtr[i] = (signed char*)malloc(k*sizeof(signed char));

进入更具可读性:

myPtr[i] = malloc (k);

现在,如果您正在为 C++ 编码,还有其他事情需要研究。

在 C++ 中,使用语言的 newdelete 方面而不是像 mallocfree 这样的传统 C 语言更容易接受。您已经提到的准则(确保每个 new 都有一个 delete)仍然有效,但是在释放 数组 项时确保使用 delete[] 会增加复杂性。

这并不是说mallocfree 不起作用;它们仍然是 C++ 语言规范的重要组成部分。只是 C++ 做事的方式可以更强大,因为它可以一次性分配对象,并在内存不足时引发异常,这意味着你没有用NULL 检查来点缀你的代码。

此外,C++ 在其标准库中提供了大量集合,这使得处理事物组比使用原始数组更容易。其中之一是vector,基本上是一个可调整大小的数组,您应该考虑在以前可能使用过数组的情况下使用它。

您展示的代码将更容易实现为字符串向量(C++ 字符串,而不是那些微不足道的 C 字符串)。而且,事实上,我会考虑更进一步,将整个数据结构封装到它自己的类中,完全隐藏它使用向量的事实。

如果您这样做,并提供您需要的所有 setter/getter 方法,您可以在以后替换整个底层数据结构 而不会影响使用它的代码。

【讨论】:

  • @user2864293,如果您没有包含正确的标题并且您的指针与您的整数大小不同,您可能会得到意想不到的结果。
  • 正确的 C++ 方法是“看在 Pete 的份上,现在是 2014 年,已经使用 std::vector”,我厌倦了在所有其他包含“数组”的 C++ 问题下留下这条评论。我想我可以找到一个对此效果有公认答案的好问题,然后将它们全部关闭为重复项,这是正确的举动吗?
  • 回答这样的问题有两个不幸的问题。使用 std::vector 的建议不会回答有关 malloc 的问题,这违反了本网站的规则。关于 malloc 的答案将使 malloc 的使用永久化。看?不管我怎么回答,我只会让事情变得更糟。所以我评论一下,但是 cmets 并不是很有效。
  • 这就是为什么留下这样的 cmets 有用的:它导致了这个答案的改进!
  • @Lightness:英语!我想我被冒犯了。不,不是真的,浏览你的编辑,我几乎为它那么糟糕而感到羞耻。谢谢:-)
【解决方案2】:

您没有为指向指针的指针分配足够的空间。 这个:

signed char** myPtr = (signed char**)malloc(n*sizeof(signed char));

应该是:

signed char** myPtr = malloc(n*sizeof(signed char*));

如果malloc 失败,您也会在此处泄漏内存:

for (unsigned char i = 0; i < n; i++)
{
    myPtr[i] = (signed char*)malloc(k*sizeof(signed char)); 
    if (myPtr[i] == NULL) {return NULL;}
}

我有点假设您希望此代码的 C 版本不能完全移植到 C++。对于您真正想做的事情,在 C++ 中可能有更合适的方式来完成相同的事情。

【讨论】:

  • @AlexReynolds 这个问题也被标记为 c++
  • @AlexReynolds,如果这是在 both C++ 和 C 上运行,就像 OP 对语言标签一样?
  • 它可能不应该被标记为 C++。
【解决方案3】:

您需要为signed charn 指针 分配空间,而不是为n signed char 分配空间:

signed char** myPtr = (signed char**)malloc(n*sizeof(signed char*));
                                                               ^^^

【讨论】:

    【解决方案4】:

    你应该使用:

    signed char** myPtr = (signed char**)malloc(n*sizeof(signed char*));
                                                                    ^^ The missing *
    

    而不是

    signed char** myPtr = (signed char**)malloc(n*sizeof(signed char));
    

    否则,您没有为指针分配足够的内存。

    当然,你不应该转换malloc的返回值。众所周知,它会导致问题。详情请见Specifically, what's dangerous about casting the result of malloc?

    用途:

    signed char** myPtr = malloc(n*sizeof(signed char*));
    

    【讨论】:

      【解决方案5】:

      getPtr你:

      // 1 byte
      malloc n*sizeof(signed char)
      

      但是你存储了一个指向有符号字符的指针,它是 32 位或 64 位,4 或 8 个字节。

      您可以使用以下方法修复它:

      signed char*  zz;
      signed char** myPtr = (unsigned char)malloc(n*sizeof(zz));
      

      【讨论】:

        【解决方案6】:

        最简单的方法是使用真正的二维数组,而不仅仅是指向数组的指针数组。你可以像这样分配你的二维数组:

        signed char (*myPtr)[k] = malloc(n*sizeof(*myPtr));
        

        使用此分配,您不需要循环到malloc() 线阵列。你可以像这样直接使用你的二维数组:

        for (unsigned char j = 0; j < n; j++)
        {
            for (unsigned char i = 0; i < k; i++)
            {
                myPtr[j][i] = 42; 
            }
        }
        

        请注意,访问数组元素的方式没有变化,即使内存布局有很大不同。但是,由于您在一个大块中分配了内存,您也可以在一次 free() 调用中释放它:

        free(myPtr);
        

        学习 C 的指针数组语法真的很值得。

        【讨论】:

          猜你喜欢
          • 2011-08-05
          • 2015-05-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-01-08
          • 2020-01-18
          相关资源
          最近更新 更多