I think the reason it is not a function from the standard's point of view is that C does not have any builtin functions (unless my memory totally fails me in this case), all functions have to be either defined locally or #included.
I heard people arguing on the Internet that you should not add parentheses when sizeof is applied to an expression, only a type. I just cannot understand why they bother. Just add a parenthesis and it is always right. Much less cognitive burden.
So
sizeof(type)
is a special case anyway.I'm all for using sizeof like a function, but that doesn't make it consistent. sizeof is just a special syntactical construct.
I like to think that sizeof is called an operator just for syntactic convenience much in the same way as typedef is a storage-class specifier.
int *foo;
// code
foo = malloc(sizeof(int));
a few months later, change foo to be a double. Code still compiles, no warning, but you're allocating half the memory you need. int i = 4;
do
printf("hey\n");
while (--i > 0);
Even though do/while is a keyword bracketing pair in C, it still only lets you use a single statement (because nested whiles). So everybody uses braces, and thus it looks quite disturbing without them.Ah, but don't forget you can still use the comma operator, so get several statements in before the semicolon:
int i = 4;
do
printf("hey"), printf("Jude.\n");
while (--i > 0);Remove the newline and it looks ok to me:
int i = 42;
do printf("hey\n");
while (--i > 0);
Another possibility would be: int i = 42;
do printf("hey\n");
while (--i > 0);The problem with sizeof is that is should be able to accept a type as an argument. No function in C can do that, according to the standard C grammar.
Somewhat similarly, the standard va_arg is a macro, not a function, also because it accepts a type.
The thought that if() could just be a lazily-evaluated function taking a code block argument, and that return() could be a way of marking the end result of an expression somehow pleases me.
I think the different expectations about sizeof() come from the artificial distinction between operators and functions, the implication being that in a compiled language the sizeof operator would be a compile-time construct, or barring that, at least a behavior of the type system. On the other hand, there are tons of compiler intrinsics in C/C++ that look exactly like functions but aren't.
> and I also do if() even in cases where the language doesn't require it
This sentence could be parsed in multiple ways though :)
if islower(c) {
(..)
}Hell, a little salty language and telling people they're being idiots when they are being idiots is not a bad thing. The best machinists and millwrights I've worked with were like that, and it was a good thing - you don't have time to ask politely when there are steal beams or a two-ton electric motor swinging towards you.
C can be almost as dangerous :-)
return (0);
I.e., with a space. I'm not sure if someone ever told or recommended me to do this, but the reason I did this was consistency with other C statements, because all C statements that take some kind of expression as a parameter (if, for, while) require it to be surrounded by parentheses. So it seemed logical to me to do the same with return. I've stopped doing it now, although some of the old code still lives.I've seen code by others where there was always a space between the function name and its arguments. That was ugly, and really confused a function call with a statement.
Whitespace between a function name and the arguments is significant, however, because with a function-like macro, there must be no whitespace between the name and the opening parenthesis. I've seen the following code in production code, for example:
#include <stdlib.h>
#define free(x) free(x), x = NULL
free(ptr1); // Macro
free (ptr2); // Call free(3) directly
(Whether or not such a macro is good idea is a different question entirely, the point is that you can prevent such macro-expansion by using whitespace. Aesthetically, I find it very disturbing, though.) char c;
short h;
int i;
char *s;
1 = sizeof (char)
2 = sizeof (short)
4 = sizeof (int)
4 = sizeof (float)
8 = sizeof (double)
1 = sizeof (c)
2 = sizeof (h)
4 = sizeof (i)
4 = sizeof (s)
4 = sizeof &c
1 = sizeof c
2 = sizeof h
4 = sizeof i
4 = sizeof s
1 = sizeof *s
4 = sizeof &main
4 = sizeof (sizeof (i))
4 = sizeof (sizeof i)
4 = sizeof sizeof i
4 = sizeof sizeof sizeof i
from: http://leapsecond.com/tools/size1.cAh, yes. No comment from Torvalds is complete without a gratuitous ad hominem.
"You're watching dancing bunnies, and you dare suggest we should remove the parens after sizeof?"
Attack on the person (watching dancing bunnies), used to discredit an unrelated opinion (parens after sizeof).
The problem with "lies to children" is that they can pile up to the point where people end up becoming completely confused about what's actually happening. As the internal inconsistencies mount, the "simple" lie starts to become a lot more complex than the "complicated" truth.
Good ol' Linus
Ah, but in Haskell, `return` is a function, though it shares only a few similarities with with its counterpart in C-like languages.
However, when using continuations or a continuation passing style, the continuation is a function that behaves almost exactly like traditional return when called!
(define (multiply x y return)
(return (* x y)))But work long enough in either one of those styles, and the other one will start to look strange.
Indentation is how you should identify blocks.
To me its more about conserving an extra bit of vertical space... I like my methods/functions quite compact. In the past I liked the balanced nature of the braces though.
Makes absolutely no appreciable difference though of course, as long as you don't get into a formatting war with your team.
sizeof(type)
or sizeof expression
but e.g. unlike a function, it doesn't evaluate the expression. sizeof(my_function()) doesn't call my_function.
sizeof(a = 12) doesn't assign 12 to a.Anyways not all those writing code have CS background
I know a CS major who insisted in pointing out the same thing. He was extremely surprised when dragons hatched from his eggs instead of the chickens he expected. It was a really heated event. He barely escaped -- and then ranted for several weeks how they were really just eggs and couldn't explain what happened.
int *foo = malloc(sizeof(*foo));
would make no sense.We can justify writing return (expr); using the same arguments that justify the sizeof (expr) convention.
The thing is that in C, return isn't a function; there are no continuations.
Similarly, sizeof is an operator, which doesn't reduce its argument expression to a value.
If we are going to make coding conventions based on pretending that C is a different language in which sizeof is a function, then pretending return is a function is also fair game.
We should prefer code like:
type *ptr = malloc(sizeof *ptr); /* no parens */
to type *ptr = malloc(sizeof (type));
In general it's often better to base sizeof on an ordinary expression rather than a type expression.If we don't use superfluous parentheses on sizeof, we can then look for sizeof followed by an open parenthesis to look for code where sizeof is applied to a type expression.
sizeof (type)
means "produce me a size_t value based on some arbitary type, without checking that it's related to anything in the surrounding code". It can be as dangerous as a (type) cast.This is fine for Scheme, which is an expression language, so every method of invoking a continuation would, syntactically, be an expression anyway.
In C, which has statements, making return look like an expression is somewhat pointless. It doesn't matter whether C "has" continuations (and it arguably has) or not.