asignacion de array dinamico usando una variable no inicializada..

Iniciado por digimikeh, 6 Julio 2019, 19:35 PM

0 Miembros y 1 Visitante están viendo este tema.

digimikeh

Hola amigos. que tal.

Tengo el siguiente caso:
Código (cpp) [Seleccionar]


#include <iostream>
#include <initializer_list>

struct x {
private:
    int * iArray = nullptr;
    int tam;       //Sin inicializar

public:
    x(std::initializer_list<int> lst) : iArray{new int[lst.size()}, tam{lst.size()}{
         std::copy(lst.begin(), lst.end(), iArray);
    }

    //Constructor de copia
    x(const x & otherX);
 
};

//Definición de constructor de copia (notar aquí que al momento de hacer
//la primera inicialización tam no ha recibido valor.
x::x(const x & otherX) : iArray{new int[tam]}, tam{otherX.tam}{
    for (int y = 0; y < tam; ++y){
         iArray[y] = otherX.iArray[y];
    }
}

int main (){
    x x0 {2, 4, 6};
    x x1 = x0;   //copia correcta...

    //prueba directa de asignacion de una variable no definida a un array dinamico:

    int x;
    int * arr = new int[x];     //error, x no está definida.
}


Como se puede observar, en la linea 22 se está inicializando el array dinamico con un tamaño de tam antes que inicializar al mismo tam, o sea, se esta inicializando con un valor desconocido...  sin embargo, esta funcionando bien, ahora bien, en la linea 35, se esta intentando crear un array dinamico con un tamaño x, pero no sirvio.... acaso la inicialización funciona distinto a la asignacion?

Gracias, saludos.

Dungeons & dragons;
dragons.Attack();

MAFUS

En C++ las cosas no necesariamente se ejecutan de izquierda a derecha sino que es el compilador quien decide el mejor orden para hacer las cosas.

Busca sobre los sequence points.

Por ejemplo: https://en.wikipedia.org/wiki/Sequence_point

digimikeh

#2
ok, el compilador determina esto siempre y cuando se este evaluando la línea actual o no?

Edit: Ya me quedo claro gracias.!
Dungeons & dragons;
dragons.Attack();

RayR

 :huh:

No hay ninguna diferencia entre lo que haces en la línea 22 y la línea 35, desde el punto de vista de la no inicialización. De entrada, usar el valor de una variable no inicializada es totalmente incorrecto (por más que no sea un error de compilación), aunque supongo que lo sabes y este ejercicio es sólo un test para ver qué pasa.

Ahora, el hecho de que aparentemente funcione bien en una línea pero no en la otra no tiene nada de extraño. Una variable no inicializada (a menos que sea variable global o static, que siempre son inicializadas a 0) tendrá "basura", es decir, cualquier valor que hubiera en la posición de memoria ocupada por esa variable. Si la línea 22 te funciona bien, simplemente significa que dio la casualidad de que el valor aleatorio que contenía tam era positivo y no muy grande. Si en la línea 35 tienes error, es porque la variable x (y por cierto, nunca deberías usar un mismo identificador para un tipo y para una variable) contenía un valor negativo o demasiado grande: new falla y arroja una excepción si se le pide reservar un número negativo de bytes o una cantidad mayor a la máxima que es posible reservar. Esto último depende de la implementación, la arquitectura para la que estés compilando, la cantidad de memoria disponible en el sistema, etc.

Dado que no se puede predecir el valor de variables no inicializadas (es perfectamente posible que en otra PC, o con otro compilador, o con otra versión del mismo compilador, la línea 22 falle pero la 35 no, o que ambas fallen, o ninguna) siempre se deben inicializar antes de usarse.

Loretz

Un momento...

El orden en que se inicializarán los miembros de la struct x es en el orden en que se encuentren declarados en la struct; en este caso, primero iArray y después tam, y no importa en qué orden se escriban en la 'initializer list' del constructor.

Hay un problema aquí donde dices:
x x1 = x0;   //copia correcta...

Al invocar el copy constructor se va a ejecutar la inicialización de iArray antes que tam, de modo que la expresión
iArray{new int[tam]}
al pretender acceder a tam sin inicializar se incurrirá en "Undefined behavior" (o sea, cualquier cosa --normalmente mala-- puede suceder).


MAFUS

He hecho unas pruebas y Rayr está en lo cierto. Se genera un array de tamaño aleatorio en el heap.

A la hora de copiar tam pero generar el array dentro del cuerpo del constructor.

digimikeh

Entonces en tal caso no sería mejor inicializar tam y luego iArray con tam? o colocar primero en el campo privado del struct a tam y en la siguiente línea al array...
Dungeons & dragons;
dragons.Attack();

MAFUS

Y si usas algo así:
x::x(const x & otherX) : iArray{new int[otherX.tam]}, tam{otherX.tam}{
     for (int y = 0; y < tam; ++y){
          iArray[y] = otherX.iArray[y];
     }
}


Ya que otherX.tam te sirve para copiar el tam también lo puedes usar para generar el array.

O

x::x(const x & otherX) : tam{otherX.tam}{
     iArray = new int[tam];
     for (int y = 0; y < tam; ++y){
          iArray[y] = otherX.iArray[y];
     }
}


Aunque, bueno, hablo desde el desconocimiento.

digimikeh

Entiendo!.. gracias por contestar!

He estado observando en el libro que estoy estudiando otras cosas que no me parecen (yo a pesar de ser un poco novato) muy comprensibles...

Luego me puse a indagar sobre las Erratas del libro y me tope con esto:
http://www.stroustrup.com/4th_printing3.html

Como verán en la pagina 74 (que es justo el punto que quería discutir en este post), en realidad el problema es por una cuestion de impresion, es decir, lo que esta escrito en la pagina 74 fue sin intencion (por algun minuto se me paso que podia ser asi y que se habia cometido un error)..


Dice:
pg 74, top: s/new double[sz]/new double[a.sz]/

...por lo que new double[a.sz] tiene mucho mas sentido que lo anterior...

Saludos!
Dungeons & dragons;
dragons.Attack();