En el lenguaje de programación C , restrict
es una palabra clave que se puede utilizar en declaraciones de punteros . Al agregar este calificador de tipo, un programador le sugiere al compilador que durante la vida útil del puntero, solo el puntero en sí o un valor derivado directamente de él (como pointer + 1
) se utilizará para acceder al objeto al que apunta.
restrict
limita los efectos del alias del puntero , lo que ayuda a las optimizaciones . Si no se sigue la declaración de intenciones y un puntero independiente accede al objeto, esto dará como resultado un comportamiento indefinido . El uso de este calificador de tipo permite que el código C logre el mismo rendimiento que el mismo programa escrito en Fortran . Fue introducido en el estándar C99 . [1]
C ++ no tiene soporte estándar para restrict
, pero muchos compiladores tienen equivalentes que por lo general el trabajo tanto en C ++ y C, tales como el GCC 's y Clang ' s __restrict__
, y Visual C ++ 's __declspec(restrict)
. Además, __restrict
es compatible con esos tres compiladores. La interpretación exacta de estas palabras clave alternativas varía según el compilador:
- En compiladores de estilo Unix como GCC y Clang,
__restrict
y__restrict__
significan exactamente lo mismo que su contraparte C. Las extensiones incluyen permitir que se apliquen a tipos de referencia ythis
. [2] - En Visual C ++, se proporcionan varios calificadores sin alias:
__declspec(restrict)
se aplica a la declaración de función y sugiere que el puntero devuelto no tiene un alias.__restrict
se usa en el mismo lugar querestrict
, pero la sugerencia sin alias no se propaga como enrestrict
. También se amplía para tipos de unión .
Mejoramiento
Si el compilador sabe que solo hay un puntero a un bloque de memoria, puede producir un código mejor optimizado. Por ejemplo:
void updatePtrs ( size_t * ptrA , size_t * ptrB , size_t * val ) { * ptrA + = * val ; * ptrB + = * val ; }
En el código anterior, los punteros ptrA
, ptrB
y val
pueden hacer referencia a la misma ubicación de memoria , por lo que el compilador puede generar un código menos óptimo:
; Máquina RISC hipotética. ldr r12 , [ val ] ; Cargue la memoria en val en r12. ldr r3 , [ ptrA ] ; Cargue la memoria en ptrA en r3. agregue r3 , r3 , r12 ; Realice la suma: r3 = r3 + r12. str r3 , [ ptrA ] ; Almacene r3 en la ubicación de memoria ptrA, actualizando el valor. ldr r3 , [ ptrB ] ; Es posible que 'load' tenga que esperar hasta que se complete la 'tienda' anterior. ldr r12 , [ val ] ; Tienes que cargar una segunda vez para asegurar la consistencia. agregar r3 , r3 , r12 str r3 , [ ptrB ]
Sin embargo, si restrict
se utiliza la palabra clave y la función anterior se declara como
void updatePtrs ( size_t * restringir ptrA , size_t * restringir ptrB , size_t * restringir val );
luego, el compilador puede asumir que ptrA
, ptrB
y val
apuntar a diferentes ubicaciones y actualizar la ubicación de la memoria a la que hace referencia un puntero no afectará las ubicaciones de la memoria a las que hacen referencia los otros punteros. El programador, no el compilador, es responsable de asegurarse de que los punteros no apunten a ubicaciones idénticas. El compilador puede, por ejemplo, reorganizar el código, primero cargando todas las ubicaciones de la memoria y luego realizando las operaciones antes de volver a enviar los resultados a la memoria.
ldr r12 , [ val ] ; Tenga en cuenta que val ahora solo se carga una vez. ldr r3 , [ ptrA ] ; Además, todas las 'cargas están al principio ... ldr r4 , [ ptrB ] agregan r3 , r3 , r12 agregan r4 , r4 , r12 str r3 , [ ptrA ] ; ... todas las tiendas al final. str r4 , [ ptrB ]
El código de ensamblaje anterior es más corto porque val
se carga solo una vez. Además, dado que el compilador puede reorganizar el código más libremente, el compilador puede generar código que se ejecuta más rápido. En la segunda versión del ejemplo anterior, todas las store
operaciones se llevan a cabo después de las load
operaciones, lo que garantiza que el procesador no tendrá que bloquearse en medio del código para esperar hasta store
que se completen las operaciones.
Tenga en cuenta que el código generado real puede tener diferentes comportamientos. El beneficio con el miniejemplo anterior tiende a ser pequeño y, en casos de la vida real, los bucles grandes que realizan un gran acceso a la memoria tienden a ser lo que realmente ayuda con la restricción.
Como se mencionó anteriormente, no está definido cómo se comporta el código incorrecto , el compilador solo asegura que el código generado funcione correctamente si el código sigue la declaración de intenciones.
Advertencias del compilador
Para ayudar a prevenir un código incorrecto, algunos compiladores y otras herramientas intentan detectar cuándo se han pasado argumentos superpuestos a funciones con parámetros marcados restrict
. [3] El estándar de codificación CERT C considera que el uso indebido restrict
y las funciones de biblioteca marcadas con él (EXP43-C) es una fuente probable de errores de software, aunque a noviembre de 2019 no se sabe que esto haya causado vulnerabilidades. [4]
Referencias
- ^ Ulrich Drepper (23 de octubre de 2007). "Memoria parte 5: Qué pueden hacer los programadores" . Lo que todo programador debería saber sobre la memoria . lwn.net .
... Las reglas de alias por defecto de los lenguajes C y C ++ no ayudan al compilador a tomar estas decisiones (a menos que se use la restricción, todos los accesos de puntero son fuentes potenciales de alias). Esta es la razón por la que Fortran sigue siendo un lenguaje preferido para la programación numérica: facilita la escritura de código rápido. (En teoría, la palabra clave restrict introducida en el lenguaje C en la revisión de 1999 debería resolver el problema. Sin embargo, los compiladores aún no se han puesto al día. La razón es principalmente que existe demasiado código incorrecto que podría inducir a error al compilador y generar código de objeto.)
- ^ "Punteros restringidos" . Usando la colección de compiladores GNU (GCC) .
- ^ "Opciones de advertencia: -Wrestrict" . GCC . Consultado el 19 de noviembre de 2019 .
- ^ "EXP43-C. Evite el comportamiento indefinido cuando utilice punteros calificados por restricción" . SEI CERT C de codificación estándar . Consultado el 19 de noviembre de 2019 .
- "ISO / IEC 9899: Borrador del Comité TC2" (PDF) . ISO . 6 de mayo de 2005: 108–112 . Consultado el 22 de diciembre de 2008 . Cite journal requiere
|journal=
( ayuda )
enlaces externos
- Desmitificando la palabra clave restringida : explicación y ejemplos de uso
- Paredes, Douglas. "Cómo utilizar el calificador restrictivo en C" . Oracle ™ . Consultado el 21 de noviembre de 2012 .
- Punteros restringidos en C : el fundamento original detrás de la definición