Home / Articles / Magic Numbers
Magic Numbers
What Are Magic Numbers?
Magic numbers are literal numeric values used directly in a program — appearing in expressions, array sizes, and as constants. Examples:
char buf[1024]; /* 1K buffer size */
int retransmissionTimer = 100; /* Re-transmit after 100 ms */
The literals 1024 and 100 are magic numbers. The literals 0 and 1 are usually excluded from this category.
What's Wrong with Magic Numbers?
Magic numbers are confusing because a number placed in the middle of an expression gives little indication of its meaning or purpose. Consider this snippet from a character-mode text editor designed for 25 rows and 80 columns (with borders reserving the first/last row and column):
void moveLeft()
{
if (curCol == 79) {
curCol = 2;
if (curRow == 24) {
curRow = 3;
shiftScreenUp(1);
} else {
curRow++;
}
} else {
curCol++;
}
displayCursor();
}
None of the layout constraints are conveyed by the literals 79, 2, 24, or 3. To a new reader, this looks like it might contain bugs.
Avoid Magic Numbers Where Possible
Some magic numbers can be avoided entirely by using language features. For example, checking whether a character is alphabetic:
if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
can be replaced with:
if (isalpha(ch))
Similarly, use NULL instead of 0 for null pointers, and use sizeof instead of hardcoding data type sizes.
Give Names to Magic Numbers
When literals cannot be avoided, give them names that convey their purpose. Compare:
if (timer.curMSecs > (timer.info.StartMSecs + 100))
deleteTimer(timer);
with:
#define maxRetransmitTime 100
...
if (timer.curMSecs > (timer.info.StartMSecs + maxRetransmitTime))
deleteTimer(timer);
Benefits of naming literals:
- Clarity — the name explains the meaning without requiring a comment.
- Maintainability — changing the value requires a single edit instead of hunting through the entire codebase.
- Fewer errors — no risk of forgetting to update one of many scattered occurrences.
Use Macros
Macros are the most common way to name constants in C. The text-editor example rewritten with macros:
#define MAXROW 24 /* Last row for border */
#define MINROW 3 /* First row for menu and second row for border */
#define MAXCOL 79 /* Last column for border */
#define MINCOL 2 /* First column for border */
void moveLeft()
{
if (curCol == MAXCOL) {
curCol = MINCOL;
if (curRow == MAXROW) {
curRow = MINROW;
shiftScreenUp(1);
} else {
curRow++;
}
} else {
curCol++;
}
displayCursor();
}
Use Enumerations
For large applications with many macros, enumerations can logically group related literals:
typedef enum screenSize {
MAXROW = 24, /* Last row for border */
MINROW = 3, /* First row for menu and second row for border */
MAXCOL = 79, /* Last column for border */
MINCOL = 2 /* First column for border */
} screenSize;
Drawbacks: enumerations in C lack type safety and can only hold integer values.
Use Constants
Constants are type-safe and support all data types:
const int MAXROW = 24; /* Last row for border */
const int MINROW = 3; /* First row for menu and second row for border */
const int MAXCOL = 79; /* Last column for border */
const int MINCOL = 2; /* First column for border */
Variables (rather than constants) should be used only when there is a strong reason to do so, such as when the value needs to be passed as a parameter to achieve generality:
void moveLeft(int MAXROW, int MAXCOL, int MINROW, int MINCOL)
{
if (curCol == MAXCOL) {
curCol = MINCOL;
...
}
...
}