符号常数的重点是什么?

Pet*_*ter 13 c

我无法理解C中符号常量的含义,我确信它们有一个原因,但我似乎无法理解为什么你不会只使用变量.

#include <stdio.h>

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;
    }

}
Run Code Online (Sandbox Code Playgroud)

比.

#include <stdio.h>

#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;
    }

}
Run Code Online (Sandbox Code Playgroud)

Ted*_*opp 17

(pre)编译器知道符号常量不会改变.它在编译时替换常量的值.如果"常量"在变量中,通常无法确定变量永远不会改变值.因此,编译后的代码必须从分配给变量的内存中读取值,这会使程序稍微变慢.

在C++中,您可以声明一个变量const,它告诉编译器几乎相同的东西.这就是为什么符号常量在C++中不受欢迎的原因.

  • 那么C中的#define就像C++中的const一样?我是否正确地认为它也像Java中的final? (3认同)
  • `#define foo 3`实际上会在您的代码中使用`foo`的任何实例,然后将其替换为`3`。然后,它将把经过预处理的代码传递给编译器。因此,它比Java中的“ final”更有力,因为编译器从未真正看到符号,而是看到了值。 (2认同)

Jon*_*ler 7

为什么命名常数是有益的一个很好的例子来自Kernighan和Pike 的优秀书籍编程实践.

§1.5幻数

[...]这个摘录自一个程序,用于在24×80光标寻址终端上打印字母频率的直方图,由于一系列幻数,它们不必要地不透明:

...
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);
Run Code Online (Sandbox Code Playgroud)

该代码包括数字20,21,22,23和27.其中明显相关......或者是它们?事实上,这个程序只有三个关键数字:24,屏幕上的行数; 80,列数; 26,字母表中的字母数.但是这些都没有出现在代码中,这使得数字变得更加神奇.

通过在计算中给主要数字命名,我们可以使代码更容易理解.例如,我们发现数字3来自(80 - 1)/ 26,并且应该有26个条目,而不是27个(一个可以由1个索引的屏幕坐标引起的一个一个错误).进行其他几个简化,结果如下:

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);
Run Code Online (Sandbox Code Playgroud)

现在它更清楚主循环的作用; 它是从0到NLET的惯用循环,表明循环遍及数据元素.此外,调用draw更容易理解,因为像MAXROW和MINCOL这样的单词提醒我们参数的顺序.最重要的是,现在可以将程序调整为另一种尺寸的显示器或不同的数据.这些数字是神秘的,代码也是如此.

修改后的代码实际上并没有使用MINROW,这很有趣; 人们想知道剩下的1中哪一个应该是MINROW.

  • +1是一个很好的例子,说明了为什么应该命名常量而不将其分散在代码中的原因。但是,通过声明具有这些值的变量可以获得相同的好处。从这个意义上说,符号常量(或枚举常量)相对于命名变量的好处尚不清楚。(实际上,我不喜欢在这种情况下使用[enum],这种情况下没有枚举清晰的项目类别(除了“有用的常量”)。) (2认同)