Как я лучше всего обрабатываю динамические многомерные массивы в C/C++?

Примечание: Неопределенный индекс

Случается, когда вы пытаетесь получить доступ к массиву с помощью ключа, который не существует в массиве.

Типичным примером для уведомления Undefined Index будет ( demo )

$data = array('foo' => '42', 'bar');
echo $data['spinach'];
echo $data[1];

Оба spinach и 1 не существуют в массив, вызывающий запуск E_NOTICE .

Решение состоит в том, чтобы убедиться, что индекс или смещение существуют до доступа к этому индексу. Это может означать, что вам необходимо исправить ошибку в вашей программе, чтобы убедиться, что эти индексы существуют, когда вы ожидаете их. Или это может означать, что вам нужно проверить, существуют ли индексы с помощью array_key_exists или isset :

$data = array('foo' => '42', 'bar');
if (array_key_exists('spinach', $data)) {
    echo $data['spinach'];
}
else {
    echo 'No key spinach in array';
}

Если у вас есть код например:


...

, тогда $_POST['message'] не будет установлена, когда эта страница будет загружена первой, и вы получите указанную выше ошибку. Только когда форма будет отправлена ​​и этот код будет запущен во второй раз, будет существовать индекс массива. Вы обычно проверяете это с помощью:

if ($_POST)  ..  // if the $_POST array is not empty
// or
if ($_SERVER['REQUEST_METHOD'] == 'POST') ..  // page was requested with POST

Вопросы, относящиеся:

25
задан Ari Ronen 7 December 2011 в 00:43
поделиться

8 ответов

Используйте повышение:: multi_array.

Как в Вашем примере, единственной вещью, которую необходимо знать во время компиляции, является количество размеров. Вот первый пример в документации:

#include "boost/multi_array.hpp"
#include <cassert>

int 
main () {
  // Create a 3D array that is 3 x 4 x 2
  typedef boost::multi_array<double, 3> array_type;
  typedef array_type::index index;
  array_type A(boost::extents[3][4][2]);

  // Assign values to the elements
  int values = 0;
  for(index i = 0; i != 3; ++i) 
    for(index j = 0; j != 4; ++j)
      for(index k = 0; k != 2; ++k)
        A[i][j][k] = values++;

  // Verify values
  int verify = 0;
  for(index i = 0; i != 3; ++i) 
    for(index j = 0; j != 4; ++j)
      for(index k = 0; k != 2; ++k)
        assert(A[i][j][k] == verify++);

  return 0;
}

Редактирование: Как предложено в комментариях, вот "простой" пример приложение, которые позволяют Вам определить размер многомерного массива во времени выполнения, спрашивая от консольного входа. Вот вывод в качестве примера этого примера приложения (скомпилированный с постоянным высказыванием, что это - 3 размера):

Multi-Array test!
Please enter the size of the dimension 0 : 4

Please enter the size of the dimension 1 : 6

Please enter the size of the dimension 2 : 2

Text matrix with 3 dimensions of size (4,6,2) have been created.

Ready!
Type 'help' for the command list.

>read 0.0.0
Text at (0,0,0) :
  ""

>write 0.0.0 "This is a nice test!"
Text "This is a nice test!" written at position (0,0,0)

>read 0.0.0
Text at (0,0,0) :
  "This is a nice test!"

>write 0,0,1 "What a nice day!"
Text "What a nice day!" written at position (0,0,1)

>read 0.0.0
Text at (0,0,0) :
  "This is a nice test!"

>read 0.0.1
Text at (0,0,1) :
  "What a nice day!"

>write 3,5,1 "This is the last text!"
Text "This is the last text!" written at position (3,5,1)

>read 3,5,1
Text at (3,5,1) :
  "This is the last text!"

>exit

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

const unsigned int DIMENSION_COUNT = 3; // dimension count for this test application, change it at will :)

// here is the type of the multi-dimensional (DIMENSION_COUNT dimensions here) array we want to use
// for this example, it own texts
typedef boost::multi_array< std::string , DIMENSION_COUNT > TextMatrix;

// this provide size/index based position for a TextMatrix entry.
typedef std::tr1::array<TextMatrix::index, DIMENSION_COUNT> Position; // note that it can be a boost::array or a simple array

/*  This function will allow the user to manipulate the created array
    by managing it's commands.
    Returns true if the exit command have been called.
*/
bool process_command( const std::string& entry, TextMatrix& text_matrix );

/* Print the position values in the standard output. */
void display_position( const Position& position );

int main()
{
    std::cout << "Multi-Array test!" << std::endl;

    // get the dimension informations from the user
    Position dimensions; // this array will hold the size of each dimension 

    for( int dimension_idx = 0; dimension_idx < DIMENSION_COUNT; ++dimension_idx )
    {
        std::cout << "Please enter the size of the dimension "<< dimension_idx <<" : ";
        // note that here we should check the type of the entry, but it's a simple example so lets assume we take good numbers
        std::cin >> dimensions[dimension_idx]; 
        std::cout << std::endl;

    }

    // now create the multi-dimensional array with the previously collected informations
    TextMatrix text_matrix( dimensions );

    std::cout << "Text matrix with " << DIMENSION_COUNT << " dimensions of size ";
    display_position( dimensions );
    std::cout << " have been created."<< std::endl;
    std::cout << std::endl;
    std::cout << "Ready!" << std::endl;
    std::cout << "Type 'help' for the command list." << std::endl;
    std::cin.sync();


    // we can now play with it as long as we want
    bool wants_to_exit = false;
    while( !wants_to_exit )
    {
        std::cout << std::endl << ">" ;
        std::tr1::array< char, 256 > entry_buffer; 
        std::cin.getline(entry_buffer.data(), entry_buffer.size());

        const std::string entry( entry_buffer.data() );
        wants_to_exit = process_command( entry, text_matrix );
    }

    return 0;
}

И Вы видите, что для соглашения элемента в массиве это действительно легко: Вы просто используете оператор () как в следующих функциях:

void write_in_text_matrix( TextMatrix& text_matrix, const Position& position, const std::string& text )
{
    text_matrix( position ) = text;
    std::cout << "Text \"" << text << "\" written at position ";
    display_position( position );
    std::cout << std::endl;
}

void read_from_text_matrix( const TextMatrix& text_matrix, const Position& position )
{
    const std::string& text = text_matrix( position );
    std::cout << "Text at ";
    display_position(position);
    std::cout << " : "<< std::endl;
    std::cout << "  \"" << text << "\"" << std::endl;
}

Примечание: Я скомпилировал это приложение в VC9 +, SP1 - получил просто некоторые легко забывающиеся предупреждения.

21
ответ дан Klaim 28 November 2019 в 20:54
поделиться

Существует два способа представить массив с 2 размерами в C++. Один являющийся более гибким, чем другой.

Массив массивов

Первый делает массив указателей, затем инициализирует каждый указатель с другим массивом.

// First dimension
int** array = new int*[3];
for( int i = 0; i < 3; ++i )
{
    // Second dimension
    array[i] = new int[4];
}

// You can then access your array data with
for( int i = 0; i < 3; ++i )
{
    for( int j = 0; j < 4; ++j )
    {
        std::cout << array[i][j];
    }
}

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

Большой массив для содержания всех значений

прием здесь должен создать значительный массив для содержания каждых данных, в которых Вы нуждаетесь. Твердая часть - то, что Вам все еще нужен первый массив указателей, если Вы хотите быть в состоянии получить доступ к данным с помощью массива [я] [j] синтаксис.

int* buffer = new int[3*4];   
int** array = new int*[3];

for( int i = 0; i < 3; ++i )
{
    array[i] = array + i * 4;
}

интервал* массив не обязателен, поскольку Вы могли получить доступ к своим данным непосредственно в буфере путем вычисления индекса в буфере от координат с 2 размерами значения.

// You can then access your array data with
for( int i = 0; i < 3; ++i )
{
    for( int j = 0; j < 4; ++j )
    {
        const int index = i * 4 + j;
        std::cout << buffer[index];
    }
}

ПРАВИЛО иметь в виду

Память компьютера линейна и все еще будет в течение долгого времени. Следует иметь в виду, что массивы с 2 размерами исходно не поддерживаются на компьютере, таким образом, единственный путь состоит в том, чтобы "линеаризовать" массив в массив с 1 размером.

8
ответ дан Vincent Robert 28 November 2019 в 20:54
поделиться

Вы можете строки выделения седла sizeof (интервал) и получать доступ к нему таблицей [row*cols+col].

5
ответ дан Guge 28 November 2019 в 20:54
поделиться

Стандартный путь, не используя повышение состоит в том, чтобы использовать станд.:: вектор:

std::vector< std::vector<int> > v;
v.resize(rows, std::vector<int>(cols, 42)); // init value is 42
v[row][col] = ...;

, Который будет заботиться о новых / удаляют память, в которой Вы нуждаетесь автоматически. Но это довольно медленно, с тех пор std::vector, прежде всего, не разработан для использования его как этот (вложение std::vector друг в друга). Например, вся память не выделяется в одном блоке, но отдельная для каждого столбца. Также строки не должны быть всей той же шириной. Быстрее использует вектор нормали, и затем делает индексное вычисление как col_count * row + col для достигания определенной строки и седла:

std::vector<int> v(col_count * row_count, 42);
v[col_count * row + col) = ...;

, Но это освободит возможность индексировать вектор с помощью [x][y]. Также необходимо сохранить сумму строк и седел где-нибудь при использовании вложенного решения, которое можно получить сумму строк с помощью v.size() и сумму седел с помощью v[0].size().

Используя повышение, можно использовать boost::multi_array, который делает точно, что Вы хотите (см. другой ответ).

<час>

существует также необработанный способ использовать собственные массивы C++. Это включает некоторую работу и никоим образом не лучше, чем решение для вложенного вектора:

int ** rows = new int*[row_count];
for(std::size_t i = 0; i < row_count; i++) {
    rows[i] = new int[cols_count];
    std::fill(rows[i], rows[i] + cols_count, 42);
}

// use it... rows[row][col] then free it...

for(std::size_t i = 0; i < row_count; i++) {
    delete[] rows[i];
}

delete[] rows;

необходимо сохранить сумму столбцов и строк, которые Вы создали где-нибудь, так как Вы не можете получить их от указателя.

4
ответ дан Johannes Schaub - litb 28 November 2019 в 20:54
поделиться

2D массивы C-стиля в C и C++ являются блоком памяти размера rows * columns * sizeof(datatype) байты.

фактические [строка] [столбец] размеры существуют только статически во время компиляции. Нет ничего там динамично во времени выполнения!

Так, как другие упомянули, можно реализовать

  int array [ rows ] [ columns ];

Как:

 int  array [ rows * columns ]

Или как:

 int * array = malloc ( rows * columns * sizeof(int) );
<час>

Затем: Объявление непостоянно размерного массива. В C это возможно:

int main( int argc, char ** argv )
{
  assert( argc > 2 );

  int rows    = atoi( argv[1] );
  int columns = atoi( argv[2] );

  assert(rows > 0 && columns > 0);
  int data [ rows ] [ columns ];  // Yes, legal!

  memset( &data, 0, sizeof(data) );

  print( rows, columns, data );
  manipulate( rows, columns, data );
  print( rows, columns, data );
}
<час>

В C можно просто передать непостоянно измеренный массив вокруг того же как массив non-variably-sized:

void manipulate( int theRows, int theColumns, int theData[theRows][theColumns] )
{
  for (   int r = 0; r < theRows;    r ++ )
    for ( int c = 0; c < theColumns; c ++  )
      theData[r][c] = r*10 + c;
}

Однако в C++ , который не возможен. Необходимо выделить массив с помощью динамического выделения, например:

int *array = new int[rows * cols]();

или предпочтительно (с автоматизированным управлением памятью)

std::vector<int> array(rows * cols);

Тогда функции должны быть изменены для принятия 1-мерных данных:

void manipulate( int theRows, int theColumns, int *theData )
{
  for (   int r = 0; r < theRows;    r ++ )
    for ( int c = 0; c < theColumns; c ++  )
      theData[r * theColumns + c] = r*10 + c;
}
3
ответ дан M.M 28 November 2019 в 20:54
поделиться

При использовании C вместо C++, Вы могли бы хотеть посмотреть на абстракцию Array_T в библиотеке Dave Hanson Интерфейсы C и Реализации . Это исключительно чисто и хорошо разработано. Я сделал, чтобы мои студенты сделали двумерную версию как осуществление. Вы могли сделать это или просто записать дополнительную функцию, которая делает индексное отображение, например,

void *Array_get_2d(Array_T a, int width, int height, int i, int j) {
    return Array_get(a, j * width, i, j);
}

Это немного более чисто для имения отдельной структуры, где Вы храните ширину, высоту и указатель на элементы.

2
ответ дан Norman Ramsey 28 November 2019 в 20:54
поделиться

Нет никакого способа определить длину данного массива в C++. Лучший способ состоял бы в том, чтобы, вероятно, передать в длине каждого размера массива и использовании это вместо .length свойства самого массива.

0
ответ дан Andy 28 November 2019 в 20:54
поделиться

Вы могли использовать malloc, чтобы выполнить это и все еще иметь его доступный через нормальный массив [] [] средний, стихи массив [строки * седла + седла] метод.

main()
{
   int i;
   int rows;
   int cols;
   int **array = NULL;

   array = malloc(sizeof(int*) * rows);
   if (array == NULL)
       return 0;  // check for malloc fail

   for (i = 0; i < rows; i++)
   {
       array[i] = malloc(sizeof(int) * cols)
       if (array[i] == NULL)
           return 0;  // check for malloc fail
   }

   // and now you have a dynamically sized array
}
0
ответ дан lillq 28 November 2019 в 20:54
поделиться