Pregunta ¿Puede ptrdiff_t representar todas las restas de punteros a elementos del mismo objeto de matriz?


Para la resta de punteros i y j a elementos del mismo objeto de matriz la nota en [expr.add # 5] lee:

[Nota: Si el valor i-j no está en el rango de valores representables de tipo std​::​ptrdiff_­t, el comportamiento no está definido. -nota final ]

Pero dado [support.types.layout # 2], el cual establece que (énfasis mía):

  1. El tipo ptrdiff_­t es un tipo de entero con signo definido por la implementación que puede aguantar la diferencia de dos subíndices en un objeto de matriz, como se describe en [expr.add].

¿Es posible que el resultado de i-j no estar en el rango de valores representables de ptrdiff_t?

PD: Me disculpo si mi pregunta es causada por mi pobre conocimiento del idioma inglés.

EDITAR: Relacionado: ¿Por qué el tamaño máximo de una matriz es "demasiado grande"?


32
2018-03-20 09:27


origen


Respuestas:


¿Es posible que el resultado de i-j no estar en el rango de valores representables de ptrdiff_t?

Sí, pero es poco probable.

De hecho, [support.types.layout]/2 no dice mucho, excepto las reglas adecuadas sobre la resta de punteros y ptrdiff_t están definidos en [expr.add]. Así que veamos esta sección.

[expr.add]/5

Cuando se restan dos punteros a elementos del mismo objeto de matriz, el tipo de resultado es un tipo integral con signo definido por la implementación; este tipo debe ser del mismo tipo que se define como std​::​ptrdiff_­t en el <cstddef> encabezamiento.

En primer lugar, tenga en cuenta que el caso donde i y j no se consideran los índices de subíndices de diferentes matrices. Esto permite tratar i-j como P-Q estaría donde P es un puntero al elemento de una matriz en subíndice i y Q es un puntero al elemento de lo mismo array en subíndice j. De hecho, restar dos punteros a elementos de diferentes matrices es comportamiento indefinido:

[expr.add]/5

Si las expresiones P y Q señalar, respectivamente, los elementos x[i] y x[j] del mismo objeto de matriz x, la expresion P - Q tiene el valor i−j   ; de lo contrario, el comportamiento no está definido.

Como conclusión, con la notación definida anteriormente, i-j y P-Q se definen para tener el mismo valor, siendo este último de tipo std::ptrdiff_t. Pero no se dice nada sobre la posibilidad de que este tipo tenga tal valor. Esta pregunta puede, sin embargo, ser respondida con la ayuda de std::numeric_limits; especialmente, uno puede detectar si una matriz some_array es demasiado grande para std::ptrdiff_t para mantener todas las diferencias de índice:

static_assert(std::numeric_limits<std::ptrdiff_t>::max() > sizeof(some_array)/sizeof(some_array[0]),
    "some_array is too big, subtracting its first and one-past-the-end element indexes "
    "or pointers would lead to undefined behavior as per [expr.add]/5."
);

Ahora, en el objetivo habitual, esto normalmente no sucedería como sizeof(std::ptrdiff_t) == sizeof(void*); lo que significa que una matriz necesitaría ser estúpidamente grande para ptrdiff_t sobrepasar. Pero no hay garantía de eso.


5
2018-03-20 09:30



Creo que es un error de las palabras.

La regla en [expr.add] se hereda de la misma regla para la resta del puntero en el estándar C. En el estándar C, ptrdiff_t no es necesario que contenga ninguna diferencia de dos subíndices en un objeto de matriz.

La regla en [support.types.layout] proviene de Core Language Issue 1122. Agregó definiciones directas para std::size_t y std::ptrdiff_t, que se supone que resuelve el problema de la definición circular. No veo que haya ninguna razón (al menos no mencionada en ningún documento oficial) para hacer std::ptrdiff_t mantener cualquier diferencia de dos subíndices en un objeto de matriz. Supongo que solo usa una definición incorrecta para resolver el problema de definición circular.

Como otra evidencia, [diff.library] no menciona ninguna diferencia entre std::ptrdiff_t en C ++ y ptrdiff_t en C. ya que en C ptrdiff_t no tiene tal restricción, en C ++ std::ptrdiff_t no debería tener tal restricción también.


1
2018-03-23 15:45