Странные проблемы управления памятью в C++ (по крайней мере, от новичка)
Я новичок в C++, у меня много опыта в Objective-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++. В противном случае, да, я знаю, что я должен использовать их. Спасибо за все предложения.
8 ответов
Красный флаг:
_bitmap = (char **)malloc(sizeof(char) * height);
должно быть
_bitmap = (char **)malloc(sizeof(char*) * height);
Вы хотите указатель на char*
, а не указатель на char
,
_bitmap = (char **)malloc(sizeof(char) * height);
должно быть
_bitmap = (char **)malloc(sizeof(char*) * height);
и только если вы кодируете C.
Лучше использовать new/delete, если вам абсолютно необходимо, чтобы растровое изображение было смежным, и
Vector< Vector < char > >
если нет
Кроме того, strcat кажется странным выбором, учитывая, что вы еще не инициализировали память. Т.е. здесь не обязательно 0, поэтому нет конца строки. Это может привести к потере памяти. Попробуйте использовать strcpy (или strncpy, если вы хотите быть в безопасности).
Связанный с этим комментарием в вашем конструкторе по умолчанию:
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
использование объекта numRows
а также numColumns
и тут же вызывает его деструктор. Этот оператор действует как локальная переменная без имени.
В вашем случае вы можете предоставить конструктор по умолчанию, указав аргументы по умолчанию для одного конструктора:
public:
Bitmap(int width = 20, int height = 30);
Этот 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
В дополнение ко всем остальным ответам:
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<std::string>
будет служить цели в этом случае превосходно.
Здесь я думаю, что вы хотите sizeof (char *) внутри malloc
_bitmap = (char **)malloc(sizeof(char) * height);
Кроме того, когда вы заполняете строку символом "а", вы должны убедиться, что не перезаписываете какую-либо память: вы выделили символ ширины, вы напечатали ему "1", а затем конкатенировали "a" раз ширины, что будет равно 1 мимо выделенной памяти (не говоря уже о том, чтобы не оставить места для нуль-терминации
Ваш malloc()
Звонок мне не подходит, но, возможно, я что-то упустил.
То, что я должен видеть, это один malloc()
вызов для хранения для массива. Если вы хотите 10 строк C в нем, это было бы malloc(10 * sizeof (char *))
, Тогда я должен увидеть еще malloc()
вызовы, которые фактически выделяют память, которую используют сами 10 строк.
Но я вижу только один вызов malloc(), который думает, что он выделяет память массива строк, а не память массива указателей строк.
Следующее должно распределяться правильно (еще не проверял).
_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]);
}
Также убедитесь, что вы определили конструктор копирования, деструктор и оператор присваивания, чтобы не допустить утечки памяти и удаления массива.