Как объявлять массивы, являющиеся аргументами функций, если их размер должен определяться пользовательским вводом в начале программы

#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[] of Board вы могли бы просто вернуть указатель, вычисленный с 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 , например, на thing Plansza[i j * m].WartoscKarty = 0; .