Como declaro uma matriz 2D em C ++ usando new?


527

Como declaro uma matriz 2D usando new?

Como, para uma matriz "normal", eu faria:

int* ary = new int[Size]

mas

int** ary = new int[sizeY][sizeX]

a) não funciona / compila eb) não realiza o que:

int ary[sizeY][sizeX] 

faz.


60
Só funciona se sizeX for constante: int (* ário) [sizeX] = new int [sizeY] [sizeX]; Qual é o caminho certo para criar um int [sizeY] [sizeX] e onde toda a memória é contígua. (Eu não acho que isso vale a pena uma resposta, uma vez que provavelmente a sua sizeX não é constante
Johannes Schaub - litb

23
Não posso acreditar que todas as dúzias de respostas abaixo estão todas erradas e não respondem à pergunta, e ainda assim são todas aprovadas. O comentário acima de Johanes Shaub é a única resposta correta para a pergunta . Uma matriz 2D e uma matriz de ponteiro para matriz são duas coisas completamente separadas, que aparentemente todo mundo confunde.
Bregalad

6
@ JohannesSchaub-litb: Isso não é 100% correto. Certamente ela não funciona nesse caso, mas não há um método para fazê-lo funcionar em todas as dimensões variam, ver stackoverflow.com/a/29375830/103167
Ben Voigt

Respostas:


749

Uma matriz 2D dinâmica é basicamente uma matriz de ponteiros para matrizes . Você pode inicializá-lo usando um loop, assim:

int** a = new int*[rowCount];
for(int i = 0; i < rowCount; ++i)
    a[i] = new int[colCount];

O acima, para colCount= 5e rowCount = 4, produziria o seguinte:

insira a descrição da imagem aqui


143
Lembre-se de que qualquer coisa alocada com newé criada no heap e deve ser desalocada delete, apenas lembre-se disso e exclua essa memória da pilha quando terminar para evitar vazamentos.
Kekoa

83
Observe também que este é um conjunto de ponteiros. não de matrizes. O ponteiro, por sua vez, aponta para matrizes. Importante para realmente corrigir os termos, pois muitos tutoriais também entendem errado. Uma matriz de matrizes seria contígua, e essa não é
Johannes Schaub - litb

4
Sim, um T [] [N] seria chamado de "matriz de matriz [N] de T" e seria um tipo incompleto, enquanto T [] [] seria um tipo inválido (todas, exceto as últimas dimensões, devem ter um tamanho conhecido ) T [N] [M] é "matriz [N] da matriz [M] de T", enquanto o seu, T [tamanhoX] é "matriz [tamanhoX] de T", em que T é um ponteiro para um int. Criar um array 2D dinamicamente funciona assim: new int [X] [Y]. Ele criará uma matriz de um tipo alocado int [X] [Y]. Este é um "buraco" no sistema de tipo de C ++, uma vez que o sistema de tipo comum de C ++ não tem dimensões da matriz com tamanhos não conhecidos em tempo de compilação, portanto, estes são chamados de 'tipos alocados'
Johannes Schaub - litb

34
Oh meu Deus, isso é lixo completo, isso é totalmente errado. Este não é um array 2D. "Uma matriz 2D dinâmica é basicamente uma matriz de ponteiros para matrizes". - Nããão, FFS! T (*ptr)[M] = new T[N][M];é a solução correta ... Nenhuma quantidade de matrizes de-ponteiros nunca vai ser o mesmo como uma matriz-de-matrizes ...
O paramagnética Croissant

7
@TheParamagneticCroissant Você pode argumentar que não é uma matriz 2D. É verdade. Pode ser indexado como uma matriz 2D, mas não é uma matriz 2D. O layout da memória é de fato retratado na imagem. O problema com essa afirmação é que ela não funciona se M não for constante.
Mehrdad Afshari

300
int** ary = new int[sizeY][sizeX]

deveria estar:

int **ary = new int*[sizeY];
for(int i = 0; i < sizeY; ++i) {
    ary[i] = new int[sizeX];
}

e a limpeza seria:

for(int i = 0; i < sizeY; ++i) {
    delete [] ary[i];
}
delete [] ary;

EDIT: como Dietrich Epp apontou nos comentários, esta não é exatamente uma solução leve. Uma abordagem alternativa seria usar um grande bloco de memória:

int *ary = new int[sizeX*sizeY];

// ary[i][j] is then rewritten as
ary[i*sizeY+j]

56
É um pouco mais pesado do que precisa e aloca mais blocos do que você precisa. Matrizes multidimensionais precisam apenas de um bloco de memória, não precisam de um bloco por linha. A alocação de apenas um bloco torna a limpeza mais simples também.
Dietrich Epp

11
@ Kevin: Alocar apenas um único bloco contíguo é o caminho a seguir (menos impacto no alocador, melhor localidade, etc.). Mas você não precisa sacrificar a assinatura limpa. Veja stackoverflow.com/a/29375830/103167
Ben Voigt

9
Não deveria ser i*sizeX+j? Se bem me lembro, com a ordenação de linhas principais, deve ser row * numColumns + col.
Arao6

2
bom pensamento, na verdade é apenas uma questão de representação - o resto é perspectiva. inteligente
Miro Rodozov

1
@ Borna: Em geral, o uso de uma única matriz 2D será mais rápido que uma matriz de matrizes. Seguir dois ponteiros pode causar paradas no pipeline. Como sempre, isso depende dos padrões de acesso.
Dietrich Epp

211

Embora essa resposta popular ofereça a sintaxe de indexação desejada, ela é duplamente ineficiente: grande e lenta no espaço e no tempo. Existe uma maneira melhor.

Por que essa resposta é grande e lenta

A solução proposta é criar uma matriz dinâmica de ponteiros, inicializando cada ponteiro em sua própria matriz dinâmica independente. A vantagem dessa abordagem é que ela fornece a sintaxe de indexação à qual você está acostumado. Portanto, se você deseja encontrar o valor da matriz na posição x, y, você diz:

int val = matrix[ x ][ y ];

Isso funciona porque a matriz [x] retorna um ponteiro para uma matriz, que é indexada com [y]. Dividindo:

int* row = matrix[ x ];
int  val = row[ y ];

Conveniente, sim? Nós gostamos da nossa sintaxe [x] [y].

Mas a solução tem uma grande desvantagem , que é gorda e lenta.

Por quê?

A razão pela qual é gorda e lenta é realmente a mesma. Cada "linha" na matriz é uma matriz dinâmica alocada separadamente. Fazer uma alocação de heap é caro, tanto no tempo quanto no espaço. O alocador leva tempo para fazer a alocação, às vezes executando algoritmos O (n) para fazer isso. E o alocador "preenche" cada uma de suas matrizes de linha com bytes extras para contabilidade e alinhamento. Esse espaço extra custa ... bem ... espaço extra. O desalocador também levará um tempo extra quando você desalocar a matriz, liberando minuciosamente cada alocação de linha individual. Me deixa suado só de pensar nisso.

Há outra razão pela qual é lento. Essas alocações separadas tendem a viver em partes descontínuas da memória. Uma linha pode estar no endereço 1.000, outra no endereço 100.000 - você entendeu. Isso significa que, quando você está atravessando a matriz, está pulando na memória como uma pessoa selvagem. Isso tende a resultar em falhas de cache que diminuem bastante o tempo de processamento.

Portanto, se você absolutamente deve ter sua sintaxe de indexação [x] [y], use essa solução. Se você deseja rapidez e pequenez (e se não se importa com isso, por que está trabalhando em C ++?), Precisa de uma solução diferente.

Uma solução diferente

A melhor solução é alocar toda a sua matriz como uma única matriz dinâmica e, em seguida, use sua (ligeiramente) matemática de indexação inteligente para acessar as células. A matemática da indexação é apenas um pouco inteligente; nah, não é nada inteligente: é óbvio.

class Matrix
{
    ...
    size_t index( int x, int y ) const { return x + m_width * y; }
};

Dada esta index()função (que eu imagino ser membro de uma classe porque precisa conhecer a m_widthmatriz), você pode acessar células dentro de sua matriz. A matriz matriz é alocada assim:

array = new int[ width * height ];

Portanto, o equivalente a isso na solução lenta e gorda:

array[ x ][ y ]

... é isso na solução pequena e rápida:

array[ index( x, y )]

Triste, eu sei. Mas você vai se acostumar. E sua CPU agradecerá.


5
@ Naoein, eu meio que desenhei uma solução sem prescrever uma em particular. Mais detalhes podem parecer: class Matrix { int* array; int m_width; public: Matrix( int w, int h ) : m_width( w ), array( new int[ w * h ] ) {} ~Matrix() { delete[] array; } int at( int x, int y ) const { return array[ index( x, y ) ]; } protected: int index( int x, int y ) const { return x + m_width * y; } };Se você endireitar esse código, pode fazer sentido e esclarecer a resposta acima.
OldPeculier

4
Eu gosto muito desta solução, ela também é aplicável à matriz de 3 dimensões? Estou pensando em algo como isto: (x + m_width * y) + (m_width * m_height * z)
Ulrar

3
O principal problema com esta solução é que há computação extra para cada índice. Fica pior se você colocar o cálculo do índice em uma função que adiciona sobrecarga extra. Pelo menos, considere usar macros ou funções embutidas para reduzir a sobrecarga. Um exemplo de macro para C ++: #define ROW_COL_TO_INDEX(row, col, num_cols) (row*num_cols + col)Você pode usá-lo como int COLS = 4; A[ ROW_COL_TO_INDEX(r, c, COLS) ] = 75; A sobrecarga realmente afeta quando fazemos multiplicações de matrizes que são de complexidade O (n ^ 3) ou O (n ^ 2,81) para o algoritmo de Strassen .
Ash Ketchum

5
O @AshKetchum Inlining (ou talvez a substituição de macros) faz sentido otimizar, mas como a computação compilada é mais complexa do que o que precisa ser feito para resolver o endereço de um [x] [y]?
Dronz

1
@Dronz Com a[x][y], o que você está realmente fazendo é *(*(a + x) + y): duas adições e duas buscas na memória. Com a[index(x, y)], o que você está realmente fazendo é *(a + x + w*y): duas adições, uma multiplicação e uma busca de memória. O último é geralmente preferível, pelas razões expostas nesta resposta (ou seja, negociar a busca de memória extra com uma multiplicação vale a pena, especialmente porque os dados não são fragmentados e, portanto, você não perde o cache).
Boris Dalstein 03/02

118

No C ++ 11 é possível:

auto array = new double[M][N]; 

Dessa forma, a memória não é inicializada. Para inicializar, faça o seguinte:

auto array = new double[M][N]();

Programa de exemplo (compile com "g ++ -std = c ++ 11"):

#include <iostream>
#include <utility>
#include <type_traits>
#include <typeinfo>
#include <cxxabi.h>
using namespace std;

int main()
{
    const auto M = 2;
    const auto N = 2;

    // allocate (no initializatoin)
    auto array = new double[M][N];

    // pollute the memory
    array[0][0] = 2;
    array[1][0] = 3;
    array[0][1] = 4;
    array[1][1] = 5;

    // re-allocate, probably will fetch the same memory block (not portable)
    delete[] array;
    array = new double[M][N];

    // show that memory is not initialized
    for(int r = 0; r < M; r++)
    {
        for(int c = 0; c < N; c++)
            cout << array[r][c] << " ";
        cout << endl;
    }
    cout << endl;

    delete[] array;

    // the proper way to zero-initialize the array
    array = new double[M][N]();

    // show the memory is initialized
    for(int r = 0; r < M; r++)
    {
        for(int c = 0; c < N; c++)
            cout << array[r][c] << " ";
        cout << endl;
    }

    int info;
    cout << abi::__cxa_demangle(typeid(array).name(),0,0,&info) << endl;

    return 0;
}

Resultado:

2 4 
3 5 

0 0 
0 0 
double (*) [2]

3
Eu preciso fazer isso em uma classe, portanto, não posso usar auto. Qual seria o tipo adequado para a matriz?
Peter Smit 27/05

3
Você pode usar isso então:using arr2d = double(*)[2]; arr2d array = new double[M][N];
Mohammad Alaggan

3
+1: é isso que o OP solicitou. O tipo apropriado para isso é M double (*)[M][N]ou double(*)[][N]N, sendo expressões constantes.
Fozi 30/09

58
O problema com esta solução é que as dimensões não podem ser um valor de tempo de execução, mas devem ser conhecidas em tempo de compilação.
Legends2k 26/10/2013

4
@ vsoftco Sim, de fato , no entanto, a pergunta era especificamente sobre ter as duas dimensões desconhecidas no momento da compilação.
Legends2k

58

Presumo que, a partir do seu exemplo de matriz estática, você deseja uma matriz retangular, e não irregular. Você pode usar o seguinte:

int *ary = new int[sizeX * sizeY];

Então você pode acessar elementos como:

ary[y*sizeX + x]

Não esqueça de usar delete [] ary.


1
Essa é uma boa maneira de fazer isso. Você também pode criar um vetor <int> com tamanho sizeX * sizeY para uma segurança extra agradável.
Dietrich Epp

4
A melhor coisa é agrupar esse código em uma classe - você pode executar uma limpeza no destruidor e implementar métodos get (x, y) e set (x, y, val) em vez de forçar o usuário a fazer a multiplicação sozinho . Operador de implementação [] é mais complicado, mas acredito que é possível.
Tadeusz Kopec

47

Existem duas técnicas gerais que eu recomendaria para isso no C ++ 11 e superior, uma para dimensões de tempo de compilação e outra para tempo de execução. Ambas as respostas assumem que você deseja matrizes uniformes e bidimensionais (e não irregulares).

Dimensões do tempo de compilação

Use um std::arrayde std::arraye, em seguida, use-o newpara colocá-lo na pilha:

// the alias helps cut down on the noise:
using grid = std::array<std::array<int, sizeX>, sizeY>;
grid * ary = new grid;

Novamente, isso só funciona se os tamanhos das dimensões forem conhecidos em tempo de compilação.

Dimensões do tempo de execução

A melhor maneira de realizar uma matriz bidimensional com tamanhos conhecidos apenas em tempo de execução é agrupá-la em uma classe. A classe alocará uma matriz 1d e sobrecarregará operator []para fornecer indexação para a primeira dimensão. Isso funciona porque, em C ++, uma matriz 2D é de linha principal:

 matriz mostrada na forma lógica e na forma unidimensional

(Retirado de http://eli.thegreenplace.net/2015/memory-layout-of-multi-dimensional-arrays/ )

Uma sequência contígua de memória é boa por razões de desempenho e também é fácil de limpar. Aqui está um exemplo de classe que omite muitos métodos úteis, mas mostra a ideia básica:

#include <memory>

class Grid {
  size_t _rows;
  size_t _columns;
  std::unique_ptr<int[]> data;

public:
  Grid(size_t rows, size_t columns)
      : _rows{rows},
        _columns{columns},
        data{std::make_unique<int[]>(rows * columns)} {}

  size_t rows() const { return _rows; }

  size_t columns() const { return _columns; }

  int *operator[](size_t row) { return row * _columns + data.get(); }

  int &operator()(size_t row, size_t column) {
    return data[row * _columns + column];
  }
}

Então, criamos uma matriz com std::make_unique<int[]>(rows * columns)entradas. Sobrecarregamos o operator []que indexará a linha para nós. Retorna um int *que aponta para o início da linha, que pode ser desreferenciado como normal para a coluna. Observe que o make_uniqueprimeiro é enviado no C ++ 14, mas você pode preenchê-lo no C ++ 11, se necessário.

Também é comum que esses tipos de estruturas sobrecarreguem operator()também:

  int &operator()(size_t row, size_t column) {
    return data[row * _columns + column];
  }

Tecnicamente, não usei newaqui, mas é trivial passar de std::unique_ptr<int[]>para int *e usar new/ delete.


seria possível definir os parâmetros do modelo de linhas e colunas?
Janus Troelsen

1
Se você conhece as dimensões em tempo de compilação (que você faz se você estiver usando parâmetros do modelo), então eu recomendo usar um std::arrayde std::arrays: std::array<std::array<int, columns> rows>.
Levi Morrison

1
Posso dizer que esta é uma resposta sofisticada / moderna, com uma filosofia muito semelhante à resposta (mais simples, em termos de LOC e conceitos) dada por @kamshi?
KcFnMi

Eles são bastante comparáveis ​​com relação à técnica subjacente: existe uma única matriz que mantém todos os valores em todas as dimensões. De alguma forma, retorne os ponteiros para o início de cada linha. Na prática, a classe geralmente terá métodos mais úteis, possivelmente possui operadores de construtor de cópia e atribuição de cópia, assertspara compilações de depuração para verificar acessos à memória, etc. Essas adições geralmente tornam mais fácil e agradável trabalhar.
Levi Morrison

1
Além disso, @KcFnMi, se você decidir usar a resposta de kamshi , não deixe de ler o comentário de Ben sobre o uso em make_uniquevez de new/delete.
Levi Morrison

31

Essa pergunta estava me incomodando - é um problema bastante comum que uma boa solução já deveria existir, algo melhor que o vetor de vetores ou rolando sua própria indexação de matriz.

Quando algo deveria existir em C ++, mas não existe, o primeiro lugar para procurar é boost.org . Lá encontrei a Boost Multidimensional Array Librarymulti_array ,. Inclusive inclui uma multi_array_refclasse que pode ser usada para quebrar seu próprio buffer de matriz unidimensional.


5
Eu entendo o seu argumento, eu pessoalmente não entendo por que tem que ser tão difícil, honestamente, essa é a razão pela qual perdemos tantos programadores para Java. Esses são os recursos básicos que fazem os caras do c ++ perderem tempo!
Oliver

Posso apenas acrescentar, acho a melhor solução, mas acho que para algumas pessoas é necessário muito cérebro para entender todas as etapas, especialmente para iniciantes ...;)! Vejo 80% dos programadores de C ++ falharem quando veem coisas digitadas.
Oliver

1
@OliverStutz, esse é o perigo de ser o primeiro. Os padrões C ++ mais recentes têm tentado desesperadamente tornar as coisas menos onerosas, a minha favorita é a autopalavra - chave. Estou surpreso que eles não tenham tentado lidar com matrizes 2D, especialmente porque o Boost já mostrou o caminho.
Mark Ransom

é engraçado como toda a automação de desenvolvimento foi incorporada. Agora, a única vantagem fácil e imediata é o java. Eu realmente gostaria que o c ++ desse um salto, tem sido uma linguagem poderosa e poderosa desde sempre ... por que ter um sabre de luz se você não o usa!
Oliver

2
O que está me incomodando ainda mais é o quão longe o C ++ está atrás do C nisso: o C99 permite matrizes multidimensionais reais alocadas no heap com dimensões definidas em tempo de execução, e o C ++ 17 ainda não chega nem perto do que o C99 permite ...
cmaster - reinstate monica

27

Por que não usar STL: vector? Tão fácil e você não precisa excluir o vetor.

int rows = 100;
int cols = 200;
vector< vector<int> > f(rows, vector<int>(cols));
f[rows - 1][cols - 1] = 0; // use it like arrays

Você também pode inicializar as 'matrizes', apenas dê um valor padrão

const int DEFAULT = 1234;
vector< vector<int> > f(rows, vector<int>(cols, DEFAULT));

Fonte: Como criar matrizes dimensionais 2, 3 (ou multi) em C / C ++?


1
melhor solução IMHO
vipin8169 03/11

1
Esta não é uma boa solução se não desejar carregar o STL devido a restrições de memória.
Katta

2
@katta A maioria dos programas não triviais de C ++ usa STL de qualquer maneira, portanto, esta é uma boa solução, mas não para um número menor de casos, incluindo o seu.
Zar Shardan

O que não entendo é por que tantas pessoas pensam no primeiro índice como as linhas e no segundo como as colunas. Rebelião contra diagramas de coordenadas XY na aula de matemática?
Dronz 15/0518

2
@Ronz É porque esse é o modelo de memória C ++ - as colunas são contíguas na memória, não as linhas. Em Fortran, é o contrário.
Levi Morrison

16

Uma matriz 2D é basicamente uma matriz 1D de ponteiros, onde cada ponteiro está apontando para uma matriz 1D, que retém os dados reais.

Aqui N é linha e M é coluna.

alocação dinâmica

int** ary = new int*[N];
  for(int i = 0; i < N; i++)
      ary[i] = new int[M];

preencher

for(int i = 0; i < N; i++)
    for(int j = 0; j < M; j++)
      ary[i][j] = i;

impressão

for(int i = 0; i < N; i++)
    for(int j = 0; j < M; j++)
      std::cout << ary[i][j] << "\n";

livre

for(int i = 0; i < N; i++)
    delete [] ary[i];
delete [] ary;

14

Como alocar uma matriz multidimensional contígua no GNU C ++? Há uma extensão GNU que permite que a sintaxe "padrão" funcione.

Parece que o problema vem do operador new []. Certifique-se de usar o operador new:

double (* in)[n][n] = new (double[m][n][n]);  // GNU extension

E é tudo: você obtém uma matriz multidimensional compatível com C ...


Qual compilador você está usando? A sintaxe da matriz compila e executa bem com o g ++ 4.6.4 e 4.7.3. Estou apenas recebendo um aviso no último] antes de = = "que o valor calculado não é usado" ou "a instrução não tem efeito". No entanto, se eu usar o g ++ 4.8.1 (supostamente totalmente compatível com c ++ 11), ele gera erros em n e não sendo constante "o tamanho da matriz no operador novo deve ser constante" e aponta para o último] da linha.
precisa saber é o seguinte

@cmaster double (*in)[m][n] = (double (*)[m][n])new double[k*m*n];também não funciona. Estou recebendo erros C2057, C2540 nporque não é conhecido no momento da compilação. Não entendo por que não posso fazê-lo, porque a memória é alocada corretamente e são apenas indicadores para lidar com essa memória convenientemente. (VS 2010)
user1234567

2
@ user3241228 gccme enganou quando escrevi o seguinte: o fornecimento -std=c++11não é suficiente para ativar a conformidade padrão estrita, também -pedantic-errorsé necessário. Sem a bandeira posterior, ele gccaceita o elenco com satisfação, mesmo que não esteja de acordo com o padrão C ++. Com o que sei agora, só posso aconselhar voltar a C ao fazer coisas que dependem fortemente de matrizes multidimensionais. O C99 é apenas muito mais poderoso nesse aspecto do que o C ++ 17 será.
cmaster - reinstate monica

VLAs @cmaster alocados dinamicamente são açúcar sintático de qualquer maneira ... Eles são bons em C, porque não há mais nada, mas C ++ tem açúcar melhor sintática :)
MM

1
@MM Pena que o C ++ não tenha açúcar sintático para uma matriz multidimensional verdadeira e consecutiva alocada no heap com tamanhos que são conhecidos apenas em tempo de execução. Contanto que você não precise disso, o açúcar sintático em C ++ é bom. Mas quando você precisa de todo o exposto, mesmo batidas Fortran C ++ ...
cmaster - Reintegrar monica

13

typedef é seu amigo

Depois de voltar e examinar muitas das outras respostas, descobri que há uma explicação mais profunda, pois muitas das outras respostas sofrem de problemas de desempenho ou forçam você a usar uma sintaxe incomum ou onerosa para declarar a matriz ou acessar a matriz elementos (ou todos os itens acima).

Primeiro, esta resposta assume que você conhece as dimensões da matriz em tempo de compilação. Se o fizer, esta é a melhor solução, pois fornecerá o melhor desempenho e permitirá que você use a sintaxe padrão da matriz para acessar os elementos da matriz .

A razão pela qual isso oferece o melhor desempenho é porque aloca todas as matrizes como um bloco de memória contíguo, o que significa que você provavelmente terá menos falhas de página e melhor localidade espacial. A alocação em um loop pode fazer com que as matrizes individuais acabem espalhadas em várias páginas não contíguas pelo espaço de memória virtual, pois o loop de alocação pode ser interrompido (possivelmente várias vezes) por outros threads ou processos, ou simplesmente devido à discrição do alocador preenchendo pequenos blocos de memória vazios disponíveis.

Os outros benefícios são uma sintaxe de declaração simples e uma sintaxe de acesso à matriz padrão.

Em C ++ usando new:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {

typedef double (array5k_t)[5000];

array5k_t *array5k = new array5k_t[5000];

array5k[4999][4999] = 10;
printf("array5k[4999][4999] == %f\n", array5k[4999][4999]);

return 0;
}

Ou estilo C usando calloc:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {

typedef double (*array5k_t)[5000];

array5k_t array5k = calloc(5000, sizeof(double)*5000);

array5k[4999][4999] = 10;
printf("array5k[4999][4999] == %f\n", array5k[4999][4999]);

return 0;
}

1
Acessar além do final de uma matriz não é garantido para causar um erro. Se você tiver sorte, o programa irá travar. Você definitivamente está no reino do comportamento indefinido.
Michael Kristofik

É verdade, embora o objetivo deste exemplo seja realmente apenas mostrar como usar typedef e new juntos para declarar uma matriz 2D.
237 Robert S. Barnes

1
Eu gostei muito da sua resposta. Eu próprio fui um defensor do typedef.
Fooo

12

Esse problema me incomoda há 15 anos e todas as soluções fornecidas não foram satisfatórias para mim. Como você cria uma matriz multidimensional dinâmica contiguamente na memória? Hoje finalmente encontrei a resposta. Usando o código a seguir, você pode fazer exatamente isso:

#include <iostream>

int main(int argc, char** argv)
{
    if (argc != 3)
    {
        std::cerr << "You have to specify the two array dimensions" << std::endl;
        return -1;
    }

    int sizeX, sizeY;

    sizeX = std::stoi(argv[1]);
    sizeY = std::stoi(argv[2]);

    if (sizeX <= 0)
    {
        std::cerr << "Invalid dimension x" << std::endl;
        return -1;
    }
    if (sizeY <= 0)
    {
        std::cerr << "Invalid dimension y" << std::endl;
        return -1;
    }

    /******** Create a two dimensional dynamic array in continuous memory ******
     *
     * - Define the pointer holding the array
     * - Allocate memory for the array (linear)
     * - Allocate memory for the pointers inside the array
     * - Assign the pointers inside the array the corresponding addresses
     *   in the linear array
     **************************************************************************/

    // The resulting array
    unsigned int** array2d;

    // Linear memory allocation
    unsigned int* temp = new unsigned int[sizeX * sizeY];

    // These are the important steps:
    // Allocate the pointers inside the array,
    // which will be used to index the linear memory
    array2d = new unsigned int*[sizeY];

    // Let the pointers inside the array point to the correct memory addresses
    for (int i = 0; i < sizeY; ++i)
    {
        array2d[i] = (temp + i * sizeX);
    }



    // Fill the array with ascending numbers
    for (int y = 0; y < sizeY; ++y)
    {
        for (int x = 0; x < sizeX; ++x)
        {
            array2d[y][x] = x + y * sizeX;
        }
    }



    // Code for testing
    // Print the addresses
    for (int y = 0; y < sizeY; ++y)
    {
        for (int x = 0; x < sizeX; ++x)
        {
            std::cout << std::hex << &(array2d[y][x]) << ' ';
        }
    }
    std::cout << "\n\n";

    // Print the array
    for (int y = 0; y < sizeY; ++y)
    {
        std::cout << std::hex << &(array2d[y][0]) << std::dec;
        std::cout << ": ";
        for (int x = 0; x < sizeX; ++x)
        {
            std::cout << array2d[y][x] << ' ';
        }
        std::cout << std::endl;
    }



    // Free memory
    delete[] array2d[0];
    delete[] array2d;
    array2d = nullptr;

    return 0;
}

Quando você invoca o programa com os valores sizeX = 20 e sizeY = 15, a saída será a seguinte:

0x603010 0x603014 0x603018 0x60301c 0x603020 0x603024 0x603028 0x60302c 0x603030 0x603034 0x603038 0x60303c 0x603040 0x603044 0x603048 0x60304c 0x603050 0x603054 0x603058 0x60305c 0x603060 0x603064 0x603068 0x60306c 0x603070 0x603074 0x603078 0x60307c 0x603080 0x603084 0x603088 0x60308c 0x603090 0x603094 0x603098 0x60309c 0x6030a0 0x6030a4 0x6030a8 0x6030ac 0x6030b0 0x6030b4 0x6030b8 0x6030bc 0x6030c0 0x6030c4 0x6030c8 0x6030cc 0x6030d0 0x6030d4 0x6030d8 0x6030dc 0x6030e0 0x6030e4 0x6030e8 0x6030ec 0x6030f0 0x6030f4 0x6030f8 0x6030fc 0x603100 0x603104 0x603108 0x60310c 0x603110 0x603114 0x603118 0x60311c 0x603120 0x603124 0x603128 0x60312c 0x603130 0x603134 0x603138 0x60313c 0x603140 0x603144 0x603148 0x60314c 0x603150 0x603154 0x603158 0x60315c 0x603160 0x603164 0x603168 0x60316c 0x603170 0x603174 0x603178 0x60317c 0x603180 0x603184 0x603188 0x60318c 0x603190 0x603194 0x603198 0x60319c 0x6031a0 0x6031a4 0x6031a8 0x6031ac 0x6031b0 0x6031b4 0x6031b8 0x6031bc 0x6031c0 0x6031c4 0x6031c8 0x6031cc 0x6031d0 0x6031d4 0x6031d8 0x6031dc 0x6031e0 0x6031e4 0x6031e8 0x6031ec 0x6031f0 0x6031f4 0x6031f8 0x6031fc 0x603200 0x603204 0x603208 0x60320c 0x603210 0x603214 0x603218 0x60321c 0x603220 0x603224 0x603228 0x60322c 0x603230 0x603234 0x603238 0x60323c 0x603240 0x603244 0x603248 0x60324c 0x603250 0x603254 0x603258 0x60325c 0x603260 0x603264 0x603268 0x60326c 0x603270 0x603274 0x603278 0x60327c 0x603280 0x603284 0x603288 0x60328c 0x603290 0x603294 0x603298 0x60329c 0x6032a0 0x6032a4 0x6032a8 0x6032ac 0x6032b0 0x6032b4 0x6032b8 0x6032bc 0x6032c0 0x6032c4 0x6032c8 0x6032cc 0x6032d0 0x6032d4 0x6032d8 0x6032dc 0x6032e0 0x6032e4 0x6032e8 0x6032ec 0x6032f0 0x6032f4 0x6032f8 0x6032fc 0x603300 0x603304 0x603308 0x60330c 0x603310 0x603314 0x603318 0x60331c 0x603320 0x603324 0x603328 0x60332c 0x603330 0x603334 0x603338 0x60333c 0x603340 0x603344 0x603348 0x60334c 0x603350 0x603354 0x603358 0x60335c 0x603360 0x603364 0x603368 0x60336c 0x603370 0x603374 0x603378 0x60337c 0x603380 0x603384 0x603388 0x60338c 0x603390 0x603394 0x603398 0x60339c 0x6033a0 0x6033a4 0x6033a8 0x6033ac 0x6033b0 0x6033b4 0x6033b8 0x6033bc 0x6033c0 0x6033c4 0x6033c8 0x6033cc 0x6033d0 0x6033d4 0x6033d8 0x6033dc 0x6033e0 0x6033e4 0x6033e8 0x6033ec 0x6033f0 0x6033f4 0x6033f8 0x6033fc 0x603400 0x603404 0x603408 0x60340c 0x603410 0x603414 0x603418 0x60341c 0x603420 0x603424 0x603428 0x60342c 0x603430 0x603434 0x603438 0x60343c 0x603440 0x603444 0x603448 0x60344c 0x603450 0x603454 0x603458 0x60345c 0x603460 0x603464 0x603468 0x60346c 0x603470 0x603474 0x603478 0x60347c 0x603480 0x603484 0x603488 0x60348c 0x603490 0x603494 0x603498 0x60349c 0x6034a0 0x6034a4 0x6034a8 0x6034ac 0x6034b0 0x6034b4 0x6034b8 0x6034bc 

0x603010: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 
0x603060: 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 
0x6030b0: 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 
0x603100: 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 
0x603150: 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 
0x6031a0: 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 
0x6031f0: 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 
0x603240: 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 
0x603290: 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 
0x6032e0: 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 
0x603330: 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 
0x603380: 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 
0x6033d0: 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 
0x603420: 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 
0x603470: 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299

Como você pode ver, a matriz multidimensional está contígua na memória e não há dois endereços de memória sobrepostos. Mesmo a rotina para liberar a matriz é mais simples que a maneira padrão de alocar dinamicamente a memória para cada coluna (ou linha, dependendo de como você visualiza a matriz). Como a matriz consiste basicamente em duas matrizes lineares, somente essas duas precisam ser (e podem ser) liberadas.

Este método pode ser estendido para mais de duas dimensões com o mesmo conceito. Não vou fazer isso aqui, mas quando você entende a ideia, é uma tarefa simples.

Espero que este código o ajude tanto quanto me ajudou.


1
Ainda há uma variedade extra de indicadores. O código que usa a matriz precisa fazer uma camada extra de indireção, porque não pode assumir array2d[i] = buffer + i * sizeX. Portanto, isso ajuda um pouco, mas no código usando a matriz, o compilador não pode apenas incrementar ponteiros para varrer a matriz.
22826 Peter Cordes

4
Sim, é exatamente assim que se faz. Mas é a maneira C de fazer isso, em C ++, usamos make_unique<int[]>(sizeX*sizeY)para configurar o armazenamento contíguo e make_unique<int*[]>(sizeX)configurar o armazenamento para os ponteiros (que devem ser atribuídos da mesma maneira que você mostra). Isso libera você do requisito de ligar delete[]duas vezes no final.
Ben Voigt 21/01

Essa resposta está meio que fazendo muito sentido para mim, ainda mais considerando o comentário do @BenVoigt. A matriz extra de ponteiros @PeterCordes se refere, não é temp? Considerando os benefícios (array 2d contínuo com dimensões desconhecidas em tempo de compilação), não tenho certeza se me importo de tê-lo pendurado. Eu não entendi o que @ PeterCordes quer dizer com extra layer of indirection, o que é? Por que o parêntese array2d[i] = (temp + i * sizeX);
KcFnMi 01/08/19

delete [] array2d [0] é o mesmo que delete [] temp?
KcFnMi

6

O objetivo desta resposta não é adicionar nada novo que os outros ainda não abordem, mas estender a resposta de @Kevin Loney.

Você pode usar a declaração leve:

int *ary = new int[SizeX*SizeY]

e sintaxe de acesso será:

ary[i*SizeY+j]     // ary[i][j]

mas isso é complicado para a maioria e pode causar confusão. Portanto, você pode definir uma macro da seguinte maneira:

#define ary(i, j)   ary[(i)*SizeY + (j)]

Agora você pode acessar a matriz usando a sintaxe muito semelhante ary(i, j) // means ary[i][j]. Isso tem as vantagens de ser simples e bonito e, ao mesmo tempo, usar expressões no lugar dos índices também é mais simples e menos confuso.

Para acessar, digamos, ary [2 + 5] [3 + 8], você pode escrever em ary(2+5, 3+8)vez da aparência complexa, ary[(2+5)*SizeY + (3+8)]ou seja, salva parênteses e ajuda na legibilidade.

Ressalvas:

  • Embora a sintaxe seja muito semelhante, NÃO é a mesma.
  • Caso você passe o array para outras funções, SizeYele deve ser passado com o mesmo nome (ou, em vez disso, ser declarado como uma variável global).

Ou, se você precisar usar a matriz em várias funções, poderá adicionar SizeY também como outro parâmetro na definição de macro da seguinte forma:

#define ary(i, j, SizeY)  ary[(i)*(SizeY)+(j)]

Você entendeu a ideia. Obviamente, isso se torna muito longo para ser útil, mas ainda pode impedir a confusão de + e *.

Definitivamente, isso não é recomendado e será condenado como má prática pela maioria dos usuários experientes, mas não pude resistir a compartilhá-lo devido à sua elegância.

Edit:
Se você deseja uma solução portátil que funcione para qualquer número de matrizes, você pode usar esta sintaxe:

#define access(ar, i, j, SizeY) ar[(i)*(SizeY)+(j)]

e, em seguida, você pode transmitir qualquer matriz para a chamada, com qualquer tamanho, usando a sintaxe de acesso:

access(ary, i, j, SizeY)      // ary[i][j]

PS: Eu testei isso, e a mesma sintaxe funciona (como lvalue e rvalue) nos compiladores g ++ 14 e g ++ 11.


4

Tente fazer isso:

int **ary = new int* [sizeY];
for (int i = 0; i < sizeY; i++)
    ary[i] = new int[sizeX];

2

Aqui, eu tenho duas opções. O primeiro mostra o conceito de uma matriz de matrizes ou ponteiro de ponteiros. Prefiro o segundo porque os endereços são contíguos, como você pode ver na imagem.

insira a descrição da imagem aqui

#include <iostream>

using namespace std;


int main(){

    int **arr_01,**arr_02,i,j,rows=4,cols=5;

    //Implementation 1
    arr_01=new int*[rows];

    for(int i=0;i<rows;i++)
        arr_01[i]=new int[cols];

    for(i=0;i<rows;i++){
        for(j=0;j<cols;j++)
            cout << arr_01[i]+j << " " ;
        cout << endl;
    }


    for(int i=0;i<rows;i++)
        delete[] arr_01[i];
    delete[] arr_01;


    cout << endl;
    //Implementation 2
    arr_02=new int*[rows];
    arr_02[0]=new int[rows*cols];
    for(int i=1;i<rows;i++)
        arr_02[i]=arr_02[0]+cols*i;

    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++)
            cout << arr_02[i]+j << " " ;
        cout << endl;
    }

    delete[] arr_02[0];
    delete[] arr_02;


    return 0;
}

1

Se seu projeto for CLI (Common Language Runtime Support) , então:

Você pode usar a classe array, não aquela que você obtém ao escrever:

#include <array>
using namespace std;

Em outras palavras, não a classe de matriz não gerenciada que você obtém ao usar o espaço para nome std e ao incluir o cabeçalho da matriz, não a classe de matriz não gerenciada definida no espaço de nome std e no cabeçalho da matriz, mas a matriz de classe gerenciada da CLI.

com essa classe, você pode criar uma matriz de qualquer classificação que desejar.

O código a seguir abaixo cria uma nova matriz bidimensional de 2 linhas e 3 colunas e do tipo int, e o nomeio "arr":

array<int, 2>^ arr = gcnew array<int, 2>(2, 3);

Agora você pode acessar os elementos da matriz, nomeie-os e escreva apenas um parênteses ao quadrado []e, dentro deles, adicione a linha e a coluna e separe-os com a vírgula ,.

O código a seguir abaixo acessa um elemento na 2ª linha e na 1ª coluna da matriz que eu já criei no código anterior acima:

arr[0, 1]

escrever apenas esta linha é ler o valor nessa célula, ou seja, obter o valor nessa célula, mas se você adicionar o =sinal de igual , está prestes a escrever o valor nessa célula, ou seja, definir o valor nessa célula. Você também pode usar os operadores + =, - =, * = e / =, é claro, apenas para números (int, float, double, __int16, __int32, __int64 e etc), mas com certeza já o conhece.

Se o seu projeto não for CLI, você poderá usar a classe de matriz não gerenciada do espaço de nome std, se é #include <array>claro, mas o problema é que essa classe de matriz é diferente da matriz da CLI. Criar matriz desse tipo é igual à CLI, exceto que você precisará remover o ^sinal e a gcnewpalavra - chave. Mas, infelizmente, o segundo parâmetro int entre <>parênteses especifica o comprimento (ou seja, tamanho) da matriz, não sua classificação!

Não há como especificar classificação neste tipo de matriz, a classificação é apenas o recurso da matriz CLI . .

matriz std se comporta como matriz normal em c ++, que você define com ponteiro, por exemplo int*e depois new int[size]:, ou sem ponteiro:, int arr[size]mas diferente da matriz normal do c ++, a matriz std fornece funções que você pode usar com os elementos da matriz, como preenchimento, início, fim, tamanho e etc., mas a matriz normal não fornece nada .

Mas ainda o array std é um array dimensional, como os arrays c ++ normais. Mas, graças às soluções sugeridas pelos outros funcionários sobre como você pode criar o array unidimensional c ++ normal para o array bidimensional, podemos adaptar as mesmas idéias ao array std, por exemplo, de acordo com a idéia de Mehrdad Afshari, podemos escrever o seguinte código:

array<array<int, 3>, 2> array2d = array<array<int, 3>, 2>();

Essa linha de código cria uma "matriz jugged" , que é uma matriz unidimensional em que cada uma de suas células é ou aponta para outra matriz unidimensional.

Se todas as matrizes unidimensionais em uma matriz dimensional são iguais em seu comprimento / tamanho, você pode tratar a variável array2d como uma matriz bidimensional real, além de usar métodos especiais para tratar linhas ou colunas, depende de como você a visualiza. em mente, na matriz 2D, essa matriz padrão suporta.

Você também pode usar a solução de Kevin Loney:

int *ary = new int[sizeX*sizeY];

// ary[i][j] is then rewritten as
ary[i*sizeY+j]

mas se você usar a matriz std, o código deverá ser diferente:

array<int, sizeX*sizeY> ary = array<int, sizeX*sizeY>();
ary.at(i*sizeY+j);

E ainda tem as funções exclusivas da matriz std.

Observe que você ainda pode acessar os elementos da matriz std usando os []parênteses e não precisa chamar a atfunção. Você também pode definir e atribuir uma nova variável int que irá calcular e manter o número total de elementos na matriz std e usar seu valor, em vez de repetirsizeX*sizeY

Você pode definir sua própria classe genérica de matriz bidimensional e definir o construtor da classe de matriz bidimensional para receber dois números inteiros para especificar o número de linhas e colunas na nova matriz bidimensional e definir a função get que recebe dois parâmetros de número inteiro que acessam um elemento na matriz bidimensional e retornam seu valor e definem a função que recebe três parâmetros, que os dois primeiros são números inteiros que especificam a linha e a coluna na matriz bidimensional e o terceiro parâmetro é o novo valor do elemento. Seu tipo depende do tipo que você escolheu na classe genérica.

Você será capaz de implementar tudo isso usando tanto o c normais ++ matriz (ponteiros ou sem) ou a matriz de DST e uso uma das ideias que outras pessoas sugeridas, e torná-lo fácil de usar como a matriz cli, ou como os dois matriz dimensional que você pode definir, atribuir e usar em C #.


1

Comece definindo a matriz usando ponteiros (Linha 1):

int** a = new int* [x];     //x is the number of rows
for(int i = 0; i < x; i++)
    a[i] = new int[y];     //y is the number of columns

1

O exemplo abaixo pode ajudar,

int main(void)
{
    double **a2d = new double*[5]; 
    /* initializing Number of rows, in this case 5 rows) */
    for (int i = 0; i < 5; i++)
    {
        a2d[i] = new double[3]; /* initializing Number of columns, in this case 3 columns */
    }

    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            a2d[i][j] = 1; /* Assigning value 1 to all elements */
        }
    }

    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            cout << a2d[i][j] << endl;  /* Printing all elements to verify all elements have been correctly assigned or not */
        }
    }

    for (int i = 0; i < 5; i++)
        delete[] a2d[i];

    delete[] a2d;


    return 0;
}

1

Se você deseja uma matriz 2D de números inteiros, cujos elementos são alocados seqüencialmente na memória, declare-a como

int (*intPtr)[n] = new int[x][n]

onde, em vez de x, você pode escrever qualquer dimensão, mas n deve ser o mesmo em dois lugares. Exemplo

int (*intPtr)[8] = new int[75][8];
intPtr[5][5] = 6;
cout<<intPtr[0][45]<<endl;

deve imprimir 6.


0

Eu deixei você com uma solução que funciona melhor para mim, em certos casos. Especialmente se alguém souber [o tamanho de?] Uma dimensão da matriz. Muito útil para uma matriz de caracteres, por exemplo, se precisarmos de uma matriz de tamanho variável de matrizes de caracteres [20].

int  size = 1492;
char (*array)[20];

array = new char[size][20];
...
strcpy(array[5], "hola!");
...
delete [] array;

A chave são os parênteses na declaração da matriz.


O StackOverflow usa apenas inglês, por favor, traduza sua pergunta.
M. Mimpen

0

Usei este sistema não elegante, mas RÁPIDO, FÁCIL e TRABALHADOR. Não vejo por que não pode funcionar porque a única maneira do sistema permitir criar uma matriz de tamanho grande e acessar peças é sem cortá-la em partes:

#define DIM 3
#define WORMS 50000 //gusanos

void halla_centros_V000(double CENW[][DIM])
{
    CENW[i][j]=...
    ...
}


int main()
{
    double *CENW_MEM=new double[WORMS*DIM];
    double (*CENW)[DIM];
    CENW=(double (*)[3]) &CENW_MEM[0];
    halla_centros_V000(CENW);
    delete[] CENW_MEM;
}

0

Não sei ao certo se a resposta a seguir não foi fornecida, mas decidi adicionar algumas otimizações locais à alocação da matriz 2D (por exemplo, uma matriz quadrada é feita através de apenas uma alocação): int** mat = new int*[n]; mat[0] = new int [n * n];

No entanto, a exclusão é assim devido à linearidade da alocação acima: delete [] mat[0]; delete [] mat;


Já mencionado em uma resposta a esta mesma pergunta: stackoverflow.com/a/27672888/103167 e uma versão de ponteiro inteligente aqui: stackoverflow.com/a/29375830/103167
Ben Voigt

-1

declarando matriz 2D dinamicamente:

    #include<iostream>
    using namespace std;
    int main()
    {
        int x = 3, y = 3;

        int **ptr = new int *[x];

        for(int i = 0; i<y; i++)
        {
            ptr[i] = new int[y];
        }
        srand(time(0));

        for(int j = 0; j<x; j++)
        {
            for(int k = 0; k<y; k++)
            {
                int a = rand()%10;
                ptr[j][k] = a;
                cout<<ptr[j][k]<<" ";
            }
            cout<<endl;
        }
    }

Agora, no código acima, pegamos um ponteiro duplo e atribuímos a ele uma memória dinâmica e fornecemos um valor para as colunas. Aqui a memória alocada é apenas para as colunas, agora para as linhas, precisamos apenas de um loop for e atribuímos ao valor de cada memória dinâmica uma linha. Agora podemos usar o ponteiro da mesma maneira que usamos uma matriz 2D. No exemplo acima, atribuímos números aleatórios à nossa matriz 2D (ponteiro). Tudo sobre DMA da matriz 2D.


-3

Estou usando isso ao criar matriz dinâmica. Se você tem uma classe ou uma estrutura. E isso funciona. Exemplo:

struct Sprite {
    int x;
};

int main () {
   int num = 50;
   Sprite **spritearray;//a pointer to a pointer to an object from the Sprite class
   spritearray = new Sprite *[num];
   for (int n = 0; n < num; n++) {
       spritearray[n] = new Sprite;
       spritearray->x = n * 3;
  }

   //delete from random position
    for (int n = 0; n < num; n++) {
        if (spritearray[n]->x < 0) {
      delete spritearray[n];
      spritearray[n] = NULL;
        }
    }

   //delete the array
    for (int n = 0; n < num; n++) {
      if (spritearray[n] != NULL){
         delete spritearray[n];
         spritearray[n] = NULL;
      }
    }
    delete []spritearray;
    spritearray = NULL;

   return 0;
  } 
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.