符号常数的重点是什么?

时间:2011-02-21 03:36:59

标签: 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;
    }

}

Vs以上。

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

}

5 个答案:

答案 0 :(得分:16)

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

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

但请注意,在C(与C ++相对)中,const int变量不是常量表达式。因此,尝试做这样的事情:

const int a = 5;
int b[a] = {1, 2, 3, 4, 5};

将在C ++中工作,但会在C中遇到编译错误(假设b应该是一个静态绑定数组)。

答案 1 :(得分:7)

为什么命名常数是有益的一个很好的例子来自Kernighan和Pike(1999)的优秀书The Practice of Programming

  

§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);
     

代码中包括数字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);
     

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

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

答案 2 :(得分:2)

变量的作用域是它们声明的结构。当然你可以使用变量而不是符号常量,但这可能需要很多工作。考虑一个经常使用弧度的应用程序。符号常量#define TWO_PI 6.28对程序员来说具有很高的价值。

答案 3 :(得分:2)

Jonathan在为什么中提出了一个很好的观点,你想在C语言中使用符号常量(以及任何其他编程语言,BTW)。

在语法上,在C中,这与C ++和许多其他语言不同,因为它对如何的限制很大,你可以声明这样的符号常量。所谓的const限定变量并不像在C ++中那样解释这一点。

  • 您可以使用定义为任何常量表达式的宏:整数或浮点常量,静态变量的地址表达式,以及从它们形成的某些表达式。这些仅由编译器的预处理阶段处理,当您在其中使用复杂表达式时必须小心。
  • Yo可以以整数枚举常量的形式声明整数常量表达式,例如在enum color { red = 0xFF00, green = 0x00FF00, blue = 0x0000FF };中。它们仅受限制使用,因为它们被固定为类型int。因此,您不会涵盖您可能想要的所有值范围。
  • 如果您愿意,您还可以看到整数字符常量,如'a'L'\x4567'作为预定义的符号常量。他们将抽象概念(字符值“a”)转换为执行平台的编码(ASCII,EBDIC,等等)。

答案 4 :(得分:0)

Jonathan提供了符号常量用户的一个很好的例子。

问题中使用的程序可能不是回答此问题的最佳程序。但是,给定程序,在下列情况下,符号常量可能更有意义:

#include <stdio.h>

#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

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

这可能更容易理解为什么符号常量可能有用。

该程序包括stdio.h,一个相当常见的包含文件。让我们看一下stdlib.h中定义的一些符号常量。此版本的stdio.h来自Xcode。

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

让我们看看stdlib.h中定义的两个符号常量。

#define EXIT_FAILURE    1
#define EXIT_SUCCESS    0

这些值可能因系统而异,但使用它们会使C编程变得更加容易和便携。已知stdinstdoutstderr的符号常量会在各种操作系统实现中发生变化。

使用BUFSIZ为C输入缓冲区定义字符数组通常很有意义。 使用EXIT_FAILURE和EXIT_SUCCESS使代码更具可读性,我不必记住0是失败还是成功。 有人会更喜欢(-1)而不是EOF吗?

使用符号常量来定义数组的大小可以更容易地在一个地方更改代码,而不必遍历搜索代码中嵌入的特定数字。