¿Cuál es el punto de las constantes simbólicas?

Tengo problemas para entender cuál es el punto de las constantes simbólicas en C, estoy seguro de que existe una razón para ellas pero parece que no veo por qué no usarías una variable.

#include  main() { float fahr, celsius; float lower, upper, step; lower = 0; upper = 300; step = 20; printf("%s\t %s\n", "Fahrenheit", "Celsius"); fahr = lower; while (fahr <= upper) { celsius = (5.0 / 9.0) * (fahr - 32.0); printf("%3.0f\t\t %3.2f\n", fahr, celsius); fahr = fahr + step; } } 

Vs.

 #include  #define LOWER 0 #define UPPER 300 #define STEP 20 main() { float fahr, celsius; printf("%s\t %s\n", "Fahrenheit", "Celsius"); fahr = LOWER; while (fahr <= UPPER) { celsius = (5.0 / 9.0) * (fahr - 32.0); printf("%3.0f\t\t %3.2f\n", fahr, celsius); fahr = fahr + STEP; } } 

El (pre) comstackdor sabe que las constantes simbólicas no cambiarán. Sustituye el valor de la constante en tiempo de comstackción. Si la “constante” está en una variable, por lo general no puede darse cuenta de que la variable nunca cambiará de valor. En consecuencia, el código comstackdo debe leer el valor de la memoria asignada a la variable, lo que puede hacer que el progtwig sea un poco más lento y más grande.

En C ++, puede declarar que una variable es const , lo que le dice al comstackdor más o menos lo mismo. Esta es la razón por la cual las constantes simbólicas están mal vistas en C ++.

Un buen ejemplo de por qué las constantes nombradas son beneficiosas proviene del excelente libro La práctica de la progtwigción de Kernighan y Pike.

§1.5 números mágicos

[…] Este extracto de un progtwig para imprimir un histogtwig de frecuencias de letras en un terminal con dirección de cursor de 24 por 80 es innecesariamente opaco debido a una gran cantidad de números mágicos:

 ... fac = lim / 20; if (fac < 1) fac = 1; for (i = 0, col = 0; i < 27; i++, j++) { col += 3; k = 21 - (let[i] / fac); star = (let[i] == 0) ? ' ' : '*'; for (j = k; j < 22; j++) draw(j, col, star); } draw(23, 2, ' '); for (i = 'A'; i <= 'Z'; i++) printf("%c ", i); 

El código incluye, entre otros, los números 20, 21, 22, 23 y 27. Están claramente relacionados ... ¿o son? De hecho, solo hay tres números críticos para este progtwig: 24, el número de filas en la pantalla; 80, el número de columnas; y 26, el número de letras en el alfabeto. Pero ninguno de estos aparece en el código, lo que hace que los números que hacen aún más mágicos.

Al dar nombres a los números principales en el cálculo, podemos hacer que el código sea más fácil de seguir. Descubrimos, por ejemplo, que el número 3 proviene de (80 - 1) / 26 y que debe tener 26 entradas, no 27 (un error off-by-one quizás provocado por las coordenadas de la pantalla de 1 índice). Haciendo un par de otras simplificaciones, este es el resultado:

 enum { MINROW = 1, /* top row */ MINCOL = 1, /* left edge */ MAXROW = 24, /* bottom edge (<=) */ MAXCOL = 80, /* right edge (<=) */ LABELROW = 1, /* position of labels */ NLET = 26, /* size of alphabet */ HEIGHT = (MAXROW - 4), /* height of bars */ WIDTH = (MAXCOL - 1)/NLET /* width of bars */ }; ... fac = (lim + HEIGHT - 1) / HEIGHT; if (fac < 1) fac = 1; for (i = 0; i < NLET; i++) { if (let[i] == 0) continue; for (j = HEIGHT - let[i]/fac; j < HEIGHT; j++) draw(j+1 + LABELROW, (i+1)*WIDTH, '*'); } draw(MAXROW-1, MINCOL+1, ' '); for (i = 'A'; i <= 'Z'; i++) printf("%c ", i); 

Ahora está más claro lo que hace el bucle principal; es un bucle idiomático de 0 a NLET, que indica que el bucle está sobre los elementos de los datos. También las llamadas a draw son más fáciles de entender porque palabras como MAXROW y MINCOL nos recuerdan el orden de los argumentos. Lo más importante es que ahora es posible adaptar el progtwig a otro tamaño de pantalla o datos diferentes. Los números se desmitifican y el código también.

El código revisado en realidad no usa MINROW, lo cual es interesante; uno se pregunta cuál de los 1 residuales debe ser MINROW.

Las variables tienen un ámbito local con respecto a la estructura en la que están declaradas. Por supuesto, puede usar variables en lugar de constantes simbólicas, pero eso puede requerir mucho trabajo. Considera una aplicación que usa frecuentemente radianes. La constante simbólica #define TWO_PI 6.28 sería de gran valor para el progtwigdor.

Jonathan hizo un buen punto por qué querría usar constantes simbólicas en C (y en cualquier otro lenguaje de progtwigción, BTW).

Sintácticamente, en C esto es diferente de C ++ y muchos otros lenguajes porque es muy restrictivo en cuanto a cómo se puede declarar tal constante simbólica. Las llamadas variables calificadas const no tienen en cuenta esto como lo harían en C ++.

  • Puede usar una macro que esté definida para cualquier expresión constante: constantes de punto flotante o entero, expresiones de dirección de variables estáticas y algunas formas de expresión que forme de ellas. Estos solo se tratan en la fase de preprocesamiento del comstackdor y tendrías que tener cuidado cuando uses expresiones complicadas en ellos.
  • Puede declarar expresiones constantes de enteros en forma de constantes de enumeración de enteros , como en el enum color { red = 0xFF00, green = 0x00FF00, blue = 0x0000FF }; . Solo tienen un uso restringido, ya que están arreglados para tener el tipo int . Así que no cubrirías todos los rangos de valores que podrías desear con ellos.
  • También puede ver constantes de caracteres enteros como 'a' o L'\x4567' como constantes simbólicas predefinidas, si lo desea. Traducen un concepto abstracto (el valor del carácter “a”) a la encoding de la plataforma ejecutora (ASCII, EBDIC, lo que sea).

Jonathan proporciona un excelente ejemplo del usuario de constantes simbólicas.

Es posible que el progtwig utilizado en la pregunta no sea el mejor para responder a esta pregunta, sin embargo, dado que las constantes simbólicas del progtwig pueden tener más sentido en el siguiente caso:

 #include  #define FAHRENHEIT_TO_CELSIUS_CONVERSION_RATIO 5.0 / 9.0 #define FAHRENHEIT_TO_CELSIUS_ZERO_OFFSET 32.0 #define FAHRENHEIT_CELSIUS_COMMON_VALUE -40.0 #define UPPER 300.0 #define STEP 20.0 main() { float fahr, celsius; printf("%s\t %s\n", "Fahrenheit", "Celsius"); fahr = FAHRENHEIT_CELSIUS_COMMON_VALUE; while (fahr <= UPPER) { celsius = (fahr - FAHRENHEIT_TO_CELSIUS_ZERO_OFFSET) * (FAHRENHEIT_TO_CELSIUS_CONVERSION_RATIO); printf("%3.0f\t\t %3.2f\n", fahr, celsius); fahr = fahr + STEP; } } 

Posiblemente esto hace que sea más fácil entender por qué las constantes simbólicas podrían ser útiles.

El progtwig incluye stdio.h , un archivo de inclusión bastante común, veamos algunas de las constantes simbólicas definidas en stdlib.h . Esta versión de stdio.h es de Xcode.

 #define BUFSIZ 1024 /* size of buffer used by setbuf */ #define EOF (-1) #define stdin __stdinp #define stdout __stdoutp #define stderr __stderrp 

Veamos también dos constantes simbólicas definidas en stdlib.h .

 #define EXIT_FAILURE 1 #define EXIT_SUCCESS 0 

Estos valores pueden variar de un sistema a otro, pero su uso hace que la progtwigción en C sea mucho más fácil y portátil. Se sabe que las constantes simbólicas para stdin , stdout y stderr cambian en varias implementaciones de sistemas operativos.

El uso de BUFSIZ para definir matrices de caracteres para los buffers de entrada de C generalmente tiene mucho sentido. El uso de EXIT_FAILURE y EXIT_SUCCESS hace que el código sea mucho más legible, y no tengo que recordar si 0 es un fracaso o un éxito. ¿Alguien preferiría (-1) sobre EOF?

El uso de una constante simbólica para definir el tamaño de los arreglos hace que sea mucho más fácil cambiar el código en un solo lugar en lugar de tener que buscar por todas partes un número específico incrustado en el código.