Using unexplained numbers instead of named constants in code makes your intent unclear, your code hard to debug/understand, and also difficult to adapt and extend later. Static analysis tools like Understand can find them, but take care fixing them because… you never know what is behind their spell.
What are these numbers used for:
0, 1, 64, 64, 3.14159265358979323846, 3.14159 and 7
Think about it for a minute and then read on.
You, as a smart reader of software maintenance tool blogs, know what these numbers might be doing in your code. But oh… 3.14159265358979323846 – that’s not actually pi, and it’s also not equal to 3.14159, and 7 – it’s lucky I’ve heard but I feel pretty unlucky seeing it in code hanging there unexplained. It’s probably the number of days in the week, yeah – that’s it.
I’ll explain them:
const int ArrayStart = 0;
#define YoungestBabyAllowedonRide 1;
const int WordSize = 64
const int AgeOfYoungestRecipientAllowed = 64
#define AverageLimitHumanShortTermMemory 7 // ooops… not days of week
When you use numbers in unexplained and in possibly contrary ways, you are making “magic numbers”. Check out some other words for magic:
Using fancy word substitution persuasion techniques we could call them “deceptive numbers” or “tricky numbers”, and maybe that would be better. They’ve been called “magic numbers” since Grace Hopper’s COBOL days, so let’s not get too stuck on what to call them and instead focus on how to find out if your team is using them.
It’s simple… just turn on the “Magic Numbers” check in Understand’s CodeCheck tool, and it will find them automagically.
Here I’ve quickly checked 1081 files of a version of the Linux kernel that we include as an Understand example project. It took about 20 seconds and returned JUST 3222 uses of magic numbers. )-:
Most of them are bit operation hex numbers:
Am I allowed to say that the Linux kernel, which is amazing, awesome, has changed the world, and did it for free might have some understanding/maintenance challenges? Of course, I can!
Hex numbers might be considered special because if you are in the club of people that use them for embedded work, as I once was in a misspent youth, you read them like other people read words.
So if you are in the club, you might be okay, but for those out of the club, or who used to be in it and know enough still to be dangerous, might need some guidance.
But… what about this:
where two different hex values have the same cool, useful comment!
I’m not dumping on this code – it’s powering the world. I am suggesting that it’s gonna be tough to understand, read, extend, and maintain especially for someone who didn’t write it originally, or who wrote it but so long ago they might as well have not written it.
This use of 2, I can deduce pretty well, and I’m not sure that a named constant would be any clearer here:
And then I examine this and have no idea what’s going down:
Would I change this to remove magic numbers? Nope. Not unless I was rewriting it and wanted to write it clearer. I’d hope to have the time to spend a good while sorting out what it was doing, what 14 means, what 15 meant, and why in one case for the same function argument a hex number was used.
It’s all probably clear to habitual users of outb, which is a common function in the Linux kernel that is used to do very low-level I/O port communications. Based on this warning in the manual page for outb, I shouldn’t mess with this – at all – or at least ’til I understand it very well:
Anyway, I don’t know what 14, 15, or oxff, ports are for. This would be clearer (I’m using contrived names):
#define PrinterPortA 14
#define PrinterPortB 15
#define BitBucket 0xff
The Opposite of Magic is Science
You, as a professional computer scientist, cranking out well-thought-out and very maintainable code, are not a magician. You explain your current self to your future self with well-named constants, functions, variables, and strong architectures that are built to be understood and extended.
You are going to hold your team to that same standard and verify it as well with automatic tools. Because… well, you are a professional thinking about today but knowing that tomorrow (or 8 years from now) is coming fast.
What should you do with established code? You may be better off leaving confusion alone until that code is refactored, or replaced. Or as a compromise only clean up when you are working in that code and already invested in the knowledge necessary to correctly clean up confusions.
In any event, to replace magic numbers with constants or #defines you MUST be sure you really understand and have taken the “magic” out of those numbers. See my first examples where 64 had a common and an atypical use.
Magic numbers are little deceptions you leave to your future self, or whoever replaced you. Be kind to the future! Explain yourself with named constants or defines. And to help you keep yourself and your colleagues out of future trouble, Understand can check for magic numbers and a few hundred other sketchy coding practices every time you hit save, checkin, or somewhere (early) in your GitOps/DevOps pipeline.
Questions, comments, suggestions? Shoot me an e-mail. [email protected]