Casting entre arrays multi e unidimensionais
Pergunta
Isto surgiu de esta resposta a uma pergunta anterior minha.É garantido que o compilador trate array[4][4]
o mesmo que array[16]
?
Por exemplo, qualquer uma das chamadas abaixo para api_func()
esteja a salvo?
void api_func(const double matrix[4][4]);
// ...
{
typedef double Matrix[4][4];
double* array1 = new double[16];
double array2[16];
// ...
api_func(reinterpret_cast<Matrix&>(array1));
api_func(reinterpret_cast<Matrix&>(array2));
}
Solução
Do padrão C++, referindo-se ao sizeof
operador:
Quando aplicado a uma matriz, o resultado é o número total de bytes na matriz.Isto implica que o tamanho de uma matriz de
n
elementos én
vezes o tamanho de um elemento.
A partir disso, eu diria que double[4][4]
e double[16]
teria que ter a mesma representação subjacente.
Ou seja, dado
sizeof(double[4]) = 4*sizeof(double)
e
sizeof(double[4][4]) = 4*sizeof(double[4])
então nós temos
sizeof(double[4][4]) = 4*4*sizeof(double) = 16*sizeof(double) = sizeof(double[16])
Acho que um compilador compatível com os padrões teria que implementá-los da mesma forma, e acho que isso não é algo que um compilador quebraria acidentalmente.A forma padrão de implementar matrizes multidimensionais funciona conforme o esperado.Quebrar o padrão exigiria trabalho extra, provavelmente sem nenhum benefício.
O padrão C++ também afirma que um array consiste em elementos alocados contíguamente, o que elimina a possibilidade de fazer algo estranho usando ponteiros e preenchimento.
Outras dicas
Eu não acho que haja um problema com o preenchimento introduzido por ter um multidimensional variedade.
Cada elemento de uma matriz deve satisfazer os requisitos de preenchimento impostos pela arquitetura.Um array [N][M] sempre terá a mesma representação na memória que um de [M*N].
Cada elemento do array deve ser disposto sequencialmente na memória pelo compilador.As duas declarações, embora de tipos diferentes, têm a mesma estrutura de memória subjacente.
@Konrad Rodolfo:
Eu mesmo confundo esses dois (linha principal/coluna principal), mas sei disso:Está bem definido.
int x[3][5], por exemplo, é um array de tamanho 3, cujos elementos são arrays int de tamanho 5.(§6.5.2.1) Adicionando todas as regras do padrão sobre arrays, endereçamento, etc.você percebe que o segundo subscrito faz referência a números inteiros consecutivos, enquanto o primeiro subscrito fará referência a objetos consecutivos de 5 int.(Portanto, 3 é o número maior;você tem 5 ints entre x[1][0] e x[2][0].)
Eu ficaria preocupado com a adição de preenchimento para coisas como Matrix[5][5] para alinhar cada palavra de linha, mas isso poderia ser simplesmente minha própria superstição.
Uma questão maior é:você realmente precisa realizar tal elenco?
Embora você poder ser capaz de se safar, ainda seria mais legível e sustentável evitá-lo completamente.Por exemplo, você poderia usar consistentemente double[m*n] como o tipo real e, em seguida, trabalhar com uma classe que agrupa esse tipo e talvez sobrecarregar o operador [] para facilitar o uso.Nesse caso, você também pode precisar de uma classe intermediária para encapsular uma única linha - para que um código como my_matrix[3][5] ainda funcione conforme o esperado.