It is common for comparison functions to return 0 on "equals", so that they can also return a negative number for "less than" and a positive number for "greater than". strcmp() and memcmp() work like this.
It is, however, idiomatic for zero to be false and nonzero to be true, because this is how the C flow control and logical boolean operators work. So it might be that the return values chosen for this function are fine, but it is the function's name that is in error (it should really just be called compare() or similar).
It is common for comparison functions to return 0 on "equals", so that they can also return a negative number for "less than" and a positive number for "greater than". strcmp() and memcmp() work like this.
It is, however, idiomatic for zero to be false and nonzero to be true, because this is how the C flow control and logical boolean operators work. So it might be that the return values chosen for this function are fine, but it is the function's name that is in error (it should really just be called compare() or similar).
This upside-down-world is common with process error returns. The shell variable $? reports the return value of the previous program to execute from the shell, so it is easy to tell if a program succeeds or fails:
$ false ; echo
true ; echo $?
0
This was chosen because there is a single case where a program succeeds but there could be dozens of reasons why a program fails -- by allowing there to be many different failure error codes, a program can determine why another program failed without having to parse output.
A concrete example is the aa-status program supplied with the AppArmor mandatory access control tool:
Upon exiting, aa-status will set its return value to the
following values:
0 if apparmor is enabled and policy is loaded.
1 if apparmor is not enabled/loaded.
2 if apparmor is enabled but no policy is loaded.
3 if the apparmor control files aren't available under
/sys/kernel/security/.
4 if the user running the script doesn't have enough
privileges to read the apparmor control files.
(I'm sure there are more widely-spread programs with this behavior, but I know this one well. :)
Videos
I am getting confused. In the "week 2, continued" lecture, prof. Dave said that "returning 0 indicates all is well effectively leaving us with 4 billion things that coould go wrong". So I concluded from here that 0 is True to C. But in the book "Absolute beginner to C" by Greg Perry in chapter 19, it is written "False is 0 to C". If 0 is False, then why does the main function returns 0 by default? Please help me settle this once and for all.
I think you're mixing up two things here: C does not have a boolean data type, but instead uses integers.
The if-statement treats values which are not 0 as true and 0 as false.
Due to this you can write:
int test = 1; // 1 means true
if(test == 1) {}
As:
if(test) {} // 1 evaluates to true anyways, so nonned to check for truth explicitly
I am not familiar with the content Prof. Dave presented, but he most likely meant the return value of a programme run on the command line. And there the convention is, that any non-zero return code means an error (with the number giving you an indication of the error; the error number or errno).
TL;DR: C uses ints for booelan values (true or false) and if-statements treat 0 as false and everything else as true. The return value of a programme run on the command line returns 0 if it ran without any error, otherwise it returns a number which is an indication of the type of error.
See The C FAQ for more information.
Hope that helps!
Another way to think about it is that main() returns an error code. Zero means "no error".
You can actually see the value that main returns. If your program is named "test", use this command to run it:
./test ; echo $?
and the shell will print out the value your main returned.
How did 1 and 0 come to be associated with True and False?
Nowadays in mathematics and computer science it is common for the number 1 to be associated with truth, and 0 with falsehood. This strikes me as a non-obvious, and so I'm wondering how this came to be taken for granted? Is this actually universal or are there other standards I'm not aware of? Is there a good reason for this association or is it mere convention?
0 is false because they’re both zero elements in common semirings. Even though they are distinct data types, it makes intuitive sense to convert between them because they belong to isomorphic algebraic structures.
0is the identity for addition and zero for multiplication. This is true for integers and rationals, but not IEEE-754 floating-point numbers:0.0 * NaN = NaNand0.0 * Infinity = NaN.falseis the identity for Boolean xor (⊻) and zero for Boolean and (∧). If Booleans are represented as {0, 1}—the set of integers modulo 2—you can think of ⊻ as addition without carry and ∧ as multiplication.""and[]are identity for concatenation, but there are several operations for which they make sense as zero. Repetition is one, but repetition and concatenation do not distribute, so these operations don’t form a semiring.
Such implicit conversions are helpful in small programs, but in the large can make programs more difficult to reason about. Just one of the many tradeoffs in language design.
Because the math works.
FALSE OR TRUE is TRUE, because 0 | 1 is 1.
... insert many other examples here.
Traditionally, C programs have conditions like
if (someFunctionReturningANumber())
rather than
if (someFunctionReturningANumber() != 0)
because the concept of zero being equivalent to false is well-understood.
I'm curious about the reasoning behind this logic:
>>> bool(1) True >>> bool(0) False >>> bool(-1) True >>> 1 == True True >>> 0 == True False >>> -1 == True False >>> -1 == False False
Any number can be converted to a bool. Non-zero evaluates to True while zero evaluates to False. Similarly, positive numbers are equivalent to True and zero is equivalent to False. This all makes intuitive sense. What confuses is me is the last two lines. Apparently, negative numbers have the boolean conversion defined but not the equivalence. Why is this?
I think your question is a false dichotomy, at least in a lot of cases; modern high-level languages1 tend not to allow conversion between booleans and integer types, so this should be considered an implementation detail and it doesn't matter how a specific implementation happens to do it. Taking this further, "register" is an implementation detail as well.
1. C and C++ are not "modern" in this context!
Because the actual register cannot store a bit, and the register has no type,
You are probably thinking about a general purpose register. However, CPUs typically also have flags to store certain results, and those flags are typically one bit containing a true/false value. Many CPU architectures have something like a CMP (compare) instruction, which can compare two values for equality, and which stores the result in one of those flags. Some CPUs allow those flags to be accessed directly, some only indirecly (like with JE/JNE (jump if equal/not equal) instructions), and some present a collection of flags as a single status register. Still, you can think of the flag as a single boolean register.
It's also not right to say that register don't have a type. You typically have different types of registers, some for integers, some for floating points. In hardware, there is typically a set of registers that are all 32 or 64 bits, but the instruction set might expose logical registers that are 8 or 16 bits wide. At the hardware level there are also no ones and zeroes, it's some amount of charge or voltage, or the absence of it, and it doesn't even have to be that a high voltage means a 1.
Mathematics usually uses 0 to represent
falseand 1 to representtrue
I think it's computer science that does that, not mathematics.
Some mathematicians believe that 0 and -1 should be used from the perspective of truth tables
Some operations on booleans have an equivalent in operations on integers. For example, if you use 0 for false and 1 for true, then a logical AND is equivalent to multiplication of those integer values. You can then also see that this no longer works if you use 0 for true and 1 for false.
When working with registers of a fixed width, you are working in a ring with modular arithmetic, and usually signed numbers are stored in two's complement. That means -1 is equivalent to all bits in that register being 1. This can make some things easier, for example in the following piece of C code:
bool a = …;
int x = 123;
int y = a ? x : 0;
The compiler could then replace the conditional expression with a bitwise AND operation:
int y = x & (int)a;
Which could result in much faster assembly being generated. However, since using just 1 and 0 for true and false is so common, most CPUs provide instructions to handle that as fast as storing booleans as -1 and 0 would.
Also, note that in C and languages that use the same ABI, 0 is false but when converting an int or other similar type to bool, any non-zero value is considered true. And while the ABI says that a boolean is stored or passed as a parameter, the value should be exactly 1 or 0, the compiler is allowed to do anything it wants behind the scenes as long as the observable end result is the same as what the language specification says. So it might actually store true as a non-zero value other than 1 if that is more expedient and nothing would break.
The solution adopted by the shell is
0to representtrueand other values to representfalse
Not exactly; the result of a command is not a boolean, it is an error code, where 0 means no error. The same goes for errno.
Mainstream FFIs all default 0 to
false
Because C defined false to be zero, and most FFIs use C's ABI.
CPU initialization is all 0 initialization,
Not necessarily, and especially not the memory. A BIOS or operating system's first task is usually to zero all memory. The CPU has defined reset values for some of the registers, and some of those reset values might not be zero. When a program is started by the operating system, it's also not the case that all registers are zero.
What is true is that often the CPU provides a fast way to set a register to zero, either by having specific instructions for that or by having a special register that is always zero, and that you can copy into other registers. And even if there is nothing explicit, you can almost always XOR a register with itself.