¿Cuándo y para qué fines se debe usar la palabra clave const en C para las variables?

59

Mientras revisaba mi código aquí,const surgió el problema del uso de la palabra clave. Entiendo que se usa para implementar el comportamiento de solo lectura en las variables.

Estoy confundido acerca de cuáles son las diversas situaciones cuando puede ser útil.

  • ¿Debería usarse por razones de claridad en los prototipos de funciones?
  • ¿Debería usarse como medida de seguridad durante el desarrollo del código?
  • ¿Debería usarse en el ámbito de varias funciones para declarar constantes de tiempo de ejecución?
  • ¿Debería usarse en absoluto?

Estas preguntas son solo ejemplos de la confusión que estoy enfrentando. La confusión general es

  • ¿Cuándo debería ser la constpalabra clave utilizada en la programación en C?
  • ¿Cuáles son los diversos tipos de beneficios que se pueden obtener al usar esta palabra clave en C?
  • ¿Hay alguna desventaja de usar constpalabras clave?


Se ha señalado que esta pregunta puede ser demasiado amplia debido a todas estas preguntas en el detalle de mi pregunta. Solo quería aclarar que estas preguntas son solo para aclarar la confusión con respecto a la pregunta principal.

¿Cuándo y para qué fines se debe usar la palabra clave const en C para las variables?

También se puede reformular como

El uso adecuado de la constpalabra clave en C` con los pros y los contras de la misma.

Aseem Bansal
fuente
Para aquellos que voten para que se cierre, explique por qué no cae en la categoría de Specific issues with software development. Estoy siendo bastante específico.
Aseem Bansal
Supongo que es porque hiciste varias preguntas en la misma publicación y, por lo tanto, tu pregunta cae en la categoría de Demasiado amplio: "Hay demasiadas respuestas posibles, o las buenas respuestas serían demasiado largas para este formato. Agregue detalles para reducir el conjunto de respuestas o para aislar un problema que puede responderse en unos pocos párrafos ".
Robert Harvey
1
@RobertHarvey Todas estas preguntas son solo para explicar mi confusión. Mi única pregunta sigue siendo el título de esta pregunta. Una buena respuesta a eso cubriría todas estas preguntas relacionadas. Entonces sigue siendo un specific issue. El uso adecuado de la constpalabra clave en C` con los pros y los contras de la misma.
Aseem Bansal
@RobertHarvey ¿Es mejor ahora?
Aseem Bansal

Respuestas:

65

Al revisar el código, aplico las siguientes reglas:

  • Utilice siempre constpara parámetros de función pasados ​​por referencia donde la función no modifica (o libera) los datos apuntados.

    int find(const int *data, size_t size, int value);
  • Utilice siempre constpara constantes que de otro modo podrían definirse utilizando un #define o una enumeración. Como resultado, el compilador puede ubicar los datos en la memoria de solo lectura (ROM) (aunque el enlazador suele ser una mejor herramienta para este propósito en los sistemas integrados).

    const double PI = 3.14;
  • Nunca use const en un prototipo de función para un parámetro pasado por valor . No tiene significado y, por lo tanto, es solo 'ruido'.

    // don't add const to 'value' or 'size'
    int find(const int *data, size_t size, const int value); 
    
  • Cuando corresponda, úselo const volatileen ubicaciones que el programa no pueda cambiar, pero que podrían cambiar. Los registros de hardware son el caso de uso típico aquí, por ejemplo, un registro de estado que refleja el estado de un dispositivo:

    const volatile int32_t *DEVICE_STATUS =  (int32_t*) 0x100;

Otros usos son opcionales. Por ejemplo, los parámetros de una función dentro de la implementación de la función se pueden marcar como const.

// 'value' and 'size can be marked as const here
int find(const int *data, const size_t size, const int value)  
{
     ... etc

o la función devuelve valores o cálculos que se obtienen y luego nunca cambian:

char *repeat_str(const char *str, size_t n) 
{
    const size_t len = strlen(str);
    const size_t buf_size = 1 + (len * n);
    char *buf = malloc(buf_size);
    ...

Estos usos de constsolo indican que no cambiará la variable; no cambian cómo o dónde se almacena la variable. El compilador, por supuesto, puede determinar que una variable no se cambia, pero al agregarla constle permite hacer cumplir eso. Esto puede ayudar al lector y agregar algo de seguridad (aunque si sus funciones son lo suficientemente grandes o complicadas como para que esto haga una gran diferencia, posiblemente tenga otros problemas). Editar, por ejemplo. una función densamente codificada de 200 líneas con bucles anidados y muchos nombres de variables largos o similares, sabiendo que ciertas variables nunca cambian podría facilitar la comprensión significativamente. Tales funciones han sido mal diseñadas o mantenidas.


Problemas con const. Probablemente escuchará el término "envenenamiento constante". Esto ocurre cuando agregar consta un parámetro de función hace que se propague 'constness'.

Editar - envenenamiento constante: por ejemplo en la función:

int function_a(char * str, int n)
{
    ...
    function_b(str);
    ...
}

si cambiamos stra const, entonces debemos asegurarnos de que fuction_btambién se necesita a const. Y así sucesivamente si se function_bpasa stra function_c, etc. Como puede imaginar, esto podría ser doloroso si se propaga en muchos archivos / módulos separados. Si se propaga a una función que no se puede cambiar (por ejemplo, una biblioteca del sistema), entonces se hace necesaria una conversión. Por lo tanto, rociar el constcódigo existente tal vez sea un problema. Sin embargo, en el nuevo código, es mejor constcalificar consistentemente donde sea apropiado.

El problema más insidioso constes que no estaba en el idioma original. Como complemento, no encaja del todo. Para empezar, tiene dos significados (como en las reglas anteriores, que significa "No voy a cambiar esto" y "esto no se puede modificar"). Pero más que eso, puede ser peligroso. Por ejemplo, compile y ejecute este código y (según el compilador / opciones) puede bloquearse cuando se ejecuta:

const char str[] = "hello world\n";
char *s = strchr(str, '\n');
*s = '\0';

strchrdevuelve un char*no a const char*. Como su parámetro de llamada es const, debe enviar el parámetro de llamada a char*. Y en este caso, eso desecha la verdadera propiedad de almacenamiento de solo lectura. Editar: esto se aplica generalmente a los vars en la memoria de solo lectura. Por 'ROM', me refiero no solo a la ROM física sino a cualquier memoria que esté protegida contra escritura, como sucede con la sección de código de los programas que se ejecutan en un sistema operativo típico.

Muchas funciones de biblioteca estándar se comportan de la misma manera, así que tenga cuidado: cuando tiene constantes reales (es decir, almacenadas en ROM) debe tener mucho cuidado de no perder su const.

William Morris
fuente
Realmente no tiene dos significados. Simplemente significa, como usted dice, " No voy a cambiar esto". Por ejemplo, es perfectamente válido tener una const volatilevariable.
detly
2
Eso es cierto para los parámetros de función, pero para las constantes en el alcance del archivo, por ejemplo, una constvariable realmente es de solo lectura: constdice "Esto no se puede modificar". A, const volatilepor otro lado, dice "Esto no se puede modificar, pero podría cambiar". Agregaré una mención de volátiles a mi respuesta.
William Morris
No estoy en desacuerdo con su comentario, solo con la idea de que es una instrucción para el compilador que lo ponga en ROM. Eso implica algún tipo de protección en tiempo de ejecución contra el comportamiento indefinido (es decir, modificar una constante de alguna manera), lo que puede conducir a malentendidos como "¿por qué puedo cambiar esta constvariable a través de un no constpuntero" (una pregunta común en SO y todos los demás foros de C !). En general, me gusta esta respuesta, aunque :)
detly
Tiene razón, "Poner esto en la ROM" es inexacto (aunque sucinto :-) - Lo cambiaré a "Esto no se puede modificar", lo cual es más preciso. Gracias por tus comentarios.
William Morris
Este es un gran resumen. Me gusta formular una frase que constdeclara tanto datos de solo lectura como vistas de datos de solo lectura, la diferencia es "esto no se puede modificar" versus "no se puede modificar esto".
Jon Purdy
8

Generalmente en cualquier lenguaje de programación se recomienda usar consto el modificador equivalente ya que

  • Puede aclararle a la persona que llama que lo que pasó no va a cambiar
  • Posibles mejoras de velocidad ya que el compilador sabe con certeza que puede omitir ciertas cosas que solo son relevantes si el parámetro puede cambiar
  • Protección contra usted mismo cambiando accidentalmente el valor
TheLQ
fuente
1

Sí, es básicamente la respuesta de TheLQ.

Es una medida de seguridad para el programador para que no modifique una variable y no invoque funciones que puedan modificarla. En una matriz o estructura, el especificador const indica que los valores de su contenido no se modificarán, e incluso el compilador no le permitirá hacerlo. Sin embargo, aún puede cambiar fácilmente el valor de la variable con solo un reparto.

En lo que generalmente veo, se usa principalmente para agregar valores constantes en el código y para indicar que la matriz o estructura no se modificará si llama a una función en particular. Esta última parte es importante, porque cuando llama a una función que modificará su matriz o estructura, es posible que desee conservar la versión original, por lo que crea una copia de la variable y luego la pasa a la función. Si ese no es el caso, no necesita la copia, obviamente, así que, por ejemplo, puede cambiar,

int foo(Structure s);

a

int foo(const Structure * s);

y no obtener la copia de arriba.

Solo para agregar, tenga en cuenta que C tiene reglas particulares con el especificador const. Por ejemplo,

int b = 1;
const int * a = &b;

no es lo mismo que

int b = 1;
int * const a = &b;

El primer código no le permitirá modificar a. En el segundo caso, el puntero es constante pero su contenido no lo es, por lo que el compilador le permitirá decir * a = 3;sin un error del compilador, pero no puede hacer aque sea una referencia a otra cosa.

lgarcia
fuente
0

De acuerdo con las declaraciones de TheLQ:

Cuando se trabaja con un equipo de programadores, declarar constes una buena manera de indicar que dicha variable no debe modificarse, o simplemente para recordarse en proyectos grandes. Es útil en ese sentido y puede ahorrar muchos dolores de cabeza.

David Otano
fuente