Глупо ли создавать динамический 2d-массив, где второе измерение является константой?

#c

#c

Вопрос:

Я пытаюсь создать массив с переменным количеством строк, но в нем всегда будет 4 столбца. Делает что-то вроде:

 int** numGrades = new int* [num_exams];
for (int i = 0; i < num_exams;   i)
{
    numGrades[i] = new int[4];
}
  

хороший способ сделать это? Я чувствую, что есть более простой способ, но я не могу придумать ни одного. Кроме того, массив продолжает вызывать у меня утечки памяти, поэтому мне интересно, связано ли это с тем, что я делаю то, чего не должен делать. К вашему сведению, векторы запрещены для этой программы.

Комментарии:

1. Для чего вы будете его использовать?

2. Это часть более крупного задания, в котором я вычисляю оценки учащихся. В этом конкретном массиве будет храниться номер каждой оценки (A, B, C, D, F) на каждом экзамене для любого заданного количества экзаменов.

3. Ответ — НЕТ. Однако, если вы имеете дело с чем-то, что всегда имеет 4 int , ваше распределение должно быть int (*numGrades)[4] = new int[num_exams][4]; таким, чтобы обеспечить однократное выделение и однократное освобождение блока памяти (также значительно упрощает перераспределение)

4. Я попытался выделить его таким образом, но теперь это не позволит мне передать массив ни одной из моих функций. В нем говорится «аргумент типа int * несовместим с типом int **». Объявляет ли это так, как вы это сделали, что это больше не 2d-массив?

Ответ №1:

Вы могли бы создать массив строк.

 struct Row{
   int values[4];
};

Row* numGrades = new Row[num_exams];
  

Комментарии:

1. Будет ли в этом какое-либо реальное преимущество? У меня уже есть это как 2d-массив, но если это стоит изменить, я это сделаю.

2. @aons32 Да, это уменьшило бы потребность в динамических распределениях. Вам не нужно беспокоиться о num_exams выделениях, а только об одном. Если векторы не разрешены, это, вероятно, было бы лучшим решением.

3. Это, вероятно, также исправило бы странные утечки памяти. Я, вероятно, попробую это завтра.

4. Альтернативы: new std::array<int,4>[num_exams]; если не все контейнеры STL запрещены или даже new int[4 * num_exams]; .

5. @aons32 Еще одно преимущество структурного подхода: если каждая строка представляет экзамены для одного учащегося, то вы также можете добавить функции в структуру и делать что-то вроде этого: for (const Rowamp; student : numGrades) student.calculateAverage(); или, как предложил Дэвид К. Рэнкин, перегрузить operator >> и << для чтения и записи данных учащихся.

Ответ №2:

Может быть, вы можете попробовать это.

 typedef int row[4];
//or
using row = int[4];

row *numGrades = new row[num_exams];
  

Ответ №3:

Выделение некоторого количества массивов фиксированного размера является прекрасным и выгодным во многих случаях.

В дополнение к struct (что является очень хорошим вариантом), другим вариантом является объявление указателя на массив с фиксированным количеством элементов. Преимущество в том, что у вас есть однократное выделение и однократное освобождение для блока памяти. (как вы делаете с массивом struct) Если вам нужно увеличить блок памяти (с помощью — declare bigger block, скопируйте существующий в больший, удалите существующее перераспределение), это упрощает процесс. В вашем случае:

  int (*numGrades)[4] = new int[num_exams][4];
  

Который выделит num_exams количество массивов из 4 int одновременно. Это дает преимущество одного delete[] numGrades; , когда вы закончите с памятью.

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

 #include <iostream>
#include <sstream>

int main (void) {
    
    std::istringstream iss { "1 2 3 4 5 6 7 8 9" };
    
    int npoints = 3,
        (*points)[3] = new int[npoints][3],
        n = 0;
    
    while (n < 3 amp;amp; iss >> points[n][0] >> points[n][1] >> points[n][2])
        n  ;
    
    for (int i = 0; i < n; i  )
        std::cout << points[i][0] << "  " << points[i][1] << "  " << points[i][2] << 'n';
    
    delete[] points;
}
  

(примечание: вам следует избегать использования new и delete в пользу контейнера, такого как std::vector, если это не для образовательных целей)

Пример использования / вывода

 $ ./bin/newptr2array3
1  2  3
4  5  6
7  8  9
  

Стоит отметить, что преимущество struct заключается в том, что это позволит вам перегружать >> и << std::istream и std::ostream , чтобы предоставить удобные функции для чтения и записи необходимых вам данных.

В любом случае, указатель на массив фиксированных элементов или создание struct , а затем массива struct — это прекрасно.

Ответ №4:

Вы могли бы пропустить цикл for:

 int* numGrades = new int[num_exams*4];
int firstElement = numGrades[x];
int secondElement = numGrades[x 1];
int thirdElement = numGrades[x 2];
int fourthElement = numGrades[x 3];
  

Пропуская цикл for, вы получаете это:

  1. Вам не обязательно иметь цикл for для освобождения памяти:

    удалить[] numGrades;

  2. Куча не так сильно фрагментируется, потому что вы не вызываете «new» так много раз.

НО все зависит от того, для чего вы его используете. В современном C не такая хорошая идея использовать dynamic, но создавать структуру в std ::vector.

Комментарии:

1. Я бы предпочел использовать векторы, но мне это не разрешено. И какой смысл пропускать цикл for?

2. @aons32 Я добавил объяснение, почему пропуск цикла for — хорошая идея.