`three = 1` in the Linux sourcecode (2014)
github.com
github.com
One example of this was that all numeric values used in a program must be factored out as symbolic constants. The reason for doing this is obvious, but it failed to account for the fact that some numbers have intrinsic meaning that should allow their use directly. But to be compliant with the standard, our C code had this boilerplate up near the top:
#define zero 0
#define one 1
This, of course, only served to make every page of code harder to read. And it didn't really even solve the problem it was meant to. We once found in some source code: #define thirteen 13
which of course did nothing to show us why that particular value was relevant.I get why you would prefer to name constants for their use, like `numIterations=3` or whatever, but renaming every integer seems senseless.
// this byte is unused and always 0
#define UNUSED_CONSTANT_FIELD_VALUE 0
bytes = {HEADER, UNUSED_CONSTANT_FIELD_VALUE, [...]}
the range of the type is the non-negative integers up to (2^32)-1
semantically, the value cannot be null
when the value is 0, the code treats it as a special case
null is treated as 0
Which, of course, is wrong. They're not the same. Same bastardization happened to Hungarian Notation, IIRC. It was meant to add some information to variables, like `iterN` for a counter, or `lenX` for a length, but someone somewhere decided that you were meant to prefix with the most primitive of type info, so you get stupid shit like `intN` and `intX`, and aren't much more informed. (Especially with an IDE showing the type info)
When the integer itself is self-explainatory but you can't just assign the integer as a name.
For example:
constexpr int 42 = 42; // compiler error
So instead: constexpr int fourtytwo = 42; // yay!
However the obviousness of what 42 means is debatable. int universe = fourtytwo; // why?
int sum = fourtytwo; // sum... of what?
int magic = fourtytwo / 7;Fortytwo: a defensive fortification with a nickname. See also Boaty McBoatface.
If you look later in the code that's exactly what is happening here.
enum RecordType {TypeOne(1),TypeTwo(2),TypeThree(3); RecordType(int dbValue) ...}
As it happens, I know an end user of this particular beast, so I show her some record IDs of each type and ask in what way they differ. She looks a few seconds, then says: 'This is clearly a type one record, the next is a type two, and here you have a type three'. After a little back an forth, it turns out even our internal course for new personnel talks about 'records' with different rules to follow for 'type one/two/three'. The end users have no other word to describe these entities than 'records' each with its own numbered type, and think about it as completely natural. The application is based on an older COBOL application, and the terminology just stuck around.Clearly, the application is well-written: The enum uses the terminology used by the business. Another of my flabbers just got ghasted.
Edit: scratch that neural network. They just made one neuron so far.
You don't get screwed because you called something apple to start with, but it now represents a race car
https://en.wikipedia.org/wiki/Type_1
https://en.wikipedia.org/wiki/Type_2
https://en.wikipedia.org/wiki/Type_III
https://en.wikipedia.org/wiki/Class_1
https://en.wikipedia.org/wiki/Class_2
https://en.wikipedia.org/wiki/Class_3
etc.
This was pretty common on mainframes, before db2 and other SQL databases
#define I 1
#define II 2
#define III 3
...
#define XIII 13
There is no zero in roman numerals, but you can use NULL for extra fun! (yes, I am a bad person) iv = secrets.randbits(IV)
plaintext = "Text for encryption"
aes = pyaes.AESModeOfOperationCTR(key, pyaes.Counter(iv))
ciphertext = aes.encrypt(plaintext)
print('Encrypted:', binascii.hexlify(ciphertext)) #define X_MINUS_V_MINUS_IV_MINUS_I 0These principles hold when the label given a clear and concise name--which is an art.
#define 0 0 > #define 0 0
That actually works on some implementations (the preprocessor grabs a token without checking that it's a identifier). I've seen: #define $ SOME_LINE_NOISE
#define && HACKY_GARBAGE
x = &&(y $ z);
as well. #define zero 0
is just bad programming, barring some philosophical code which cares about "zero-ness". Names need to reflect what they represent, not their precise values. Are we looking up the first index? Then how about #define first_index 0
? Are we summing up values which are coerced to zero when empty? Then how about #define empty_value_integer 0
or #define additive_identity 0
?Yes, that means there might be more than one variable with a value of zero, and that's completely fine since we now have several megabytes available for both source code and for compilation.
To be clear, I'm not endorsing the rule, just saying that there are more productive ways of dealing with it.
But that is the intrinsic meaning OP was talking about. Plenty of mathmatics deals with 0 or 1 as important constants. If you have a function that mods by a parameter, you need error handling for zero. Calling it value_that_causes_undefined_behaviour isn't going to improve code clarity. Beyond that;
for (i = 0; i < IMPORTANT_QUANTITY; i++)
, or some variation thereof, is very idiomatic C.
705 /*
706 * Iterate through the groups which hold BACKUP superblock/GDT copies in an
707 * ext4 filesystem. The counters should be initialized to 1, 5, and 7 before
708 * calling this for the first time. In a sparse filesystem it will be the
709 * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...
710 * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...
711 */
712 static unsigned ext4_list_backups(struct super_block *sb, unsigned *three,
713 unsigned *five, unsigned *seven)The problem is that it is extremely clear in a way that makes you confidently think something totally wrong.
Then you could put the relevant comment about the weirdness of "three = 1" there, and callers wouldn't have to remember the magic sequence.
https://github.com/torvalds/linux/blob/master/fs/ext4/resize...
Upon a quick code review, these lines look buggy:
unsigned three = 1;
unsigned five = 5;
unsigned seven = 7;
Without digging deeper, the reader of this code thinks "The first line surely must be a bug, right???" int three = 3;
is insane.I would never ascribe to previous programmer (unless he just learned what variables are) the intention of creating variable (not even constant, which wouldn't make it any better) named 'three' and assigning actual value 3 to it.
When it contains other values it is still subpar variable naming.
unsigned three = 1;
is accompanied by unsigned five = 5;
unsigned seven = 7;
what would be the sane conclusion?Then maybe leave a comment right after `= 1;` why this variable shouldn't be initialized with 3 that would clear up confusion for you if it was there.
But if it were my job to touch these lines of code, I would want to leave them better than I found them.
This is really a question about two processes - the one where people look at, read, edit and discuss the code, and the extent to which this is discoverable by them. And the one where people run the code and expect certain things to work and would notice if they didn't work, or didn't work correctly.
This fails both of those checks. This file has been edited 8 times in the last year, by 5 different people. It's not so long that several of them wouldn't have read through the whole thing. This bug, if it was a bug, is unlikely to have persisted for 6 years without being noticed. It's not obfuscated or deeply technical in any way.
Secondly, this is code which is fairly critical to millions of systems. Ext4s is in literally hundreds of millions of devices. It's used in data centers in contexts where people are likely to follow up on any unusual behavior by their filesystem. The code in question is likely to be very well exercised in tests. It's likely to be extremely well exercised by people doing research stress testing/comparison testing of filesystem code. It is undoubtedly very well exercised by real-world deployments.
The bar to assume that you know something the person who wrote the code didn't, as opposed to the other way around, should be very high here. Even taking all the benefit of the doubt (which you should always do, never assume that stuff 'probably works' without evidence) it's not hard to dismiss this.
In this case, I do not agree that this could have gone unnoticed for a long time, even only looking at the behavior of the running system. When multiplied by the number of installations and how much filesystems are used, it is run a lot. It's also the kind of thing people investigate when it happens in certain settings - we lost a block and our whole filesystem was corrupted. And the kind of thing people test for.
Good reflex. In my first job out of school (big research lab), we would occasionally find odd bugs in the code base (as would be expected...bugs happen). The head of the project would often wonder, “how could the system ever have functioned with this bug? Someone must have deliberately tampered with the source to discredit us.”
The first few times I thought he was joking, but no: he seriously believed this and even tried to investigate.
This was years before source control, which would have answered the question.
Edit: fix iPad-caused typos
We were called in to help them because their management (and users) were aware they were underwater with the amount of bug fixes, live production data changes they made to keep things working, and impossibility of adding anything new to the system without tipping something else over.
It seems the attitude that "there's no chance we have any bugs" is pretty prevalent anywhere I've ever been, along with the usual mix of "how did this ever work" which I still always find pretty shocking to run across.
It’s a local variable so it seems that there would be little impact in renaming it (and the other two). But if the local (kernel in this case) coding conventions discourage variable renaming, a brief comment referring to the later comment would be worthwhile.
Over commenting is a problem, but this would a good use case if renaming is not appropriate.
unsigned three = 1;
To me, that is a 20 foot tall neon orange flashing Chesterton's fence. I think, "surely there is a very strong reason for this." Maybe not a good reason, but definitely a compelling one."Three is the number of the counting, and the number thou shalt count to is three"
Powers of three, five, and seven sounds a little bit like Totvald's very own little FizzBuzz game hidden in ext4's file-system source code.
What's that?
- if the data block id is a multiple of 3, replace its contents with 0x03
- if the data block id is a multiple of 5, replace its contents with 0x05
- if the data block id is a multiple of both 3 and 5, replace its contents with 0x15
It's not a Chesterton's fence, though. The whole point of Chesterton's fence is that whoever put up the fence that you want to take down couldn't be bothered to add signage explaining why the fence was present, thus either it isn't important to keep the fence up long term, or they're a idiot, in which case there's no reason to belive they were correct in deciding that having a fence there was useful in the first place.
With this fence, if you walk a few meters to the side (read, "select 'ext4_list_backups' and hit ^F and ^G a few times); et viola, there's your signs.
If only there was a system for fixing the code l yourself instead of having to post about it to a widely read tech site.
*ANY* time you use a non-obvious constant (like start of a loop that isn't 0), good practice to comment immediately at the source, or pull it into a well-named variable/macro.
Don't agree? Spend 35 years programming and you'll see this pop up again and again and again and again... SO much time wasted figuring out magic #s.
https://github.com/torvalds/linux/blob/d158fc7f36a25e19791d2...
Intended for fixed point arithmetic where 1 actually means 1/256.
But then, you should probably do something such as "#define CLOCK_DIVISOR_3 0x1" instead of simply "int three = 1".
_3 = 1;
It's shorter. I know, _ is probably reserved, so maybe: x3 = 1; // x is not multiply
There. I fixed it for you. #define CR_LF "\n""Write software like the next person to work on it is a psychopath and they know where you live"