Articulo de referencia

constante (programación informática)

En algunos lenguajes de programación , `const` es un calificador de tipo (una palabra clave aplicada a un tipo de dato ) que indica que los datos son de solo lectura. Si bien es...

En algunos lenguajes de programación , `const` es un calificador de tipo (una palabra clave aplicada a un tipo de dato ) que indica que los datos son de solo lectura. Si bien esto se puede usar para declarar constantes , consten la familia de lenguajes C difiere de construcciones similares en otros lenguajes en que es parte del tipo y, por lo tanto, tiene un comportamiento complicado cuando se combina con punteros , referencias, tipos de datos compuestos y verificación de tipos . En otros lenguajes, los datos no están en una sola ubicación de memoria , sino que se copian en tiempo de compilación para cada uso. [ 1 ] Los lenguajes que lo usan incluyen C , C++ , D , JavaScript , Julia y Rust .

Introducción

Cuando se aplica en la declaración de un objeto , [ a ] indica que el objeto es una constante : su valor no puede modificarse, a diferencia de una variable . Este uso básico —declarar constantes— tiene paralelismos en muchos otros lenguajes.

Sin embargo, a diferencia de otros lenguajes, en la familia de lenguajes C, el constes parte del tipo , no parte del objeto . Por ejemplo, en C, declara un objeto de tipo – el es parte del tipo, como si se analizara como "(const int) x" – mientras que en Ada , declara una constante (un tipo de objeto) de tipo: el es parte del objeto , pero no parte del tipo .constintx=1;xconst intconstX:constantINTEGER:=1_XINTEGERconstant

Esto tiene dos consecuencias sutiles. En primer lugar, constse puede aplicar a partes de un tipo más complejo; por ejemplo, const int* const x;declara un puntero constante a un entero constante, mientras que const int* x;declara un puntero variable a un entero constante, y int* const x;declara un puntero constante a un entero variable. En segundo lugar, dado que constforma parte del tipo, debe coincidir como parte de la comprobación de tipos. Por ejemplo, el siguiente código no es válido:

void f ( int & x ); // ... const int i ; f ( i );

porque el argumento de fdebe ser un entero variablei , pero es un entero constante . Esta coincidencia es una forma de corrección del programa y se conoce como corrección constante . Esto permite una forma de programación por contrato , donde las funciones especifican como parte de su firma de tipo si modifican sus argumentos o no, y si su valor de retorno es modificable o no. Esta verificación de tipos es de interés principalmente en punteros y referencias, no en tipos de valor básicos como los enteros, pero también para tipos de datos compuestos o tipos con plantillas como los contenedores . Está oculta por el hecho de que a constmenudo se puede omitir, debido a la coerción de tipos ( conversión de tipo implícita ) y a que C es paso por valor (C++ y D son paso por valor o paso por referencia).

Consecuencias

La idea de la constancia no implica que la variable, tal como se almacena en la memoria del ordenador , sea inmodificable. Más bien, constla constancia es una construcción de tiempo de compilación que indica lo que un programador debería hacer, no necesariamente lo que puede hacer. Sin embargo, cabe señalar que, en el caso de datos predefinidos (como const char*literales de cadena ), C constsuele ser inmodificable .

Distinción de las constantes

Si bien una constante no cambia su valor mientras el programa se está ejecutando, un objeto declarado constsí puede cambiar su valor mientras el programa se está ejecutando. Un ejemplo común son los registros de solo lectura dentro de los sistemas embebidos, como el estado actual de una entrada digital. Los registros de datos para entradas digitales a menudo se declaran como consty volatile. El contenido de estos registros puede cambiar sin que el programa haga nada ( volatile), pero sería incorrecto que el programa intentara escribir en ellos ( const).

Otros usos

Además, una función miembro (no estática) puede declararse como const. En este caso, el thispuntero dentro de dicha función es de tipo const T*en lugar de simplemente de tipo T*. [ 2 ] Esto significa que las funciones no constantes para este objeto no pueden ser llamadas desde dentro de dicha función, ni se pueden modificar las variables miembro . En C++, una variable miembro puede declararse como mutable, lo que indica que esta restricción no se aplica a ella. En algunos casos, esto puede ser útil, por ejemplo con el almacenamiento en caché , el conteo de referencias y la sincronización de datos . En estos casos, el significado lógico (estado) del objeto no cambia, pero el objeto no es físicamente constante ya que su representación bit a bit puede cambiar.

Sintaxis

En C, C++ y D, todos los tipos de datos, incluidos los definidos por el usuario, pueden declararse como constantes const, y la corrección constante exige que todas las variables u objetos se declaren como tales a menos que necesiten modificarse. Este uso proactivo de las constantes consthace que los valores sean "más fáciles de entender, rastrear y razonar" [ 3 ] , lo que aumenta la legibilidad y la comprensibilidad del código y simplifica el trabajo en equipo y el mantenimiento del código, ya que comunica información sobre el uso previsto de un valor. Esto puede ayudar tanto al compilador como al desarrollador al razonar sobre el código. También puede permitir que un compilador optimizador genere código más eficiente [ 4 ] .

Tipos de datos simples

Para tipos de datos simples que no son punteros, aplicar el constcalificador es sencillo. Puede ir a ambos lados de algunos tipos por razones históricas (por ejemplo, const char foo = 'a';es equivalente a char const foo = 'a';). En algunas implementaciones, usar constdos veces (por ejemplo, const char consto char const const) genera una advertencia pero no un error.

Punteros y referencias

Para los tipos de puntero y referencia, el significado de constes más complejo: puede referirse al puntero en sí, al valor al que apunta o a ambos const. Además, la sintaxis puede resultar confusa. Un puntero puede declararse como un constpuntero a un valor modificable, un puntero modificable a un constvalor o constun puntero a constun valor.Un constpuntero no puede reasignarse para que apunte a un objeto diferente del que se le asignó inicialmente, pero puede usarse para modificar el valor al que apunta (llamado el apuntado ). [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] Las variables de referencia en C++ son una sintaxis alternativa para constlos punteros. Un puntero a un constobjeto, por otro lado, puede reasignarse para que apunte a otra ubicación de memoria (que debe ser un objeto del mismo tipo o de un tipo convertible), pero no puede usarse para modificar la memoria a la que apunta. Un constpuntero a un constobjeto también puede declararse y no puede usarse para modificar el apuntado ni reasignarse para que apunte a otro objeto. El siguiente código ilustra estas sutilezas:

void foo ( int * p , const int * cp , int * const pc , const int * const cpc ) { * p = 0 ; // OK: modifica los datos a los que apunta p = NULL ; // OK: modifica el puntero* cp = 0 ; // ¡Error! No se puede modificar el dato al que apunta cp = NULL ; // OK: modifica el puntero* pc = 0 ; // OK: modifica los datos a los que apunta pc = NULL ; // ¡Error! No se puede modificar el puntero* cpc = 0 ; // ¡Error! No se puede modificar el dato al que apunta cpc = NULL ; // ¡Error! No se puede modificar el puntero }

Convención C

Siguiendo la convención habitual de C para las declaraciones, la declaración sigue al uso, y el *en un puntero se escribe en el puntero, indicando la desreferenciación . Por ejemplo, en la declaración int *p, la forma desreferenciada *pes un int, mientras que la forma de referencia pes un puntero a un int. Por lo tanto, constmodifica el nombre a su derecha. La convención de C++ es, en cambio, asociar *con el tipo, como en int* p, y leer constcomo modificando el tipo a la izquierda. const int * cpse puede leer como " *cpes un const int" (el valor es constante), o " cpes un const int *" (el puntero es un puntero a un entero constante). Por lo tanto:

int * p ; // *p es un valor entero const int * cp ; // *cp es una constante int int * const pc ; // pc es una constante int* const int * const cpc ; // cpc es un puntero constante y apunta a un valor constante

Convención de C++

Siguiendo la convención de C++ de analizar el tipo, no el valor, una regla general es leer la declaración de derecha a izquierda. Así, todo lo que se encuentra a la izquierda del asterisco se identifica como el tipo al que apunta, y todo lo que se encuentra a la derecha son las propiedades del puntero. Por ejemplo, en nuestro ejemplo anterior, const int*se puede leer como un puntero modificable que apunta a un entero no modificable, y int* constse puede leer como un puntero no modificable que apunta a un entero modificable.

Una regla más genérica para ayudar a comprender declaraciones y definiciones complejas funciona de la siguiente manera:

  1. encontrar el identificador de la declaración en cuestión
  2. leer hasta donde sea posible hacia la derecha (es decir, hasta el final de la declaración o hasta el siguiente paréntesis de cierre, lo que ocurra primero)
  3. Vuelva al punto donde comenzó la lectura y lea hacia atrás a la izquierda (es decir, hasta el comienzo de la declaración o hasta el paréntesis de apertura que coincida con el paréntesis de cierre encontrado en el paso anterior).
  4. Una vez que llegue al inicio de la declaración, finalice. De lo contrario, continúe en el paso 2, más allá del paréntesis de cierre que coincidió por última vez.

Aquí tienes un ejemplo:

Al leer hacia la izquierda, es importante leer los elementos de derecha a izquierda. Por lo tanto, un const int*se convierte en un puntero a unconst int y no en un constpuntero a unint .

En algunos casos, C/C++ permite constcolocar la palabra clave a la derecha del tipo. Aquí hay algunos ejemplos:

const int * cp ; // equivalente a int const* cp, const int * const cpc ; // equivalente a int const* const cpc

Aunque C/C++ permite tales definiciones (que se asemejan bastante al idioma inglés cuando se leen de izquierda a derecha), el compilador sigue leyendo las definiciones según el procedimiento mencionado anteriormente: de derecha a izquierda. Pero anteponer constuna constante a otra introduce rápidamente discrepancias entre lo que se pretende escribir y lo que el compilador interpreta como escrito. Consideremos los punteros a punteros:

int ** pp ; // un puntero a un puntero a enteros const int ** cpp ; // un puntero a un puntero a un valor entero constante (no un puntero a un puntero constante a enteros) int * const * pcp ; // un puntero a un puntero constante a valores enteros (no un puntero constante a un puntero a enteros) int ** const ppc ; // un puntero constante a punteros a enteros (ppc, el identificador, al ser constante no tiene sentido) const int ** const cppc ; // un puntero constante a punteros a valores enteros constantes

Algunos optan por escribir el símbolo de puntero en la variable, argumentando que adjuntarlo al tipo puede resultar confuso, ya que sugiere fuertemente un "tipo" de puntero, lo cual no es necesariamente el caso en C.

// dos formas: int * a ; // asterisco alineado a la izquierda int * a ; // asterisco alineado a la derechaint * a , b ; // confuso (a es un puntero a un int pero b es simplemente un int) int * a , b ; // a es un puntero a un int y b es un intint * a , * b ; // feo (a y b son ambos punteros a enteros, pero esta es una forma incómoda de escribirlo) int * a , * b ;

Tenga en cuenta que en C# , la alineación a la derecha del asterisco es ilegal. [ 10 ]

int * a , * b ; // ilegal en C# int * a , b ; // declara dos int*

Las preguntas frecuentes de Bjarne Stroustrup recomiendan declarar solo una variable por línea si se utiliza la convención de C++, para evitar este problema. [ 11 ]

Las mismas consideraciones se aplican a la definición de referencias y referencias de valores r:

int i = 22 ; const int & cr = i ; const int & cr2 = i , cr3 = i ; // confuso: // cr2 es una referencia, pero cr3 no lo es: // cr3 es una constante int inicializada con el valor de i// error: ya que las referencias no pueden cambiar de todos modos. int & const rc = myInt ;// C++: int && rref = int ( 5 ), value = 10 ; // confuso: rref es una referencia a rvalue, pero value es un simple int. int && rref = int ( 5 ), value = 10 ;

Las declaraciones más complejas se presentan al usar arreglos multidimensionales y referencias (o punteros) a punteros. Si bien a veces se argumenta que dichas declaraciones son confusas y propensas a errores, y que por lo tanto deberían evitarse o reemplazarse por estructuras de nivel superior, el procedimiento descrito al inicio de esta sección siempre puede utilizarse sin generar ambigüedades ni confusiones.

Parámetros y variables

constSe puede declarar tanto en parámetros de función como en variables ( estáticas o automáticas, incluyendo globales o locales). La interpretación varía según el uso. Una constvariable estática ( variable global o variable local estática ) es una constante y puede usarse para datos como constantes matemáticas, como const double PI = 3.14159–realmente más largas– o parámetros generales en tiempo de compilación. Una constvariable automática (variable local no estática) significa que se está realizando una única asignación , aunque se puede usar un valor diferente cada vez, como const int x_squared = x * x. Un constparámetro en paso por referencia significa que el valor referenciado no se modifica –es parte del contrato– mientras que un constparámetro en paso por valor (o el puntero mismo, en paso por referencia) no agrega nada a la interfaz (ya que el valor se ha copiado), pero indica que internamente, la función no modifica la copia local del parámetro (es una única asignación). Por esta razón, algunos prefieren usar consten parámetros solo para paso por referencia, donde cambia el contrato, pero no para paso por valor, donde expone la implementación.

C++

En C++, existen cuatro tipos diferentes de const:

  • const, la constante de tiempo de ejecución tradicional
  • constexpr, una constante o expresión que puede evaluarse en tiempo de compilación (ver constexpr )
  • consteval, que declara que cualquier llamada a una función debe producir un valor que sea una expresión en tiempo de compilación.
  • constinit, que declara que la expresión tiene inicialización estática (constante)

Métodos

Para aprovechar el enfoque de diseño por contrato para tipos definidos por el usuario (estructuras y clases), que pueden tener métodos así como datos miembros, el programador puede etiquetar los métodos de instancia como constsi no modificaran los miembros de datos del objeto. Aplicar el constcalificador a los métodos de instancia es, por lo tanto, una característica esencial para la corrección constante, y no está disponible en muchos otros lenguajes orientados a objetos como Java y C# o en C++/CLI de Microsoft o Managed Extensions for C++ . Si bien los métodos pueden ser llamados por objetos y no objetos por igual, los métodos que no son objetos solo pueden ser invocados por objetos que no son objetos. El modificador en un método de instancia se aplica al objeto al que apunta el puntero " ", que es un argumento implícito que se pasa a todos los métodos de instancia. Por lo tanto, tener métodos es una forma de aplicar la corrección constante al argumento implícito del puntero " " al igual que otros argumentos.constconstconstconstconstconstthisconstthis

Este ejemplo ilustra:

clase Entero { privado : int i ; público : // observe la etiqueta const [[ nodiscard ]] int get () const noexcept { return i ; }// Nótese la falta de "const" void set ( int j ) noexcept { i = j ; } };void foo ( Integer & nonConstInteger , const Integer & constInteger ) { int y = nonConstInteger . get (); // OK int x = constInteger . get (); // OK: get() es constantenonConstInteger.set ( 10 ); // OK: nonConstInteger es modificable constInteger.set ( 10 ); // ¡ Error! set ( ) es un método no constante y constInteger es un objeto calificado como constante }

En el código anterior, el thispuntero implícito " " a set()tiene el tipo " Integer* const"; mientras que el thispuntero " " a get()tiene el tipo " Integer const* const", lo que indica que el método no puede modificar su objeto a través del thispuntero " ".

A menudo, el programador proporcionará un método consty otro que no sea un constmétodo con el mismo nombre (pero posiblemente con usos muy diferentes) en una clase para dar cabida a ambos tipos de llamadores. Considere lo siguiente:

importar std ;usando std :: array ;clase IntegerArray { privado : array < int , 100 > datos ; público : [[ nodiscard ]] int & obtener ( int i ) noexcept { return datos [ i ]; }[[ nodiscard ]] int const & get ( int i ) const noexcept { return data [ i ]; } };void foo ( IntegerArray & a , const IntegerArray & ca ) { // Obtiene una referencia a un elemento de la matriz // y modifica su valor referenciado.a.get ( 5 ) = 42 ; // ¡OK! ( Llamada: int& IntegerArray::get(int)) ca.get ( 5 ) = 42 ; // ¡ Error! (Llamada: int const& IntegerArray::get(int) const ) }

La constnaturaleza del objeto que realiza la llamada determina qué versión IntegerArray::get()se invocará y, por lo tanto, si quien realiza la llamada recibe una referencia con la que puede manipular o solo observar los datos privados del objeto. Técnicamente, los dos métodos tienen firmas diferentes porque sus thispunteros " " tienen tipos distintos, lo que permite al compilador elegir el correcto. (Devolver una constreferencia a un int, en lugar de simplemente devolver el intpor valor, puede ser excesivo en el segundo método, pero la misma técnica se puede usar para tipos arbitrarios, como en la Biblioteca de Plantillas Estándar ).

Lagunas en la corrección de constantes

En C y C++ existen varias excepciones a la corrección absoluta mediante `const`. Estas excepciones existen principalmente para garantizar la compatibilidad con el código existente.

La primera, que se aplica solo a C++, es el uso de const_cast, que permite al programador eliminar el constcalificador, haciendo que cualquier objeto sea modificable. La necesidad de eliminar el calificador surge al usar código y bibliotecas existentes que no se pueden modificar pero que no son consistentes. Por ejemplo, considere este código:

// Prototipo para una función que no podemos cambiar pero que // sabemos que no modifica el objeto al que apunta. void myLibFunc ( int * p , int size );void callLibFunc ( const int * p , int size ) { myLibFunc ( p , size ); // ¡Error! Elimina el calificador constint * nonConstPtr = const_cast < int *> ( p ); // Eliminar calificador myLibFunc ( nonConstPtr , size ); // OK }

Sin embargo, cualquier intento de modificar un objeto que se declara constmediante una conversión constante produce un comportamiento indefinido según el estándar ISO C++. En el ejemplo anterior, si ptrhace referencia a una variable global, local o miembro declarada como const, o a un objeto asignado en el montón mediante new int const, el código solo es correcto si LibraryFuncrealmente no modifica el valor al que apunta ptr.

El lenguaje C necesita una excepción debido a una situación particular. Se permite definir variables con duración de almacenamiento estática con un valor inicial. Sin embargo, el inicializador solo puede usar constantes, como constantes de cadena y otros literales, y no se le permite usar elementos no constantes, como nombres de variables, independientemente de si los elementos del inicializador están declarados consto no, o de si la variable de duración estática está declarada consto no. Existe una forma no portable de inicializar una constvariable con duración de almacenamiento estática. Al construir cuidadosamente una conversión de tipo en el lado izquierdo de una asignación posterior, constse puede escribir en una variable, eliminando efectivamente el constatributo y "inicializándola" con elementos no constantes, como otras constvariables, etc. Escribir en una constvariable de esta manera puede funcionar como se espera, pero causa un comportamiento indefinido y contradice seriamente la corrección de const.

constexpr size_t BUFFER_SIZE = 8 * 1024 ; const size_t userTextBufferSize ; // El valor inicial depende de const BUFFER_SIZE, no se puede inicializar aquí....int setupUserTextBox ( TextBox * defaultTextBoxType , Rectangle * defaultTextBoxLocation ) { * ( size_t * ) & userTextBufferSize = BUFFER_SIZE - sizeof ( TextBoxControls ); // advertencia: podría funcionar, pero no está garantizado por C ... }

Otra laguna [ 12 ] se aplica tanto a C como a C++. Específicamente, los lenguajes dictan que los punteros y referencias a miembros son "superficiales" con respecto a la constnaturaleza de sus propietarios; es decir, un objeto contenedor consttiene todos constlos miembros excepto que los miembros a los que apuntan (y los que hacen referencia) siguen siendo mutables. Para ilustrarlo, considere este código C++:

struct MyStruct { int val ; int * ptr ; };void foo ( const MyStruct & s ) { int i = 42 ; s.val = i ; // Error: s es constante, por lo que val es un entero constante s.ptr = & i ; // Error : s es constante, por lo que ptr es un puntero constante a un entero *s.ptr = i ; // OK : los datos a los que apunta ptr siempre son mutables, aunque a veces esto no sea deseable }

Aunque el objeto spasado foo()es constante, lo que hace que todos sus miembros sean constantes, el objeto al que apunta s.ptrsigue siendo modificable, aunque esto puede no ser deseable desde el punto de vista de constla corrección, ya que spodría ser el único propietario del objeto al que apunta. Por esta razón, Meyers argumenta que el comportamiento predeterminado para los punteros y referencias a miembros debería ser la constpropiedad "profunda", que podría ser anulada por un mutablecalificador cuando el objeto al que apunta no pertenece al contenedor, pero esta estrategia crearía problemas de compatibilidad con el código existente. Por lo tanto, por razones históricas , esta laguna permanece abierta en C y C++.

Esta última laguna se puede cerrar usando una clase para ocultar el puntero detrás de una constinterfaz correcta, pero dichas clases o bien no admiten la semántica de copia habitual de un constobjeto (lo que implica que la clase contenedora tampoco se puede copiar mediante la semántica habitual) o permiten otras lagunas al permitir la eliminación de la constnaturaleza a través de una copia inadvertida o intencional.

Finalmente, varias funciones en la biblioteca estándar de C violan la corrección de const antes de C23 , ya que aceptan un constpuntero a una cadena de caracteres y devuelven un constpuntero que no es un puntero a una parte de la misma cadena. strstry strchrestán entre estas funciones. Algunas implementaciones de la biblioteca estándar de C++, como la de Microsoft [ 13 ], intentan cerrar esta laguna proporcionando dos versiones sobrecargadas de algunas funciones: una constversión " " y una constversión "no ".

Problemas

El uso del sistema de tipos para expresar constancia conlleva diversas complejidades y problemas, por lo que ha sido criticado y no se ha adoptado fuera de la reducida familia C (C, C++ y D). Java y C#, fuertemente influenciados por C y C++, rechazaron explícitamente constlos calificadores de tipo de estilo , expresando en su lugar la constancia mediante palabras clave que se aplican al identificador ( finalen Java consty readonlyen C#). Incluso dentro de C y C++, el uso de constvaría significativamente, con algunos proyectos y organizaciones que lo utilizan de forma consistente y otros que lo evitan.

strchrproblema

El constcalificador de tipo causa dificultades cuando la lógica de una función es independiente de si su entrada es constante o no, pero devuelve un valor que debe ser del mismo tipo calificado que la entrada. En otras palabras, para estas funciones, si la entrada es constante (calificada con `const`), el valor de retorno también debe serlo, pero si la entrada es variable (no calificada con `const` const), el valor de retorno también debe serlo. Debido a que la firma de tipo de estas funciones difiere, se requieren dos funciones (o potencialmente más, en caso de múltiples entradas) con la misma lógica, una forma de programación genérica .

Este problema surge incluso para funciones simples en la biblioteca estándar de C, en particular strchr; esta observación se la atribuye Ritchie a Tom Plum a mediados de la década de 1980. [ 14 ] La strchrfunción localiza un carácter en una cadena; formalmente, devuelve un puntero a la primera aparición del carácter cen la cadena s, y en C clásico (K&R C) su prototipo es:

char * strchr ( char * s , int c );

La strchrfunción no modifica la cadena de entrada, pero el valor de retorno suele ser utilizado por quien la llama para modificar la cadena, como por ejemplo:

if ( p = strchr ( q , '/' )) { * p = ' ' ; }

Así, por un lado, la cadena de entrada puede ser const(ya que no es modificada por la función), y si la cadena de entrada es constel valor de retorno también debería serlo, simplemente porque podría devolver exactamente el puntero de entrada, si el primer carácter coincide, pero por otro lado, el valor de retorno no debería ser constsi la cadena original no era const, ya que quien llama podría desear usar el puntero para modificar la cadena original.

En C++ esto se hace mediante sobrecarga de funciones , normalmente implementada mediante una plantilla , lo que da como resultado dos funciones, de modo que el valor de retorno tenga el mismo consttipo calificado que la entrada:

char * strchr ( char * s , int c ); const char * strchr ( const char * s , int c );

Estos, a su vez, pueden definirse mediante una plantilla:

plantilla < T > T * strchr ( T * s , int c ) { ... }

En D esto se maneja a través de la inoutpalabra clave, que actúa como un comodín para const, immutable o no calificado (variable), lo que produce: [ 15 ] [ b ]

inout ( char )* strchr ( inout ( char )* s , int c );

Sin embargo, en C ninguna de estas opciones es posible [ c ] ya que C no tiene sobrecarga de funciones y, en su lugar, esto se maneja mediante una única función donde la entrada es constante pero la salida es modificable:

char * strchr ( const char * s , int c );

Esto permite usar código C idiomático, pero elimina el calificador `const` si la entrada realmente lo contenía, lo que viola la seguridad de tipos . Esta solución fue propuesta por Ritchie y posteriormente adoptada. Esta diferencia es uno de los problemas de compatibilidad entre C y C++ .

Desde C23 , este problema se resuelve con el uso de la _Genericfacilidad del lenguaje: los identificadores de strchry las demás funciones afectadas por el problema se han convertido en macros que expanden una llamada a una función apropiada que devolverá un constpuntero si se les pasó uno y un puntero no calificado si se les pasó un puntero no calificado. [ 16 ]

D

En la versión 2 del lenguaje de programación D , existen dos palabras clave relacionadas con `const`. [ 17 ] La immutablepalabra clave denota datos que no pueden modificarse a través de ninguna referencia. La constpalabra clave denota una vista no mutable de datos mutables. A diferencia de C++ const, D consty immutableson "profundos" o transitivos , y cualquier cosa alcanzable a través de un objeto o es consto respectivamente .immutableconstimmutable

Ejemplo de constante frente a inmutable en D

int [] foo = new int [ 5 ]; // foo es mutable. const int [] bar = foo ; // bar es una vista constante de datos mutables. immutable int [] baz = foo ; // Error: todas las vistas de datos inmutables deben ser inmutables.int [] inmutable nums = new inmutable ( int )[ 5 ]; // No se puede crear ninguna referencia mutable a nums. const int [] constNums = nums ; // Funciona. inmutable es implícitamente convertible a const. int [] mutableNums = nums ; // Error: No se puede crear una vista mutable de datos inmutables.

Ejemplo de constante transitiva o profunda en D

clase LinkedList { int valor ; LinkedList siguiente ; }immutable LinkedList ll = new immutable ( LinkedList ); ll . next . value = 5 ; // No compilará. ll.next es de tipo immutable(LinkedList). // ll.next.value es de tipo immutable(int).

Historia

constfue introducido por Bjarne Stroustrup en C con Classes , el predecesor de C++ , en 1981, y originalmente se llamaba readonly. [ 18 ] [ 19 ] En cuanto a la motivación, Stroustrup escribe: [ 19 ]

"Cumplía dos funciones: como una forma de definir una constante simbólica que obedece a las reglas de ámbito y tipo (es decir, sin usar una macro) y como una forma de considerar un objeto en memoria como inmutable."

El primer uso, como alternativa tipada y con ámbito definido a las macros, se cumplió de forma análoga para las macros de tipo función mediante la inlinepalabra clave. Los punteros constantes y la * constnotación fueron sugeridos por Dennis Ritchie y, por lo tanto, adoptados. [ 19 ]

constLuego se adoptó en C como parte de la estandarización, y aparece en C89 (y versiones posteriores) junto con el otro calificador de tipo, volatile. [ 20 ] Se sugirió un calificador adicional, noalias, en la reunión de diciembre de 1987 del comité X3J11, pero fue rechazado; su objetivo finalmente se cumplió con la restrictpalabra clave en C99 . Ritchie no apoyó mucho estas adiciones, argumentando que no "tenían suficiente peso", pero finalmente no abogó por su eliminación del estándar. [ 21 ]

Posteriormente, D heredó constde C++, donde se conoce como constructor de tipos (no calificador de tipos ), y añadió dos constructores de tipos más, immutabley inout, para manejar casos de uso relacionados. [ d ]

Otros idiomas

Otros lenguajes no siguen el modelo de C/C++ al incluir la constancia como parte del tipo, aunque a menudo presentan construcciones superficialmente similares y pueden usar la constpalabra clave `constante`. Normalmente, esto se utiliza únicamente para constantes (objetos constantes).

C# tiene una constpalabra clave, pero con una semántica radicalmente diferente y más simple: significa una constante en tiempo de compilación y no forma parte del tipo. Es esencialmente equivalente a constexpren C y C++.

Nim tiene una constpalabra clave similar a la de C#: también declara una constante en tiempo de compilación en lugar de formar parte del tipo. Sin embargo, en Nim, una constante puede declararse a partir de cualquier expresión que pueda evaluarse en tiempo de compilación. [ 22 ] En C#, solo los tipos integrados de C# pueden declararse como const; los tipos definidos por el usuario, incluidas las clases, las estructuras y los arreglos, no pueden ser const. [ 23 ]

Rust tiene una constpalabra clave similar a la de C#, que declara constantes, funciones y otros símbolos en tiempo de compilación. [ 24 ] Por lo tanto, es esencialmente equivalente a la constexprde C y C++.

Java no usa const– en su lugar tiene final, que se puede aplicar a las declaraciones de "variables" locales y se aplica al identificador , no al tipo. Tiene un uso orientado a objetos diferente para los miembros de objetos, que es el origen del nombre. La especificación del lenguaje Java considera constcomo una palabra clave reservada – es decir, una que no se puede usar como identificador de variable – pero no le asigna ninguna semántica: es una palabra reservada (no se puede usar en identificadores) pero no una palabra clave (no tiene un significado especial). La palabra clave se incluyó como un medio para que los compiladores de Java detectaran y advirtieran sobre el uso incorrecto de palabras clave de C++. [ 25 ] Existe una solicitud de mejora para implementar constla corrección en el Proceso de la Comunidad Java , pero se cerró en 2005 basándose en que era imposible implementarla de forma retrocompatible. [ 26 ]

El Ada 83 contemporáneo tenía de forma independiente la noción de un objeto constante y una constantpalabra clave, [ 27 ] [ e ] con parámetros de entrada y parámetros de bucle implícitamente constantes. Aquí constantes una propiedad del objeto, no del tipo.

JavaScript tiene una constdeclaración que define una variable con ámbito de bloque que no se puede reasignar ni redeclarar. Define una referencia de solo lectura a una variable que no se puede redefinir, pero en algunas situaciones el valor de la variable en sí puede cambiar, como si la variable se refiere a un objeto y se modifica una propiedad del mismo. [ 28 ]

Véase también

Notas

  1. Formalmente, cuandoconstforma parte del tipo derivado más externo en una declaración; los punteros complican la discusión.
  2. El código D idiomático usaría un array en lugar de un puntero. [ 15 ]
  3. En C11 y posteriores_Genericse podría haber utilizado para implementar una constante correctastrchr.
    char * strchr_m ( char * s , int c ); const char * strchr_c ( const char * s , int c ); #define strchr(X,Y) _Generic((X), \  char*: strchr_m, \  const char*: strchr_c \ )(X,Y)
  4. D también introdujo elsharedconstructor de tipos, pero esto está relacionado con casos de uso devolatile, no deconst.
  5. El estándar Ada lo denomina " palabra reservada "; consulte ese artículo para obtener información sobre su uso.

Referencias

  1. "Elementos constantes – La referencia de Rust" . doc.rust-lang.org . Consultado el 22 de junio de 2022 .
  2. "El puntero" . Borrador del estándar C++ . Consultado el 30 de marzo de 2020. El tipo de en una función miembro cuyo tipo tiene un cv-qualifier-seq cv y cuya clase es es "puntero a cv ".thisthisXX
  3. Herb Sutter y Andrei Alexandrescu (2005). Estándares de codificación de C++ . pág. 30. Boston: Addison Wesley. ISBN 0-321-11358-6
  4. "¿Por qué el argumento kfree() es constante?" . lkml.org. 12-01-2013.
  5. "5.1. Extensiones implementadas en GNU Fortran: 5.1.16 Punteros Cray". El compilador de GNU Fortran . 2006. Archivado del original el 21/12/2022 . Consultado el 21/12/2022 .
  6. Fahey, Mark R.; Nagle, Dan (1999-04-19). "Punteros Fortran de Cray frente a punteros Fortran 90 y migración del Cray C90 al SGI Origin2000" (PDF) . Vicksburg, Massachusetts, EE. UU.: Estación Experimental de Vías Navegables del Cuerpo de Ingenieros del Ejército de EE. UU., Centro Principal de Recursos Compartidos. Archivado (PDF) del original el 23-12-2022 . Recuperado el 23-12-2022 .(8 páginas)
  7. "Apéndice C: Características y diferencias de Fortran 90 > Características > Punteros Cray" . Guía del usuario de Fortran . Oracle Corporation . 2010. Archivado del original el 21 de septiembre de 2021. Consultado el 23 de diciembre de 2022 .
  8. "Apéndice C: Características y diferencias de Fortran 90 > Características > Punteros de caracteres Cray" . Guía del usuario de Fortran . Oracle Corporation . 2010. Archivado del original el 23/12/2022 . Consultado el 23/12/2022 .
  9. "Capítulo 4. Tipos de datos". Manual de referencia del lenguaje Fortran, Volumen 1. Vol. 1. Silicon Graphics, Inc. 1999 [1993]. Número de documento: 007-3692-004. Archivado del original el 23/12/2022 . Recuperado el 23/12/2022 . (Nota: Adaptado de "FORTRAN 90 HANDBOOK" (1992, McGraw-Hill, Inc. ) por Walter S. Brainerd, Jeanne C. Adams, Jeanne T. Martin, Brian T. Smith y Jerrold L. Wagener).
  10. "Código inseguro, tipos de punteros y punteros a funciones" . learn.microsoft.com . Microsoft Learn. 6 de febrero de 2025.
  11. "Stroustrup: Preguntas frecuentes sobre estilo y técnica de C++" .
  12. Scott Meyers (2005). Effective C++, Tercera edición . págs. 21–23. Boston: Addison Wesley. ISBN 978-0-321-33487-9
  13. "strchr, wcschr, _mbschr (CRT)" . Msdn.microsoft.com . Consultado el 23/11/2017 .
  14. "Dennis Ritchie: Por qué no me gustan los calificadores de tipo X3J11" .
  15. 1 2 El lenguaje de programación D, Andrei Alexandrescu , 8.8: Propagación de un calificador del parámetro al resultado
  16. "WG14-N3020 : Funciones de biblioteca estándar que preservan calificadores" (PDF) . open-std.org . 13 de junio de 2022. Archivado (PDF) del original el 13 de octubre de 2022. 
  17. "const(FAQ) – Lenguaje de programación D" . Digitalmars.com . Consultado el 18 de agosto de 2013 .
  18. Bjarne Stroustrup , "Extensiones del concepto de tipo del lenguaje C", Memorando técnico interno de Bell Labs, 5 de enero de 1981.
  19. 1 2 3 Rivalidad entre hermanos: C y C++ , Bjarne Stroustrup , 2002, pág. 5
  20. Dennis M. Ritchie , " El desarrollo del lenguaje C" (enlace obsoleto archivado el 15 de julio de 2012 en archive.today ), 2003: "X3J11 también introdujo una serie de pequeñas adiciones y ajustes, por ejemplo, los calificadores de tipo const y volatile , y reglas de promoción de tipos ligeramente diferentes."
  21. "Para empezar, quiero decir que no estoy convencido de que ni siquiera los calificadores anteriores a diciembre ('const' y 'volatile') sean realmente útiles; sospecho que lo que añaden al coste de aprender y usar el lenguaje no se compensa con una mayor expresividad. 'Volatile', en particular, es un adorno para aplicaciones esotéricas y se expresa mucho mejor por otros medios. Su principal virtud es que casi todo el mundo puede olvidarse de él. 'Const' es a la vez más útil y más intrusivo; es imposible evitar aprender sobre él, debido a su presencia en la interfaz de la biblioteca. Sin embargo, no defiendo la eliminación de los calificadores, aunque solo sea porque ya es demasiado tarde."
  22. Manual de Nim: Sección Const
  23. const (Referencia de C#)
  24. "const - Rust" . doc.rust-lang.org . Consultado el 1 de septiembre de 2025 .
  25. Gosling, James; Joy, Bill; Steele, Guy. "Especificación del lenguaje Java, tercera edición" .
  26. "ID de error: JDK-4211070 Java debería admitir parámetros constantes (como C++) para el mantenimiento del código [ sic ] " . Bugs.sun.com . Consultado el 4 de noviembre de 2014 .
  27. 1815A3.2.1 . Declaraciones de objetos. Archivado el 20 de octubre de 2014 en Wayback Machine : "El objeto declarado es una constante si la palabra reservada 'constant' aparece en la declaración del objeto; la declaración debe incluir entonces una inicialización explícita. El valor de una constante no se puede modificar después de la inicialización. Los parámetros formales de modo de entrada de subprogramas y entradas, y los parámetros formales genéricos de modo de entrada, también son constantes; un parámetro de bucle es una constante dentro del bucle correspondiente; un subcomponente o segmento de una constante es una constante."
  28. "const" . MDN . Consultado el 31 de octubre de 2017 .
  • "Const-Corrección" de Herb Sutter
  • "¿Optimización constante?" por Herb Sutter
  • Preguntas frecuentes de C++ simplificadas: La corrección de `const` por Marshall Cline
  • Sección " Sustitución de valores " del libro electrónico gratuito Thinking in C++ de Bruce Eckel.
  • "Aquí hay un const, allí hay un const" de Walter Bright
  • "Constante e invariante" de la especificación del lenguaje de programación D, versión 2 (experimental)
Obtenido de " https://en.wikipedia.org/w/index.php?title=Const_(computer_programming)&oldid=1361328264 "