una - Resultado inesperado cuando C++ almacena elemento en std:: vector del valor de retorno de la funciĆ³n
valores de retorno en lenguaje c (2)
Cuando la función implica una reasignación, descubrí que algunos compiladores pueden guardar la dirección antes de la llamada a la función. Lleva el valor de retorno almacenado en la dirección no válida.
Hay un ejemplo para explicar el comportamiento en la descripción anterior.
#include <stdio.h>
#include <vector>
using namespace std;
vector<int> A;
int func() {
A.push_back(3);
A.push_back(4);
return 5;
}
int main() {
A.reserve(2);
A.push_back(0);
A.push_back(1);
A[1] = func();
printf("%d/n", A[1]);
return 0;
}
Hay algunos compiladores comunes de C ++, y el resultado de la prueba es el siguiente.
- GCC (Colección compilador GNU): Error en tiempo de ejecución o salida
1
- Clang: salida
5
- VC ++: salida
5
¿Es un comportamiento indefinido?
El comportamiento no está definido en todas las versiones de C ++ anteriores a C ++ 17. La simple razón es que los dos lados del operador de asignación se pueden evaluar en cualquier orden:
- Suponiendo que
A[1]
se evalúa primero, obtiene unint&
refiriéndose al segundo elemento deA
en ese punto. - Luego, se evalúa el
func()
, que puede reasignar el almacenamiento para el vector, dejando la referencia previamente recuperadaint&
una referencia colgante. - Finalmente, la asignación se realiza, escribiendo en el almacenamiento no asignado. Dado que la memoria caché asignadores estándar, el sistema operativo a menudo no detectará este error.
Solo en C ++ 17, se hizo la regla especial 20 para la asignación:
En cada expresión de asignación simple E1 = E2 y cada expresión de asignación compuesta E1 @ = E2, cada cómputo de valor y efecto secundario de E2 se secuencia antes de cada cálculo de valor y efecto secundario de E1
Con C ++ 17, A[1]
debe evaluarse después de la llamada a func()
, que luego proporciona un comportamiento definido y confiable.
Si revisa la documentation , en "Invalidación del iterador", verá que push_back()
puede invalidar cada iterador si cambia de capacidad, ya que tendría que reasignar la memoria. Recuerde que, para un std::vector
, un puntero también es un iterador válido. Debido a que push_back()
puede o no reasignar, y usted no tiene forma de saber si lo hará, el comportamiento no está definido.