#c
#c
Вопрос:
Для моего проекта «основы программирования» мне было приказано создать «игру памяти». 2 игрока в свои соответствующие ходы выбирают, какие карты открывать на доске размером «m x n». «m» и «n» должны быть выбраны в начале каждой игры. Мой вопрос в том, как я могу создать массив структур, используемых для отображения платы в момент ввода пользователем. До сих пор я просто использовал const int для создания массива максимального размера, однако более 95% индексов массивов пусты при использовании этого метода. Есть ли способ создать массив сразу после ввода пользователем, а также определить и объявить эти функции с помощью массива структур, размер которых соответствует размеру ввода? Вот мой код до сих пор:
const int MAX_M = 1000;
const int MAX_N = 1000;
Karta Plansza2[MAX_M][MAX_N];
void SprawdzanieParzystosci(intamp; m, intamp; n);
void RozmiaryTablicy(intamp; m, intamp; n);
void generuj(int m, int n, Karta Plansza[MAX_M][MAX_N]);
void WyswietleniePlanszy(int m, int n, Karta Plansza[MAX_M][MAX_N]);
void generuj(int m, int n, Karta Plansza[][MAX_N])
{
srand((unsigned int)time(NULL));
char A;
int B;
int C;
int D;
int k = 0;
int w1, w2, k1, k2;
for (int i = 0; i < m; i )
for (int j = 0; j < n; j ) {
Plansza[i][j].WartoscKarty = 0;
}
while (k < (m*n))
{
A = char(rand() % 10 65);
B = (rand() % 10);
C = (rand() % 10);
D = ((rand() % 2000000) 1);
do{
w1 = rand() % m;
k1 = rand() % n;
}while(Plansza[w1][k1].WartoscKarty != 0);
Plansza[w1][k1].ZnakPierwszy = A;
Plansza[w1][k1].LiczbaPierwsza = B;
Plansza[w1][k1].LiczbaDruga = C;
Plansza[w1][k1].WartoscKarty = D;
k ;
do{
w2 = rand() % m;
k2 = rand() % n;
} while (Plansza[w2][k2].WartoscKarty != 0);
Plansza[w2][k2].ZnakPierwszy = A;
Plansza[w2][k2].LiczbaPierwsza = B;
Plansza[w2][k2].LiczbaDruga = C;
Plansza[w2][k2].WartoscKarty = D;
k ;
}
}
/////////////////////////////////////////////////////
void WyswietleniePlanszy(int m, int n, Karta Plansza[MAX_M][MAX_N])
{
for (int i = 0; i < m; i ) {
for (int j = 0; j < n; j )
cout << "***" << setw(5);
cout << "n";
for (int j = 0; j < n; j )
cout << "*" << Plansza[i][j].ZnakPierwszy << "*" << " ";
cout << "n";
for (int j = 0; j < n; j )
cout << "*" << Plansza[i][j].LiczbaPierwsza << "*" << " ";
cout << "n";
for (int j = 0; j < n; j )
cout << "*" << Plansza[i][j].LiczbaDruga << "*" << " ";
cout << "n";
// for(int j = 0; j < 10; j )
// cout << wzor[i][j].num4 << " ";
for (int j = 0; j < n; j )
cout << "***" << setw(5);
cout << "n";
cout << endl;
}
}
/////////////////////////////////////////////////////
void RozmiaryTablicy(intamp; m, intamp; n)
{
cout << "Podaj rozmiar m tablicy: ";
cin >> m;
cout << "Podaj rozmiar n tablicy: ";
cin >> n;
}
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
void SprawdzanieParzystosci(intamp; m, intamp; n)
{
while ((m * n) % 2 != 0 || (m <= 0) || (n <= 0)) {
RozmiaryTablicy(m, n);
if((m * n) % 2 != 0 || (m <= 0) || (n <= 0)) cout << "Zle dane. Prosze podac dane jeszcze raz" << endl;
}
}
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
int main()
{
int m =1;
int n =1;
SprawdzanieParzystosci(m, n);
generuj(m,n,Plansza2);
WyswietleniePlanszy(m,n,Plansza2);
cout << m << endl;
cout << n << endl;
system("pause");
return 0;
}
Например, если пользователь вводит m = 5 и n = 6, это создаст массив Plansza [5] [6] вместо массива Plansza [1000] [1000]
Комментарии:
1. Это невозможно в C .
vector
Вместо этого используйте a .2. Здравствуйте, мне запретили использовать нестандартные библиотеки, и я не могу использовать <vector> в этом проекте, я подумал, может быть, есть способ с динамическими массивами, но я нигде не могу найти решение этой проблемы
3. если вам не разрешено использовать
std::vector
, напишите свой собственный. Самое сложное — правильно распределить память, и это вам все равно придется делать. Остальное — навороты4. @KrzysiekDymanowski
std::vector
— это стандартная библиотека, поэтому проблем быть не должно. Я был бы больше удивлен, что вам разрешено использоватьsystem("pause");
.5. Но если вы не хотите использовать вектор, то вы можете просто выделить память старым добрым способом с
new[]
помощью илиmalloc
. Однако тогда вам нужно будет не забыть освободить его, а также перераспределить, когда вы хотите изменить размер массива. Вам также нужно будет отслеживать размер массива (это не делается автоматически) и следить за тем, чтобы вы не выходили за его границы.
Ответ №1:
Быстрый взлом платы, обратите внимание на красивые board[row][column]
обозначения и возвращаемую ссылку на поле. C 17 (может работать в C 14)
#include <iostream>
#include <memory>
#include <cstring>
using DaType = char;
class Board {
int rows = 0;
int cols = 0;
std::unique_ptr<DaType[]> board; // RAII
public:
class Row {
DaType *board;
public:
Row(DaType *row) : board(row) {}
DaTypeamp; operator[](int col) { return board[col]; }
};
Board(int row, int col) : rows(row), cols(col), board(std::make_unique<DaType[]>(row*col)) { memset(board.get(), '.', rows*cols); }
Row operator[](int row) { return Row(board.get() row*cols); }
};
int main() {
const int sx = 6, sy = 10;
Board board(sx,sy);
board[3][5] = 'x';
for (int i = 0; i < sx; i ) {
for (int j = 0; j < sy; j )
std::cout << board[i][j];
std::cout << 'n';
}
}
Ps. в прошлый раз, когда я это делал, это казалось проще…
Обновление благодаря IlCapitano
class Board {
int rows = 0;
int cols = 0;
std::unique_ptr<DaType[]> board; // RAII
public:
Board(int row, int col) : rows(row), cols(col), board(std::make_unique<DaType[]>(row*col)) { memset(board.get(), '.', rows*cols); }
DaType *operator[](int row) { return board.get() row*cols; }
};
Комментарии:
1. В
operator[]
ofBoard
вы могли бы просто вернуть указатель, вычисленный сboard.get() row*cols
помощью вместо использования прокси-класса. Поведение будет таким же, но сам класс намного проще.
Ответ №2:
Самый простой способ решить эту проблему — просто использовать std::vector
, поскольку размер массивов в аргументах, stackallocations и т. Д. должно быть известно во время компиляции.
Самым простым вариантом без использования vector было бы объявить Plansza2
как a Karta*
и динамически выделять память после SprawdzanieParzystosci
использования Plansza2 = new Karta[m*n];
(не забудьте вызвать delete[](Plansza2);
перед завершением вашей программы). Если вы сделаете это, вы сможете получить доступ к ячейкам с Plansza2[y * m x]
помощью (при m
условии, что это ширина и n
высота). Преимущество сопоставления 2-мерного массива с 1-мерным массивом путем размещения всех строк друг за другом заключается в том, что вам нужно только одно выделение и одно удаление, и, кроме того, это улучшает удобство использования кэша.
Более чистым способом решения этой проблемы (устранение возможности утечки памяти, если что-то вызывает исключение или вы забываете вызвать delete
) было бы создать свой собственный класс для 2-мерных массивов, который вызывался new[]
бы в конструкторе и delete[]
в деструкторе. Если вы это сделаете, вы можете определить Kartaamp; operator()(int x, int y);
и const Kartaamp; operator()(int x, int y) const;
вернуть соответствующую ячейку, что позволит вам получить доступ к ячейке с dynamicMap(x, y)
помощью . operator[]
может принимать только один аргумент и поэтому его сложнее использовать для доступа к 2-мерному массиву (вы можете, например, использовать std::pair в качестве аргумента или возвращать прокси-класс, в котором также определен operator[] ). Однако, если вы пишете свой собственный деструктор, вам нужно позаботиться о конструкторах копирования (всегда) и перемещения (c 11 и далее) и операторах присваивания, поскольку создание экземпляров по умолчанию приведет к тому, что ваш деструктор попытается удалить один и тот же указатель несколько раз. Примером оператора присваивания перемещения является:
DynamicMapamp; DynamicMap::operator=( DynamicMapamp;amp; map ){
if(this == amp;map)
return *this; //Don't do anything if both maps are the same map
dataPointer = map.dataPointer; //Copy the pointer to "this"
map.dataPointer = nullptr; //Assign nullptr to map.dataPointer because delete[] does nothing if called with null as an argument
//You can move other members in the above fashion, using std::move for types more complex than a pointer or integral, but be careful to leave map in a valid, but empty state, so that you do not try to free the same resource twice.
return *this;
}
Конструктор перемещения не требует предложения if в начале, но в остальном идентичен, и оператор копирования конструктора / присваивания, вероятно, следует объявить как = delete;
, поскольку, вероятно, это будет ошибка, если вы скопируете свою карту. Если вам нужно определить операции копирования, не копируйте указатель, а вместо этого создайте новый массив и скопируйте содержимое.
Комментарии:
1. Привет. большое вам спасибо за ваш ответ, я попытался сделать это так, как вы описали во втором абзаце, однако я не совсем уверен, как мне придется переписать свою
generuj
функцию, чтобы она снова заработала. Я на самом деле спрашивал и для такой небольшой программы (также я не изучаю информатику, а робототехнику, студенты CS должны быть намного точнее в своих проектах) Я могу просто#define MAX_M 1000
, а затем объявить и определить каждый Plansza какPlansza[MAX_M][MAX_M]
2. Объявление функции должно быть
void generuj(int m, int n, Karta Plansza*)
таким, и каждое вхождение arrayaccess должно быть замененоx y * m
, например, на thingPlansza[i j * m].WartoscKarty = 0;
.