MaxLevs
@MaxLevs

Непонятная ошибка доступа к памяти winAPI. Как решить?

Захотелось написать программу для рисования треугольника Сперинского.

  • double* lineFrom(HDC hDC, double x, double y, double l, double angle) - рисует линию длинной а, от точки (x,y), под углом angle; возвращает координаты новой точки
  • double* triangle(HDC hDC, double x, double y, double a, double angle = 0) - рисует равносторонний треугольник со сторонойа, от точки (x,y) - вершина, повернутый на углол angle; возвращает координаты двух других вершин {x1, y1, x2, y,2}
  • void cut(HDC hDC, double* PT, double a, size_t n, double angle = 0) - "вырезает" (рисует треугольник поменьше) равносторонний треугольник со сторонойа/2, от треугольника с двумя вершинами PT, повернутого на углол angle


Написал рекурсивный вариант, но мне не нравилось, что при большом N нужно долго ждать отрисовки.
Код. Рекурсия
#include <windows.h>
#include <conio.h>
#define _USE_MATH_DEFINES
#include <cmath>

#define GREEN RGB(138, 179, 45)
#define   RED RGB(181,2,51)

/*Settings*/
#define  DELY 0
#define     N 14
#define   LEN 900
#define   AGL 0
#define COLOR GREEN

double* lineFrom(HDC hDC, double x, double y, double l, double angle) {
	Sleep(DELY);
	double dX = l*cos(angle), dY = l*sin(angle);
	double* res = new double[2];
	res[0] = x + dX;
	res[1] = y + dY;
	MoveToEx(hDC, x, y, NULL);
	LineTo(hDC, res[0], res[1]);
	return res;
}

double getRad(double grad) {
	return (grad / 180) * M_PI;
}

double middle(double a, double b) {
	return (a - b) / 2 + b;
}

double* triangle(HDC hDC, double x, double  y, double a, double angle = 0) {
	double* buff = new double[2];
	double* bPoint = new double[4];
	buff[0] = x; buff[1] = y;
	angle += getRad(60);
	buff = lineFrom(hDC, buff[0], buff[1], a, angle);
	bPoint[0] = buff[0]; bPoint[1] = buff[1];
	buff = lineFrom(hDC, buff[0], buff[1], a, angle += getRad(120));
	bPoint[2] = buff[0]; bPoint[3] = buff[1];
	buff = lineFrom(hDC, buff[0], buff[1], a, angle += getRad(120));
	return bPoint;
}

double geth(double a) {
	return  a * sqrt(3) / 2;
}

void cut(HDC hDC, double* PT, double a, size_t n, double angle = 0) {
	double* D = new double[2];
	a /= 0;
	D[0] = middle(PT[0], PT[2]);
	D[1] = middle(PT[1], PT[3]);
	double* sP = triangle(hDC, D[0], D[1], a, angle - getRad(180));
	n -= 1;
	if (n > 0) {
		double* sp2 = new double[4];
		cut(hDC, sP, a, n, angle);

		sp2[0] = D[0]; sp2[1] = D[1];
		sp2[2] = PT[2]; sp2[3] = PT[3];
		cut(hDC, sp2, a, n, angle);

		sp2[0] = PT[0]; sp2[1] = PT[1];
		sp2[2] = D[0]; sp2[3] = D[1];
		cut(hDC, sp2, a, n, angle);
	}
}

void main() {
	HWND hwnd;
	char Title[1024];
	GetConsoleTitle(Title, 1024);
	hwnd = FindWindow(NULL, Title);
	RECT rc;
	GetClientRect(hwnd, &rc);
	int iWidth = rc.right;
	int iHeight = rc.bottom;
	HDC hdc = GetDC(hwnd);
	HPEN p1, p2 = CreatePen(PS_SOLID, 2, COLOR);
	p1 = (HPEN)SelectObject(hdc, p2);

	double curX, curY;
	curX = (iWidth) / 2;
	curY = (iHeight - geth(LEN)) / 2;

	double* bPoint = triangle(hdc, curX, curY, LEN, getRad(AGL));
	cut(hdc, bPoint, LEN, N, getRad(AGL));


	SelectObject(hdc, p1);
	ReleaseDC(hwnd, hdc);
	DeleteObject(p2);
	_getch();
}


Решил переделать ради интереса через итерации. (менял только void cut()) Ясное дело, что для неё нужно больше памяти, ведь для каждой следующей итерации требуется 4*3^i переменных типа double для хранения данных для перебора.

Вроде все нормально, но при i = 4 функция winApi lineTo выдает ошибку доступа к памяти. В чем может быть проблема? Изменял только cut. Работало для любых N.

Код. Итерации
#include <windows.h>
#include <conio.h>
#define _USE_MATH_DEFINES
#include <cmath>

#define GREEN RGB(138, 179, 45)
#define   RED RGB(181,2,51)

/*Settings*/
#define  DELY 0
#define     N 4
#define   LEN 900
#define   AGL 0
#define COLOR GREEN



double getRad(double grad) {
	return (grad / 180) * M_PI;
}

double middle(double a, double b) {
	return (a - b) / 2 + b;
}

double geth(double a) {
	return  a * sqrt(3) / 2;
}

double* lineFrom(HDC hDC, double x, double y, double l, double angle) {
	Sleep(DELY);
	double dX = l*cos(angle), dY = l*sin(angle);
	double* res = new double[2];
	res[0] = x + dX;
	res[1] = y + dY;
	MoveToEx(hDC, x, y, NULL);
	LineTo(hDC, res[0], res[1]);
	return res;
}

double* triangle(HDC hDC, double x, double  y, double a, double angle = 0) {
	double* buff = new double[2];
	double* bPoint = new double[4];
	buff[0] = x; buff[1] = y;
	angle += getRad(60);
	buff = lineFrom(hDC, buff[0], buff[1], a, angle);
	bPoint[0] = buff[0]; bPoint[1] = buff[1];
	buff = lineFrom(hDC, buff[0], buff[1], a, angle += getRad(120));
	bPoint[2] = buff[0]; bPoint[3] = buff[1];
	buff = lineFrom(hDC, buff[0], buff[1], a, angle += getRad(120));
	return bPoint;
}

void cut(HDC hDC, double* PT, double a, size_t n, double angle = 0) {
	/* Создание массива A для аргументов размером [1][4] */
	double **A, **B;
	A = new double*[1];
	A[0] = new double[4];
	
	/* Инициализация A начальными значениями */
	A[0][0] = PT[0]; A[0][1] = PT[1];
	A[0][2] = PT[2]; A[0][3] = PT[3];

	for (int i = 0; i < n; ++i) {
		B = new double*[(i+1) * 3];
		a /= 2;
		for (int j = 0; j < pow(3, i); ++j) {
			/* Буффер данных для след. итерации*/
			for(int k = 0; k < 3; ++k)
				B[3 * j + k] = new double[4]; // Нужно ли создавать или достаточно присвоить?

				
			/* Обрезаем для текущего */
			double D[2] = { 
				middle(A[j][0], A[j][2]), 
				middle(A[j][1], A[j][3])
			};
			double* buff = triangle(hDC, D[0], D[1], a, angle - getRad(180));
	
			/* Расчет параметров для j+1 и запись их в В*/
			B[3 * j + 0][0] = buff[0]; B[3 * j + 0][1] = buff[1];
			B[3 * j + 0][2] = buff[2]; B[3 * j + 0][3] = buff[3];

			B[3 * j + 1][0] = D[0];    B[3 * j + 1][1] = D[1];
			B[3 * j + 1][2] = A[j][0]; B[3 * j + 1][3] = A[j][1];

			B[3 * j + 2][0] = A[j][2]; B[3 * j + 2][1] = A[j][3];
			B[3 * j + 2][2] = D[0];    B[3 * j + 2][3] = D[1];
			
			delete[] A[j];
		}
		delete[] A; // Нужно ли удалять или достаточно приравнять?
	}
}

void main() {
	HWND hwnd;
	char Title[1024];
	GetConsoleTitle(Title, 1024);
	hwnd = FindWindow(NULL, Title);
	RECT rc;
	GetClientRect(hwnd, &rc);
	int iWidth = rc.right;
	int iHeight = rc.bottom;
	HDC hdc = GetDC(hwnd);
	HPEN p1, p2 = CreatePen(PS_SOLID, 2, COLOR);
	p1 = (HPEN)SelectObject(hdc, p2);

	double curX, curY;
	curX = (iWidth) / 2;
	curY = (iHeight - geth(LEN)) / 2;

	double* bPoint = triangle(hdc, curX, curY, LEN, getRad(AGL));
	cut(hdc, bPoint, LEN, N, getRad(AGL));

	SelectObject(hdc, p1);
	ReleaseDC(hwnd, hdc);
	DeleteObject(p2);
	_getch();
}

  • Вопрос задан
  • 200 просмотров
Решения вопроса 1
@res2001
Developer, ex-admin
1.НА КАЖДУЮ ОПЕРАЦИЮ NEW ДОЛЖНА БЫТЬ СООТВЕТСТВУЮЩАЯ ОПЕРАЦИЯ DELETE.
У вас явно не хватает delete в разных местах.
2.Использовать двумерные массивы так как вы их используете ... ну это извращение какое-то. Сейчас именно так учат? Я понимаю, что удобно делать [i][j] ... но есть же адресная арифтметика, разъименование. И можно легко перейти от указателя на двумерный массив, к указателю на одномерный и использовать индексацию на одномерном массиве.
3.Не нужно выделять память на каждой итерации цикла - достаточно выделить в самом начале массив максимального размера, а дальше использовать его внутри цикла на всех итерациях.
4.Обычно как раз рекурсия более прожорлива к памяти (да и к ЦПУ), чем итерационный метод, т.к. рекурсией съедается стек и если большая вложенность, то стек может кончится.

PS: про ошибку памяти: запустите программу под отладчиком, она прервется, когда произойдет ошибка, в отладчике сможете перейти в последнюю вашу функцию (по стеку вызовов) и посмотреть где-что не так (я надеюсь вы не думаете, что ошибка действительно в lineTo).
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы