¿Por qué la dimensión de una matriz es parte de su tipo?

14

Mientras leía el libro de C ++ Primer, me encontré con esta declaración: "El número de elementos en una matriz es parte del tipo de matriz". Así que quería averiguarlo usando el siguiente 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;
}

Y curiosamente, el resultado de typeid en las dos matrices mostró que de alguna manera son diferentes.

  • ¿Qué está pasando detrás de escena?
  • ¿Por qué es necesario que las matrices tengan un tipo que incluya su tamaño? ¿Es solo porque su tamaño no debería cambiar?
  • ¿Cómo afectará esto a la comparación de matrices?

Solo quiero poder entender profundamente el concepto.

pulpo
fuente
3
No es estrictamente necesario incluir información de tamaño en el tipo, pero es útil
por
Cualquier tutorial sobre matrices explicará (1). No estoy seguro de qué quieres decir con (3), ya que no hay una forma integrada de comparar matrices.
HolyBlackCat

Respuestas:

20

¿Qué está pasando detrás de escena?

Un no asignado dinámicamente es, por definición, un contenedor de tamaño fijo de elementos homogéneos. Una matriz de Nelementos de tipo Tse presenta en la memoria como una secuencia contigua de Nobjetos de tipo T.


¿Por qué es necesario que las matrices tengan un tipo que incluya su tamaño?

No creo que sea "necesario" que un tipo de matriz incluya su tamaño; de hecho, puede usar un puntero para referirse a una secuencia contigua de T objetos. Tal puntero perdería información sobre el tamaño de la matriz.

Sin embargo, es algo útil tener. Mejora la seguridad de los tipos y codifica información útil en tiempo de compilación que se puede utilizar de varias maneras. Como ejemplo, puede usar referencias a matrices para sobrecargar en matrices de diferentes tamaños

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

o para calcular el tamaño de una matriz como una expresión constante

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

¿Cómo afectará esto a la comparación de matrices?

No lo hace, de verdad.

No puede comparar matrices de estilo C de la misma manera que compararía dos números (por ejemplo, intobjetos). Tendría que escribir algún tipo de comparación lexicográfica y decidir qué significa para colecciones de diferentes tamaños. std::vector<T>proporciona eso , y la misma lógica se puede aplicar a las matrices.


Bonificación: proporciona C ++ 11 y superior std::array, que es un contenedor alrededor de una matriz de estilo C con una interfaz similar a un contenedor. Debería preferirse a las matrices de estilo C, ya que es más coherente con otros contenedores (p std::vector<T>. Ej. ) Y también admite comparaciones lexicográficas listas para usar.

Vittorio Romeo
fuente
2
"Tendría que escribir algún tipo de comparación lexicográfica y decidir qué significa para colecciones de diferentes tamaños". Podrías usar std::equal(a través destd::begin y std::endque están definidos para matrices). En ese caso, las matrices de diferentes tamaños no son iguales.
Stu
3
Vale la pena señalar que los bucles basados ​​en rangos cuyo rango es una matriz necesitan leer el tamaño de la matriz en el momento de la compilación: es un poco más sutil ya que (¡afortunadamente!) Uno nunca escribe el tipo de matriz en este ejemplo, pero parece que surge mucho más que sobrecargar en función del tamaño.
Milo Brandt
8

La cantidad de espacio que se asigna a un objeto cuando lo crea depende completamente de su tipo. La asignación de la que estoy hablando no son asignaciones de newo malloc, sino el espacio asignado para que pueda ejecutar su constructor e inicializar su objeto.

Si tiene una estructura definida como (por ejemplo)

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

Luego, cuando construyes el objeto:

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

Puedes pensar en el proceso de construcción del objeto como un proceso:

  • Asigne 2 bytes de espacio (en la pila, pero donde no importa para este ejemplo)
  • Ejecute el constructor del objeto (en este caso, copie 'a'y 'b'al objeto)

Es importante tener en cuenta que los 2 bytes de espacio necesarios están completamente determinados por el tipo de objeto, los argumentos de la función no importan. Entonces, para una matriz, el proceso es el mismo, excepto que ahora la cantidad de espacio necesaria depende de la cantidad de elementos en la matriz.

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

Entonces, los tipos de ay bdeben reflejar el hecho de que anecesita suficiente espacio para 1 personaje y bnecesita suficiente espacio para 5 caracteres. Eso significa que el tamaño de estas matrices no puede cambiar repentinamente, una vez que se crea una matriz de 5 elementos, siempre es una matriz de 5 elementos. Para tener objetos tipo "matriz" donde el tamaño puede variar, necesita una asignación dinámica de memoria, que su libro debería cubrir en algún momento.

SirGuy
fuente
0

Es por una razón interna para la biblioteca de tiempo de ejecución. Si considera las siguientes declaraciones, por ejemplo:

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

Entonces queda claro cuál es el problema: por ejemplo, el direccionamiento de unsigned int *tiene que preocuparse por el sizeof operatordireccionamiento de unsigned int.

Hay una explicación más detallada para el resto de lo que ve aquí, pero es en gran parte una recapitulación de lo que se cubrió en el lenguaje de programación C, 2da edición de Kernighan y Ritchie con respecto al programa que imprime el texto en lenguaje claro del tipo declarado cuerda.

CR Ward
fuente