Por que a dimensão de uma matriz faz parte de seu tipo?

14

Ao ler o livro C ++ Primer, deparei-me com esta declaração: "O número de elementos em uma matriz faz parte do tipo da matriz". Então, eu queria descobrir usando o seguinte código:

#include<iostream>

int main()
{
    char Array1[]{'H', 'e', 'l', 'p'};
    char Array2[]{'P', 'l', 'e', 'a', 's', 'e'};

    std::cout<<typeid(Array1).name()<<std::endl;        //prints  A4_c
    std::cout<<typeid(Array2).name()<<std::endl;        //prints  A6_c

    return 0;
}

E, curiosamente, o resultado do typeid nas duas matrizes mostrou que elas são de alguma forma diferentes.

  • O que está acontecendo nos bastidores?
  • Por que é necessário que as matrizes tenham um tipo que inclua seu tamanho? É apenas porque seu tamanho não deve mudar?
  • Como isso afetará a comparação de matrizes?

Só quero ser capaz de entender profundamente o conceito.

polvo
fonte
3
Não é estritamente necessário para incluir informações de tamanho no tipo, mas é útil
byxor
Qualquer tutorial sobre matrizes explicará (1). Não sei ao certo o que você quer dizer com (3), pois não há uma maneira integrada de comparar matrizes.
precisa saber é o seguinte

Respostas:

20

O que está acontecendo nos bastidores?

Um alocado não dinamicamente é, por definição, um contêiner de tamanho fixo de elementos homogêneos. Uma matriz de Nelementos do tipo Té apresentada na memória como uma sequência contígua de Nobjetos do tipo T.


Por que é necessário que as matrizes tenham um tipo que inclua seu tamanho?

Não acredito que seja "necessário" que o tipo de uma matriz inclua seu tamanho - na verdade, você pode usar um ponteiro para se referir a uma sequência contígua de Tobjetos. Esse ponteiro perderia informações de tamanho sobre a matriz.

É, no entanto, uma coisa útil de se ter. Ele melhora a segurança do tipo e codifica informações úteis em tempo de compilação que podem ser usadas de várias maneiras. Como exemplo, você pode usar referências a matrizes para sobrecarregar matrizes de tamanhos diferentes

void foo(int(&array)[4]) { /* ... */ }
void foo(int(&array)[8]) { /* ... */ }

ou para descobrir o tamanho de uma matriz como uma expressão constante

template <typename T, std::size_t N>
constexpr auto sizeOf(const T(&array)[N]) { return N; }

Como isso afetará a comparação de matrizes?

Realmente não.

Você não pode comparar matrizes no estilo C da mesma maneira que compararia dois números (por exemplo, intobjetos). Você precisaria escrever algum tipo de comparação lexicográfica e decidir o que isso significa para coleções de tamanhos diferentes. std::vector<T>fornece isso e a mesma lógica pode ser aplicada às matrizes.


Bônus: C ++ 11 e superior fornece std::array, que é um invólucro em torno de uma matriz de estilo C com uma interface semelhante a contêiner. Deve ser preferido às matrizes no estilo C, pois é mais consistente com outros contêineres (por exemplo std::vector<T>) e também suporta comparações lexicográficas prontas para uso.

Vittorio Romeo
fonte
2
"Você teria que escrever algum tipo de comparação lexicográfica e decidir o que isso significa para coleções de tamanhos diferentes". Você pode apenas usar std::equal(via std::begine std::endque são definidos para matrizes). Nesse caso, matrizes de tamanhos diferentes não são iguais.
Stu
3
Pode ser interessante notar que os loops baseados em intervalo cujo intervalo é uma matriz precisam ler o tamanho da matriz em tempo de compilação - é um pouco mais sutil, pois (felizmente!) Nunca se escreve o tipo da matriz neste exemplo, mas parece muito mais do que sobrecarregar com base no tamanho.
Milo Brandt
8

A quantidade de espaço alocado para um objeto quando você o cria depende inteiramente de seu tipo. A alocação de que estou falando não é alocação de newor malloc, mas o espaço alocado para que você possa executar seu construtor e inicializar seu objeto.

Se você tiver uma estrutura definida como (por exemplo)

struct A { char a, b; }; //sizeof(A) == 2, ie an A needs 2 bytes of space

Então, quando você constrói o objeto:

A a{'a', 'b'};

Você pode pensar no processo de construção do objeto como um processo:

  • Aloque 2 bytes de espaço (na pilha, mas onde não importa neste exemplo)
  • Executar o construtor do objeto (neste caso, copiar 'a'e 'b'para o objeto)

É importante observar que os 2 bytes de espaço necessários são inteiramente determinados pelo tipo do objeto, os argumentos para a função não importam. Portanto, para uma matriz, o processo é o mesmo, exceto que agora a quantidade de espaço necessária depende do número de elementos na matriz.

char a[] = {'a'}; //need space for 1 element
char b[] = {'a', 'b', 'c', 'd', 'e'}; //need space for 5 elements

Portanto, os tipos ae bdevem refletir o fato de que aprecisa de espaço suficiente para 1 caractere e bprecisa de espaço suficiente para 5 caracteres. Isso significa que o tamanho dessas matrizes não pode mudar repentinamente, uma vez que uma matriz de 5 elementos é criada, ela é sempre uma matriz de 5 elementos. Para ter objetos do tipo "matriz" onde o tamanho pode variar, você precisa de alocação dinâmica de memória, que seu livro deve cobrir em algum momento.

SirGuy
fonte
0

É por uma razão interna da biblioteca de tempo de execução. Se você considerar as seguintes instruções, por exemplo:

unsigned int i;
unsigned int *iPtr;
unsigned int *iPtrArr[2];
unsigned int **iPtrHandle;

Então fica claro qual é o problema: Por exemplo, o endereçamento de unsigned int *deve se preocupar com o sizeof operatorou endereçamento de unsigned int.

Há uma explicação mais detalhada para o restante do que você vê aqui, mas é em grande parte uma recapitulação do que foi abordado na linguagem de programação C, 2ª edição de Kernighan e Ritchie, sobre o programa que imprime o texto em linguagem simples do tipo declarado corda.

CR Ward
fonte