2011-01-11 21 views
12

¿Puedo verificar si un puntero determinado apunta a un objeto dentro de una matriz, especificado por sus límites?comprobando si los puntos del puntero dentro de una matriz

template <typename T> 
bool points_within_array(T* p, T* begin, T* end) 
{ 
    return begin <= p && p < end; 
} 

O hacer las comparaciones puntero invocar un comportamiento indefinido si p puntos fuera de los límites de la matriz? En ese caso, ¿cómo resuelvo el problema? Funciona con punteros vacíos? ¿O es imposible de resolver?

+1

Tu comparación funciona, pero en realidad no garantiza que el 'final' tenga nada que ver con la matriz. Creo que es mejor usar una talla (ver mi respuesta a continuación). – BeeBand

+0

@jweyrich? ¿Por qué dices eso? Suponiendo que el OP solo utiliza conversiones de puntero bien definidas, cada puntero del programa apunta a datos bien alineados (o son 'nulos'). Los datos no alineados esencialmente no existen de acuerdo con el estándar C++. 'p' garantiza que se alineará a menos que exista un comportamiento indefinido en el sitio de la llamada. – jalf

+0

@jalf: Retracté mi comentario. Gracias por la lección. – jweyrich

Respuesta

10

Aunque la comparación es válida sólo para los punteros dentro de la matriz y "uno más allá del final", es válido utilizar un conjunto o un mapa con un puntero como la clave, que utiliza std::less<T*>

Había una gran discusión en este camino de vuelta en 1996 en comp.std.c++

+0

¿Es porque std :: less de alguna manera evita mágicamente la comparación? –

+1

@Maxim: std :: less es necesario para implementar cualquier magia que se necesite para obtener resultados consistentes. Puede indicar que la dirección '0A00: 0001' es menor que' 0100: 0001', pero puede no indicar que son equivalentes. 'operator <' tiene permitido comparar solo las compensaciones e indicar que esas direcciones son equivalentes. –

+0

¿Podría referirme a cualquier implementación de biblioteca estándar estándar de C++ que lo haga? –

7

Directamente desde el MSDN documentation:

dos punteros de diferentes tipos no pueden ser comparados a menos que:

  • Un tipo es un tipo de clase derivada de otro tipo.
  • Al menos uno de los punteros se convierte (arroja) explícitamente para escribir void *. (El otro puntero se convierte implícitamente al tipo void * para la conversión.)

Así que un void* se puede comparar con cualquier otra cosa (incluyendo otro void*). Pero, ¿la comparación producirá resultados significativos?

Si dos punteros apuntan a elementos de la misma matriz o con el elemento de uno más allá del final de la matriz, la puntero al objeto con la mayor subíndice compara superior. La comparación de los punteros está garantizada solo válida cuando los punteros se refieren a objetos en la misma matriz oa la ubicación después del final de la matriz.

Parece que no. Si no sabe ya sabe que está comparando elementos dentro del conjunto (o simplemente lo pasa), entonces la comparación no garantiza que sea significativa.

Hay, sin embargo, una solución: El STL proporciona std::less<> y std::greater<>, que funcionará con cualquier tipo de puntero y producirá resultados válidos en todos los casos:

if (std::less<T*>()(p, begin)) { 
    // p is out of bounds 
} 

Actualización:

La respuesta a this question da la misma sugerencia (std::less) y también cita el estándar.

+3

No citaría MSDN como una fuente definitiva de C++ ya que a menudo es inexacta o específica de MS. –

+0

@Maxim Yegorushkin: Estoy de acuerdo con eso. Pero han hecho mucho trabajo para señalar problemas específicos de MS y han mejorado mucho ese problema. Además, esto solo confirma lo que ya "sabía". Y, por último, IMO MSDN es infinitamente más fácil de buscar que el estándar. – Jon

+0

Su presupuesto habla de punteros de diferentes tipos, mientras que el OP está interesado en comparar punteros del mismo tipo. –

4

El estándar C++ no especifica lo que ocurre cuando se comparan punteros con objetos que no residen en la misma matriz, por lo tanto, el comportamiento no definido. Sin embargo, el estándar C++ no es el único estándar que debe cumplir su plataforma. Otros estándares como POSIX especifican cosas que el estándar C++ deja como comportamiento indefinido. En plataformas con espacio de direcciones virtuales como Linux y Win32/64, puede comparar cualquier puntero sin causar ningún comportamiento indefinido.

1

comparaciones sobre los tipos de puntero no neccesarily dan como resultado un orden total. std :: less/std :: greater_equal do, sin embargo. Entonces ...

template <typename T> 
bool points_within_array(T* p, T* begin, T* end) 
{ 
    return std::greater_equal<T*>()(p, begin) && std::less<T*>()(p, end); 
} 

funcionará.

+2

_A_ orden total, no necesariamente uno en el que los intervalos separados no se "superponen". –

+0

Si los rangos se pueden superponer, no sería un orden total. – fizzer

+0

@fizzer: Sí, podría. Imagine que la matriz B aparece en las posiciones 0.5, 1.5, 2.5 cuando se compara con los índices de la matriz A. –

0

no se podía hacer esto con std::distance, es decir, su problema efectivamente se reduce a:

return distance(begin, p) >= 0 && distance(begin, p) < distance(begin, end); 

Dado este iterador de acceso aleatorio (puntero) se está pasando en, debe reducirse a algunos aritmética de punteros en lugar de comparaciones de punteros? (Supongo que el final realmente es el final y no el último elemento en el conjunto, si es el último, entonces cambie el menor que a <=).

Podría estar lejos de la marca ...

4

La única manera correcta de hacer esto es un enfoque como este.

template <typename T> 
bool points_within_array(T* p, T* begin, T* end) 
{ 
    for (; begin != end; ++begin) 
    { 
     if (p == begin) 
      return true; 
    } 
    return false; 
} 

Es obvio que esto no funciona si T == void. No estoy seguro si dos void* definen técnicamente un rango o no. Ciertamente, si usted tenía Derived[n], sería incorrecto decir que (Base*)Derived, (Base*)(Derived + n) define un rango válido por lo que no puede ver que es válida para definir un rango con otra cosa que no sea un indicador al tipo de elemento de la matriz real.

El método siguiente falla porque no se especifica qué < devuelve si los dos operandos no apuntan a miembros del mismo objeto o elementos de la misma matriz. (5,9 [expr.rel]/2)

template <typename T> 
bool points_within_array(T* p, T* begin, T* end) 
{ 
    return !(p < begin) && (p < end); 
} 

El método a continuación falla, ya que también se especifica lo std::less<T*>::operator() devoluciones si los dos operandos no apuntan a los miembros del mismo objeto o elementos de la misma matriz.

Es cierto que un std::less hay que especializarse para cualquier tipo de puntero para producir un orden total si el construido en < no, pero esto sólo es útil para usos tales como proporcionar una clave para un set o map. No se garantiza que el orden total no intercala matrices u objetos distintos entre sí.

Por ejemplo, en una arquitectura de memoria segmentada, el desplazamiento de objeto se podría usar para < y como el diferenciador más significativo para std::less<T*> con el índice de segmento utilizado para romper relaciones. En dicho sistema, un elemento de una matriz podría ordenarse entre los límites de una segunda matriz distinta.

template <typename T> 
bool points_within_array(T* p, T* begin, T* end) 
{ 
    return !(std::less<T*>()(p, begin)) && (std::less<T*>()(p, end)); 
} 
+0

¿Puedes usar == para comparar punteros como este, p. en una arquitectura segmentada? http://stackoverflow.com/q/4909766/511601 –

+0

@FredNurk: '==' tiene que ser capaz de comparar punteros que provienen de diferentes objetos completos, si eso implica comparar los identificadores de segmento, entonces eso es lo que la implementación tiene que hacer. hacer. (Si existe algo así como "el segmento actual" y esto puede cambiar, entonces el identificador del segmento debe ser parte de lo que se almacena como puntero; no esperaría obtener un objeto diferente si quitaba el mismo valor del puntero en un contexto diferente) –

Cuestiones relacionadas