vectores tipos suma programacion nombres multiplicar matrices elementos ejemplos cadenas arreglos arreglo almacenar c++ arrays pointers initialization

c++ - tipos - vectores en c



DeclaraciĆ³n de array de int (8)

¿Hay alguna diferencia entre estas dos declaraciones?

int x[10];

vs.

int* x = new int[10];

Supongo que la declaración anterior (como la última) es una declaración de puntero y ambas variables podrían tratarse de la misma manera. ¿Significa que son intrínsecamente iguales?


De acuerdo con el estándar, en realidad deberíamos distinguir entre tres tipos diferentes de declaraciones de matriz:

int x[10]; void method() { int y[10]; int *z = new int[10]; delete z; }

La primera declaración, int x[10] , utiliza la duración del almacenamiento estático , definida por cppreference como: "El almacenamiento para el objeto se asigna cuando el programa comienza y se desasigna cuando finaliza el programa. Solo existe una instancia del objeto. Todos los objetos declarados en el ámbito del espacio de nombres (incluido el espacio de nombres global) tienen esta duración de almacenamiento, más los declarados con estática o externa. "

El segundo, int y[10] , utiliza la duración de almacenamiento automático , definida por cppreference como: "El objeto se asigna al principio del bloque de código delimitador y se desasigna al final. Todos los objetos locales tienen esta duración de almacenamiento, excepto los declarados static, extern o thread_local ".

El tercero, int *z = new int[10] , generalmente se denomina asignación de memoria dinámica , y en realidad es una secuencia de dos pasos:

  • En primer lugar, se llama al operador new, que asigna la memoria de forma dinámica utilizando los métodos de asignación predeterminados de la biblioteca estándar o una implementación definida por el usuario (ya que los nuevos pueden anularse durante el tiempo de ejecución). La memoria asignada será suficiente para ajustarse a los N elementos asignados, más cualquier memoria adicional requerida para mantener los metadatos para la asignación dada (de modo que pueda liberarse posteriormente con éxito).
  • En segundo lugar, si el primer paso es exitoso, procedemos a inicializar o construir cada objeto en la matriz.

Como ya se mencionó en otros comentarios, estos tipos de declaración tienen sus sutiles diferencias, pero las más comunes son:

  1. En la mayoría de los sistemas operativos modernos:

    • el almacenamiento automático generalmente se asigna en la pila, que es (en términos generales) un espacio de memoria preasignado específico del subproceso que utiliza un mecanismo LIFO
    • el almacenamiento estático usa el espacio de memoria preasignado reservado dentro del ejecutable ( más específicamente segmentos .BSS y .DATA, dependiendo de si la variable tiene cero inicialización o no )
    • la memoria dinámica se asigna utilizando la memoria de pila, y está sujeta a la merced del sistema de gestión de RAM del sistema y a otros mecanismos, como la paginación.
  2. La memoria asignada dinámicamente debe delete explícitamente por el programador, mientras que las variables de almacenamiento estáticas y automáticas están a cargo del "entorno"

  3. Las variables de almacenamiento estáticas y automáticas están limitadas a un alcance específico, mientras que la memoria asignada dinámicamente no tiene límites, lo que significa que una variable declarada en un módulo puede pasarse a cualquier otro módulo que opere en el mismo espacio de direcciones.

  4. Al asignar una matriz usando new[] , el tamaño puede ser 0

  5. (Como ya lo señaló @R Sahu) Los tipos de &x y &z son diferentes:

    • &x es int (*)[10]
    • &z es int **

El primero es una matriz de int de tamaño 10 . Decir que está creado en la pila es incorrecto. Porque el Estándar no garantiza eso. Su implementación-definida. Su duración de almacenamiento puede ser estática o automática dependiendo de si x es una variable global o una variable local .

En el segundo, crea un puntero de tipo int* . No necesariamente creado en montón, el estándar no dice eso. La memoria asignada abarca más de 10 * sizeof(int) bytes. Para esto debes desasignar la memoria tú mismo, escribiendo:

delete [] x;

En este caso, la memoria del puntero x se asigna dinámicamente y se desasigna dinámicamente, por lo que se dice que dichos objetos tienen una duración de almacenamiento dinámico .


Las declaraciones son completamente diferentes.

El primer caso,

int x[10];

declara x como una matriz de 10 enteros, mientras que el segundo caso,

int* x = new int[10];

declara x como un puntero a int - una variable con valor igual a una dirección de un int , e inicializa ese puntero al resultado de una nueva expresión ( new int [10] ) que asigna dinámicamente una matriz de diez enteros.

A pesar de las diferencias, los dos se pueden usar de manera similar;

  • la sintaxis de matriz (por ejemplo, x[i] , donde i es un valor integral entre 0 y 9 inclusive) se puede usar para establecer o recuperar valores de las matrices respectivas en la sintaxis anterior;
  • la aritmética del puntero se puede utilizar para obtener una dirección de un elemento de la matriz (por ejemplo, x + i es equivalente a &x[i] para i entre 0 y 10 inclusive. [sí, es posible obtener la dirección "uno después del final"] ];
  • La desreferenciación del puntero y el acceso a la matriz son equivalentes. es decir, *(x+i) x[i] son equivalentes, para i entre 0 y 9 [desreferenciar un puntero "pasado el final" da un comportamiento indefinido].

Sin embargo, también hay algunas diferencias clave, por ejemplo;

Resultados del operador de sizeof sizeof(x) da valores diferentes en los dos casos.

  1. En el primer caso, sizeof(x) == sizeof(int)*10 . sizeof(int) proporciona una función definida por la implementación, pero sizeof(x)/sizeof(*x) siempre dará la cantidad de elementos en la matriz (es decir, un std::size_t con un valor de 10 ).
  2. En el segundo, sizeof(x) == sizeof(int *) - que es un valor definido por la implementación. El valor de sizeof(x)/sizeof(*x) es prácticamente improbable que arroje un valor de 10 . Lo que significa que esta técnica no puede usarse para obtener la cantidad de elementos.

Lifetime .

  1. En el primer caso, la vida útil de x depende del alcance en el que se produce la declaración. Si la declaración ocurre en el alcance del archivo (es decir, en una unidad de compilación, fuera de cualquier bloque de funciones), entonces x tiene una duración de almacenamiento estático (por lo que existe mientras el programa se esté ejecutando). Si la declaración ocurre en un bloque, x - y todos sus elementos - deja de existir cuando termina el bloque. Por ejemplo

    { int x[10]; } // x and all its elements cease to exist here

  2. En el segundo caso, solo el puntero x tiene una vida que depende del alcance. La memoria asignada dinámicamente (el resultado de la new x[10] ) nunca se desasigna. Esto significa que la vida útil de x duración de la matriz (asignada dinámicamente) a la que hace referencia están desacopladas, lo que nos lleva a una tercera diferencia .....

Resultado de la asignación Una matriz no puede ser reasignada, un puntero puede (a menos que esté apropiadamente const calificado).

Considere un contexto de

// x as previously defined in one or the other form int y[10]; int z; x = y; x = &z;

En el primer caso, ambas asignaciones darán como resultado un diagnóstico del compilador: las asignaciones no son válidas. En el segundo caso, las asignaciones son válidas y hacen que x señale la dirección de (el primer elemento de) y y a la dirección de z respectivamente. A menos que el valor de x se almacene en otro puntero antes de la reasignación, la memoria asignada por la nueva expresión ( new int [10] ) se filtrará: el programa ya no podrá acceder a ella, pero tampoco se liberará.


Lo único similar entre

int x[10];

y

int* x = new int[10];

es que cualquiera de los dos puede usarse en algunos contextos donde se espera una int* :

int* b = x; // Either form of x will work void foo(int* p) {} foo(x); // Either form will work

Sin embargo, no se pueden usar en todos los contextos donde se espera una int* . Específicamente,

delete [] x; // UB for the first case, necessary for the second case.

Algunas de las diferencias centrales se han explicado en las otras respuestas. Las otras diferencias principales son:

Diferencia 1

sizeof(x) == sizeof(int)*10 // First case sizeof(x) == sizeof(int*) // Second case.

Diferencia 2

Tipo de &x es int (*)[10] en el primer caso

Tipo de &x es int** en el segundo caso

Diferencia 3

Función dada

void foo(int (&arr)[10]) { }

puede llamarlo usando la primera x no la segunda x .

foo(x); // OK for first case, not OK for second case.


Primer caso: x se crea en el segmento de pila / datos en función de si es una variable local no estática o una variable estática / global. Y la dirección de x no es modificable.

Segundo caso: ''x'' es un puntero que apunta a una matriz creada generalmente en el montón (tienda gratuita). Puedes cambiar x apuntando a otra cosa también. Además, debe ocuparse de desasignarlo utilizando delete[] x;


Si desea dimensionar una matriz de forma dinámica, por ejemplo:

void doSomething(int size) { int x[size]; // does not compile int *z = new int[size]; //Do something interesting ... doMore(z, size); }

entonces x no se compilará en C ++, por lo que debe usar z. La buena noticia es que ahora puede usar z, en la mayoría de los casos, como si estuviera asignado estáticamente, por ejemplo:

void doMore(int anArray[], int size) { // ... }

toma z como argumento, tratando el puntero como una matriz.


Son los mismos en cuanto a ambos puntos x a la primera dirección de memoria en la matriz de 10 enteros, aunque muy diferentes en ese

int x[10]

declara la memoria en la memoria de acceso aleatorio estática, y la palabra clave ''nuevo'' los crea dinámicamente con el montón, y es casi lo mismo que usar malloc en c para crear dinámicamente una matriz.

No solo eso, sino que (creo que no se ha probado la teoría) existe la posibilidad de que:

int* x = new int[10];

podría fallar, y dependiendo del compilador, podría devolver un error o un puntero nulo. Si el compilador de c ++ se adhiere a los estándares ANSI / ISO, entonces es compatible con una forma nueva de "no-throw" que devuelve un valor nulo si falla la asignación, en lugar de lanzar una excepción.

La otra diferencia es que el operador ''nuevo'' puede estar sobrecargado.

De lo que no estoy seguro, sin embargo, es si alguno (en c ++) crea una matriz terminada nula. Sé que en c, en el compilador que uso al menos, debes asegurarte de anexar siempre un / 0 a cualquier cadena o matriz si esperas poder iterar sobre ellos sin sobreextender los límites.

Solo mi valor de $ .02. :)


#include<iostream> int y[10]; void doSomething() { int x[10]; int *z = new int[10]; //Do something interesting delete []z; } int main() { doSomething(); }

1.

int x[10];

- Crea una matriz de enteros de tamaño 10 en la pila.
- No tiene que borrar explícitamente esta memoria porque desaparece a medida que la pila se desenrolla.
- Su alcance se limita a la función doSomething()

int y[10];

- Crea una matriz de enteros de tamaño 10 en el segmento BSS / Datos.
- No tiene que eliminar explícitamente esta memoria.
- Dado que se declara global , es accesible globalmente.

int *z = new int[10];

- Asigna una matriz dinámica de enteros de tamaño 10 en el montón y devuelve la dirección de esta memoria a z .
- Debe eliminar explícitamente esta memoria dinámica después de usarla. utilizando:

delete[] z;