【问题标题】:Strange memory management issues in C++ (from a beginner, at least)C++ 中奇怪的内存管理问题(至少来自初学者)
【发布时间】:2011-01-20 18:06:11
【问题描述】:

我是 C++ 新手,我有很多 Objective-C 经验。

我试图在我的类中拥有一个 c 字符串数组(即char **)作为实例变量,它被分配并填充到我的构造函数中,然后在另一个成员函数中我想打印出来整个“网格”。

分配有效,我用字符串填充了我的数组(现在只是“aaaaaaa”等等)。检查构造函数的末尾,我看到每一行都已按预期成功创建和填充。

但是,我随后调用了我的 printGrid() 函数,然后事情变得奇怪了。如果我有 25 行要打印,比如说,前 12 行左右会打印垃圾,那么剩下的 13 行会按预期打印出来。所以好像我在某处践踏记忆,我不确定在哪里。

我的代码可能看起来有点乱,因为我一直在尝试不同的东西,所以我会尽量让它看起来有凝聚力。

main.cpp:我在哪里调用函数

#include <iostream>
#include "Bitmap.h"

using namespace std;
int main (int argc, char * const argv[]) {

    Bitmap bitmap(15, 25);
    bitmap.printBitmap();

    return 0;
}

Bitmap.h:我的班级的标题

class Bitmap {
private:
    char **_bitmap;
        void printLine(char const*lineString);
    int _width;
    int _height;
public:
    Bitmap();
        Bitmap(int width, int height);
    void printBitmap();
};

Bitmap.cpp:动作发生的地方

#include <iostream>
#include "Bitmap.h"

using namespace std;
Bitmap::Bitmap() {
    // allocate space for the bitmap
    int numRows = 20;
    int numColumns = 30;

    Bitmap(numRows, numColumns); // Can I even safely do this? I'm not using the default constructor in my main() but I'm still curious.
}

Bitmap::Bitmap(int width, int height) {
    _width = width;
    _height = height;

    _bitmap = (char **)malloc(sizeof(char*) * height); // FIXED this line (used to be char, now it's char *).
    for (int currentRow = 0; currentRow < height; currentRow++) {
        _bitmap[currentRow] = (char *)malloc((sizeof(char) * width));
        snprintf(_bitmap[currentRow], width, "%s", "1");

        for (int currentColumn = 0; currentColumn < width; currentColumn++) {
            _bitmap[currentRow] = strcat(_bitmap[currentRow], "a");
        }
        printf("currentRow %0d: %s\n",currentRow, _bitmap[currentRow]); // Each row prints out FINE here, as expected
    }
}

void Bitmap::printBitmap() {
    int numColumns =_width;
    int numRows = _height;

    if (NULL == _bitmap)
        return;

    // iterate over the bitmap, line by line and print it out
    for (int currentRow = 0; currentRow < numRows; currentRow++) {

        // If there are, say, 25 lines, the first 12 or so will be garbage, then the remaining will print as expected
        printLine((char const *)_bitmap[currentRow]);
    }
}

void Bitmap::printLine(char const*lineString) {
    printf(":%s\n", lineString);    
}

这是针对学校的,教授不允许使用 C++ 向量或字符串。否则,是的,我知道我应该使用这些。谢谢大家的建议。

【问题讨论】:

  • 只是出于好奇:如果您使用 C++ 编写此代码,为什么不使用可用的功能,例如新建/删除等?
  • .....这是c++?我可以看到你已经掌握了 c 风格的 c++ 和 c++ 的大杂烩——考虑投资一本好的 c++ 书! ;) malloc 在 C++ 领域是一个faux-pas,考虑使用std::vector
  • @Sagekilla:这里不是new/delete。 std::vector&lt;std::string&gt; 是 OP 真正应该在这里使用的。
  • ...这里有个提示-calloc() 还将初始化分配给一个字符的块(内部循环中的每一件事都在做什么)-所以一行:_bitmap[currentRow] = (char *)calloc('a', (sizeof(char) * width));-注意:这不会是一个空终止的字符串,所以你仍然应该 NUL 正确地终止它......
  • @jbrennan:然后重新制作一个基本的std::vector。

标签: c++ arrays memory-management cstring


【解决方案1】:

红旗:

_bitmap = (char **)malloc(sizeof(char) * height);

应该是

_bitmap = (char **)malloc(sizeof(char*) * height);

您需要一个指向 char* 的指针,而不是指向 char 的指针。

【讨论】:

  • 哇。呵呵,是的,哈哈。我想知道我是怎么错过的。好的。好吧,现在前 3 行是垃圾,其余的都很好。我一定还在某个地方踩到了记忆。
  • @jbrennan,在此修复后,它看起来对我来说是正确的,对我来说也很好。尝试从 stratch 重新编译并再次测试。
  • @jbrennan,我收回了。见大卫的回答。您忘记了空终止符的空间。
  • @jbrennan 另请查看 Greg 关于构造函数默认参数的答案。
【解决方案2】:
_bitmap = (char **)malloc(sizeof(char) * height);

应该是

_bitmap = (char **)malloc(sizeof(char*) * height);

并且仅当您正在编写 C 代码时。

如果您绝对需要位图是连续的,最好使用 new/delete,并且

Vector< Vector < char > > 

如果你不这样做。

另外,strcat 似乎是一个奇怪的选择,因为您还没有初始化内存。 IE。不一定是0,所以字符串没有结尾。这可能会导致你的记忆力下降。尝试使用 strcpy (或 strncpy 如果你想安全的话)。

【讨论】:

  • 这是给学校的,教授不会让我们使用向量或其他内置的 C++ 数据结构。真可惜。
  • @Matthieu 不。我会将它标记为 C,除非你知道,我正在使用对象。更具体地说,对象构造函数。我的问题部分是基于不了解对象生命周期在 C++ 中是如何工作的。
【解决方案3】:

与默认构造函数中的此注释相关:

Bitmap(numRows, numColumns); // Can I even safely do this? I'm not using
                             // the default constructor in my main() but
                             // I'm still curious.

这并不像你认为的那样。这不是调用另一个构造函数来进行额外的初始化。相反,这会使用numRows 和numColumns 创建另一个 临时未命名的Bitmap 对象,然后立即调用其析构函数。这条语句就像一个没有名字的局部变量。

在您的情况下,您可以通过为您的一个构造函数提供默认参数来提供默认构造函数:

public:
    Bitmap(int width = 20, int height = 30);

【讨论】:

    【解决方案4】:

    这个 malloc 没有为字符串末尾的 0 字节留出空间:

        _bitmap[currentRow] = (char *)malloc((sizeof(char) * width));
    

    由于 "sizeof(char)" 定义为 1,你可以这样做:

        _bitmap[currentRow] = (char *)malloc(width+1);
    

    在这个结构中:

        for (int currentColumn = 0; currentColumn < width; currentColumn++) {
            _bitmap[currentRow] = strcat(_bitmap[currentRow], "a");
        }
    

    你真的不想用strcat,直接赋值char即可:

        for (int currentColumn = 0; currentColumn < width; currentColumn++) {
            _bitmap[currentRow][currentColumn] = 'a';
        }
        _bitmap[currentRow][width] = 0; // and don't forget to terminate the string
    

    【讨论】:

      【解决方案5】:

      除了所有其他答案:

      Bitmap::Bitmap() {
          // allocate space for the bitmap
          int numRows = 20;
          int numColumns = 30;
      
          Bitmap(numRows, numColumns); // Can I even safely do this? I'm not using the default constructor in my main() but I'm still curious.
      }
      

      不,你不能这样做。每个构造函数都是独立的,它们不能相互委托。

      对于内存管理,请使用会自动为您控制内存的专用资源管理类。该标准提供了一系列优秀的类,std::vector&lt;std::string&gt; 在这种情况下将很好地发挥作用。

      【讨论】:

      • 感谢您的信息。遗憾的是,我不允许在这项作业中使用向量或 C++ 字符串(这是给学校的)。
      【解决方案6】:

      以下应该正确分配(没测试过)。

      _bitmap = new char*[height];
      for (int currentRow = 0; currentRow < height; currentRow++) 
      {         
          _bitmap[currentRow] = new char[width];         
          snprintf(_bitmap[currentRow], width, "%s", "1");          
          for (int currentColumn = 0; currentColumn < width; currentColumn++) 
          {             
              _bitmap[currentRow] = strcat(_bitmap[currentRow], "a");         
          }                 // Each row prints out FINE here, as expected     
      
          printf("currentRow %0d: %s\n",currentRow, _bitmap[currentRow]); 
      } 
      

      还要确保定义复制构造函数、析构函数和赋值运算符,以确保内存不会泄漏并且数组不会被删除。

      【讨论】:

        【解决方案7】:

        这里我想你想在 malloc 中使用 sizeof (char *)

        _bitmap = (char **)malloc(sizeof(char) * height);
        

        另外,当你用“a”填充字符串时,你必须确保你没有覆盖任何内存:你分配了宽度字符,你打印“1”给它,然后连接“a”宽度时间,这将超过分配的内存 1(更不用说不为 nul 终止留下任何空间

        【讨论】:

          【解决方案8】:

          您的malloc() 电话在我看来不正确,但也许我遗漏了一些东西。

          我应该看到的是一个malloc() 调用阵列的存储。如果你想要 10 个 C 字符串,那就是malloc(10 * sizeof (char *))。然后我应该看到更多的malloc() 调用,它们实际上分配了 10 个字符串本身使用的内存。

          但我只看到一个 malloc() 调用,似乎认为它正在分配字符串数组内存,而不是字符串指针数组内存。

          【讨论】:

          • @jbrennan - 啊,那么也许唯一的问题实际上是您分配的大小。通常sizeof(char) 是1,而sizeof(char *) 是4 或8。所以你分配的字符串指针数组太小了。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-06-27
          • 2022-01-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多