vectores una resueltos programacion matriz matrices llenar ingresar ejercicios ejemplos datos como arreglos c++ c++11 language-lawyer allocator

c++ - una - matrices en programacion



Alineación de matriz con 0 elementos. (1)

basic.stc.dynamic.allocation / 2 de N3337 (básicamente C ++ 11):

La función de asignación intenta asignar la cantidad de almacenamiento solicitada. Si tiene éxito, devolverá la dirección del inicio de un bloque de almacenamiento cuya longitud en bytes será al menos tan grande como el tamaño solicitado. No hay restricciones en el contenido del almacenamiento asignado en el retorno de la función de asignación. El orden, la contigüidad y el valor inicial del almacenamiento asignado por llamadas sucesivas a una función de asignación no se especifican. El puntero devuelto se alineará adecuadamente para que pueda convertirse en un puntero de cualquier tipo de objeto completo con un requisito de alineación fundamental (3.11) y luego se use para acceder al objeto o la matriz en el almacenamiento asignado (hasta que el almacenamiento sea desasignado explícitamente por una llamada a una función de desasignación correspondiente). Incluso si el tamaño del espacio solicitado es cero, la solicitud puede fallar. Si la solicitud es exitosa, el valor devuelto será un valor de puntero no nulo (4.10) p0 diferente de cualquier valor p1 devuelto previamente, a menos que ese valor p1 se haya pasado posteriormente a un operador de borrado. El efecto de desreferenciar un puntero devuelto como una solicitud de tamaño cero no está definido.

Alineación fundamental (basic.align / 2):

Una alineación fundamental se representa mediante una alineación menor o igual a la mayor alineación admitida por la implementación en todos los contextos, que es igual a la alineación de (std :: max_align_t)

Alineación extendida (basic.align / 3):

Una alineación extendida se representa mediante una alineación mayor que alignof (std :: max_align_t).

Se define por implementación si se admiten las alineaciones extendidas y los contextos en los que se admiten

Por lo tanto, el puntero devuelto por el operator new debe tener una alineación fundamental. Incluso si el tamaño cero especificado. Y es la implementación definida, ya sea 8 es fundamental o alineación extendida. Si es fundamental, entonces Foo está bien. Si se extiende, entonces se define por la implementación que Foo es compatible con el operator new .

Tenga en cuenta que para C ++ 17, la situación ha mejorado:

basic.stc.dynamic.allocation / 2 de C ++ 17 :

La función de asignación intenta asignar la cantidad de almacenamiento solicitada. Si tiene éxito, devolverá la dirección del inicio de un bloque de almacenamiento cuya longitud en bytes será al menos tan grande como el tamaño solicitado. No hay restricciones en el contenido del almacenamiento asignado en el retorno de la función de asignación. El orden, la contigüidad y el valor inicial de almacenamiento asignado por llamadas sucesivas a una función de asignación no están especificados. El puntero devuelto se alineará adecuadamente para que se pueda convertir en un puntero a cualquier tipo de objeto completo adecuado ([new.delete.single]) y luego se use para acceder al objeto o matriz en el almacenamiento asignado (hasta que el almacenamiento sea explícitamente desasignado por una llamada a una función de desasignación correspondiente). Incluso si el tamaño del espacio solicitado es cero, la solicitud puede fallar. Si la solicitud se realiza correctamente, el valor devuelto será un valor de puntero no nulo ([conv.ptr]) p0 diferente de cualquier valor p1 devuelto previamente, a menos que ese valor p1 se haya pasado posteriormente a un operador eliminado. Además, para las funciones de asignación de bibliotecas en [new.delete.single] y [new.delete.array], p0 representará la dirección de un bloque de almacenamiento separado del almacenamiento para cualquier otro objeto accesible para el llamante. El efecto de dirigir indirectamente a través de un puntero devuelto como una solicitud de tamaño cero no está definido.

He puesto énfasis en la parte relevante. Esa oración significa que el puntero devuelto del void *operator new(...) debe tener una alineación adecuada. No menciona el tamaño cero como un caso especial (pero, por supuesto, es UB para eliminar la referencia al puntero devuelto).

Así que la respuesta es la habitual, no hay un manejo especial de cero:

  1. void *operator new(std::size_t) debe devolver un puntero alineado de alignof(std​::​max_align_t)
  2. void *operator new(std::size_t, std::align_val_t align) debe devolver un puntero alineado de align )

Tenga en cuenta que la implementación está definida, qué versión se llamará para Foo . Depende de si 8 es igual o menor que alignof(std​::​max_align_t) . Si es menos, entonces se llama a la 1ra versión (porque no tiene alineación extendida ). De lo contrario se llama el segundo.

ACTUALIZACIÓN: Como comenta Massimiliano Janes, estos párrafos se aplican al resultado del operator new , no al resultado de la nueva expresión. Una implementación podría agregar un desplazamiento arbitrario al resultado del operator new[] . Y el estándar no dice nada sobre el valor de este desplazamiento x :

nueva T [5] da como resultado una de las siguientes llamadas:

operador nuevo [] (sizeof (T) * 5 + x)

operador nuevo [] (sizeof (T) * 5 + x, std :: align_val_t (alignof (T)))

Aquí, cada instancia de x es un valor no especificado no negativo que representa la sobrecarga de asignación de matriz; el resultado de la nueva expresión se compensará con esta cantidad del valor devuelto por el operador nuevo []. Esta sobrecarga se puede aplicar en todas las nuevas expresiones de matriz, incluidas las que hacen referencia al operador de la función de biblioteca new [] (std :: size_t, void *) y otras funciones de asignación de ubicaciones. La cantidad de gastos generales puede variar de una invocación de nueva a otra.

Sin embargo, en mi opinión, esta compensación x no puede ser arbitraria. Si no es un múltiplo de alineación, entonces la nueva expresión devolverá un puntero no alineado (en todos los casos. No solo el cero, sino también el parámetro de tamaño diferente a cero). Claramente eso no es lo que queremos.

Así que creo que esto es un agujero en el estándar. El valor de x debe restringir para que sea un múltiplo de alineación (al menos en el caso de asignación no cero). Pero debido a esta omisión, parece que el estándar no garantiza que una new[] expresión new[] devuelva un puntero alineado en absoluto (también en el caso distinto de cero).

C ++ permite la asignación dinámica de matrices de tamaño cero :

int* p = new int[0]; delete[] p;

No puedo hacer mucho con ese puntero (ya que la matriz no tiene elementos), pero la nueva expresión es necesaria para devolverme un puntero válido ( != nullptr ) que luego debo delete[] nuevamente como si fuera una matriz real.

¿Hay algún requisito con respecto a la alineación de la memoria devuelta por una nueva expresión? Considerar:

struct alignas(8) Foo { int x; }; Foo* p = new Foo[0]; delete[] p;

¿Se garantiza que p apunta a una dirección alineada con 8? Además, si escribo un asignador personalizado, ¿debo devolver los punteros a las direcciones alineadas en tal caso?