Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>One good example of why named constants are beneficial comes from the excellent book <a href="http://plan9.bell-labs.com/cm/cs/tpop/" rel="noreferrer">The Practice of Programming</a> by Kernighan and Pike.</p> <blockquote> <h3>§1.5 Magic Numbers</h3> <p>[...] This excerpt from a program to print a histogram of letter frequencies on a 24 by 80 cursor-addressed terminal is needlessly opaque because of a host of magic numbers:</p> <pre><code>... fac = lim / 20; if (fac &lt; 1) fac = 1; for (i = 0, col = 0; i &lt; 27; i++, j++) { col += 3; k = 21 - (let[i] / fac); star = (let[i] == 0) ? ' ' : '*'; for (j = k; j &lt; 22; j++) draw(j, col, star); } draw(23, 2, ' '); for (i = 'A'; i &lt;= 'Z'; i++) printf("%c ", i); </code></pre> <p>The code includes, among others, the numbers 20, 21, 22, 23, and 27. They're clearly related...or are they? In fact, there are only three numbers critical to this program: 24, the number of rows on the screen; 80, the number of columns; and 26, the number of letters in the alphabet. But none of these appears in the code, which makes the numbers that do even more magical.</p> <p>By giving names to the principal numbers in the calculation, we can make the code easier to follow. We discover, for instance, that the number 3 comes from (80 - 1)/26 and that let should have 26 entries, not 27 (an off-by-one error perhaps caused by 1-indexed screen coordinates). Making a couple of other simplifications, this is the result:</p> <pre><code>enum { MINROW = 1, /* top row */ MINCOL = 1, /* left edge */ MAXROW = 24, /* bottom edge (&lt;=) */ MAXCOL = 80, /* right edge (&lt;=) */ 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 &lt; 1) fac = 1; for (i = 0; i &lt; NLET; i++) { if (let[i] == 0) continue; for (j = HEIGHT - let[i]/fac; j &lt; HEIGHT; j++) draw(j+1 + LABELROW, (i+1)*WIDTH, '*'); } draw(MAXROW-1, MINCOL+1, ' '); for (i = 'A'; i &lt;= 'Z'; i++) printf("%c ", i); </code></pre> <p>Now it's clearer what the main loop does; it's an idiomatic loop from 0 to NLET, indicating that the loop is over the elements of the data. Also the calls to <code>draw</code> are easier to understand because words like MAXROW and MINCOL remind us of the order of arguments. Most important, it's now feasible to adapt the program to another size of display or different data. The numbers are demystified and so is the code.</p> </blockquote> <p>The revised code doesn't actually use MINROW, which is interesting; one wonders which of the residual 1's should be MINROW.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. CO+1 for an excellent example of why one should name constants and not scatter them through the code. However, the same benefit can be obtained by declaring variables with those values. The benefit of symbolic constants (or enum constants) over named variables isn't so clear from this. (Actually, I don't like using `enum` in situations like this where there is no clear class of items being enumerated [aside from "useful constants"].)
      singulars
    2. CO@Ted: It depends on the context. The example is for a fixed 24x80 screen; constants are appropriate. If you take into account current window size, named variables appropriately initialized are clearly better. The OP's code would be most clarified by using a `for (fahr = LOWER; fahr <= UPPER; fahr += STEP)` loop, where I'm neutral about the relative merits of named constants vs pure numbers. In the example, the values for lower, upper and step are more nearly arbitrary. If the values were `FREEZING_POINT`, `BOILING_POINT` and `(BOILING_POINT - FREEZING_POINT) / 30`, then names are better.
      singulars
    3. COI agree completely about using names instead of the raw numbers. I just don't like using `enum` instead of `#define` in this particular example. The benefits of using constants (`#define` and/or `enum`) instead of variables become much clearer in examples where the compiler can do things like eliminate a series of calculations in an inner loop because it recognizes an expression a compile-time constant. This example doesn't really demonstrate that kind of benefit. (I'm surprised nobody's yet mentioned how useful symbolic constants are for switch statements. Now I have! :))
      singulars
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload