They aren't functions, technically, though they behave somewhat like functions from the programmer's point of view. They are what are called
macros, and they are called just like functions are, but the compiler (or rather, the pre-processor) treats them differently.
To understand this, you need to know that C actually consist of
two languages: the pre-processor language, which as the name suggests is processed before the compilation, and the C language proper. All the statements which begin with a hash mark as the first character of a line (such as
#include and
#define) are pre-processor directives, and they change the source code itself before the compiler actually begins running. For example, the
#include directive tells the program to copy the text of another file into the source code stream, verbatim.
What happens when you use
#define is that it creates a name for the string of code that follows the first word, so, if you have a statement
Code:
#define BUFFER_SIZE 127
Then wherever
BUFFER_SIZE appears in the code, the pre-processor will replace it with 127. This is not a variable or constant declaration; the exact string is replaced, before the compiler itself gets to see the code. So, if you had written the macro as
Code:
#define BUFFER_SIZE 128 - 1
it would insert the exact string '128 -1' into the source code stream. This can cause some problems at times, for example, if you wrote:
Code:
a = BUFFER_SIZE * 16;
it would actually convert to
which results as 112 at runtime rather than 2048 as you would probably expect. This is part of why instructors in C today tend to shy away from the pre-processor macros. You generally can avoid such side-effects by putting the whole macro body in parentheses, but even this isn't foolproof.
With a parameterized macro, like your example of
haszero(), the parameter is substituted for the arguments given it, so that
Code:
#define haszero(v) (((v) - 0x01010101UL) & ~(v) & 0x80808080UL)
/* ...some code later ... */
a = haszero(10);
would produce
Code:
a = (((10) - 0x01010101UL) & ~(10) & 0x80808080UL)
(I'll leave the evaluation of this up to you.)
As for the 'return type', it doesn't strictly return a value the way a function does; rather, the statement is inserted into the code as it is. In this case, the result is to compute whether there are any zero bits in the number below the highest set bit. It results in either a value of zero, if there aren't, or a non-zero number if there are. Since C treats all non-zero values as true, it can be used to test the number in question.