IIRC, if the argument to sizeof is a type, the parentheses are mandatory, anyway, so using it like it was a function is more consistent.
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.
Agreed. I spend most of my time writing non-semicolon languages like Ruby, Haskell, Scala, Elixir, Clojure, and Python. As a result, I never developed the semi-colon reflex that a lot of Algol-family language users built up. I constantly forget semi-colons even after all these years, and my own inability to remember annoys me.
I used to skip semicolons in JavaScript because I could never remember to include them. Using a linter with Emacs made it a non-issue; I get a warning in my editor immediately when I miss one.
PS: I'm really enjoying Rust, so maybe this will be the language that finally forces me to pick up the semi-colon habit.
I thought about that regarding semicolons, optional braces, newline placement, consistent placing of whitespace in general; then I decided having the machine tell me where I need to do more work is all very well and good, but what I really want is a program that will do the work for me instead. So I wrote one: https://www.npmjs.com/package/jsclean
I don't believe its equivalent. When I'm writing Scala code in the backend and have to switch to frontend I often find myself omitting the semi-colons but going back and fixing due to self-imposed coding conventions. For Javascript just as in Python or Scala, semi-colons are optional for a good reason.
We would all be better off if semi colons had just been required in JavaScript. The problem is that semi colons are not actually optional in JavaScript. Instead JavaScript use ASI, automatic semi colon invasion, where the compiler attempts to determine where semi colons should go. Unfortunately the rules are complex, prone to certain errors, and I can't rely on everyone I ever work with understanding all of those rules. For all of these reasons if you are writing JavaScript you should just use semi colons.
>Unfortunately the rules are complex, prone to certain errors, and I can't rely on everyone I ever work with understanding all of those rules.
Are they really complex? This recently released version 4.0.0 of the JavaScript Standard Style [1] suggests to never start a line with "(" or "[". This rule looks even simpler than the rules of operator precedence.
Well, semicolons may be optional in Python but the official python style strongly recommends agains (needing to use them)
It's not about forgetting them once in a while, it's about playing a game of "needs a semicolon or doesn't" which increases the (already high) number of things the developer needs to worry about
Dereferencing a null pointer is legal in C. It's the conversion of a null pointer from from an r-value to an l-value that's illegal, which does not happen in that snippet of code.
That's why it's perfectly legal in C to do this (&*foo), even if foo is a null pointer.
> Dereferencing a null pointer is legal in C. It's the conversion of a null pointer from from an r-value to an l-value that's illegal, which does not happen in that snippet of code.
And it does not happen in that snippet of code because sizeof is nothing like a function.
DRY is good, but making the structure of your code reflect the actual semantics you want is better. What you want is to allocate space for foo. So write that.
Another oddity of C that amuses me is the do/while loop without braces:
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.
Its not so disturbing when you consider that braces aren't special cased in the C grammar. They group statements so they can be used together where a statement is needed. From the grammar's perspective the "normal" way is without braces. It's just that all the C-alikes have gone a different direction with the way braces are parsed leading everyone to regard the original behavior in C as ugly warts.
for, if, and while without curly braces for single statement blocks are easily one of the worst things about C. It bites you in the ass every time. The balance between its utility and its capacity to cause bugs is so one sided, I don't understand why it's even taught to beginners. If you are teaching a new programmer that saving keystrokes is important, you're on the fast track to creating a shitty programmer.
That's pretty severe. Written with spacing, its really pretty clear what is meant by
if (condition)
Foo(x)
Braces are, in my opinion, an unfortunate necessity in some cases. They are a much larger cause of error than NOT using them ever could be.
An ideal IDE would make blocking visible (background tone change etc), and braces could be emitted automatically by the IDE without ever cluttering up the code shown to the programmer.
But tying everyone to a single IDE is never going to happen. The language is out there. It's going to stay backward compatible forever.
How could the presence of braces cause a worse error than no braces? Code compiles when you completely omit braces but put more than one statement underneath. The error is logical, not syntactical.
But if you have an open brace without a matching close brace, that's a compile error. What other error are you referring to?
How can you know that you won't add more statements to the block? Why have a special case at all? For me it's just become muscle memory to add the braces. It's a risk with exactly zero upside to omit braces.
non-bracket is useful and informative in some cases when used properly (your local style guide takes precedence of course). I would never write
if (condition)
Foo(x)
for the reasons you said. But it can be very useful for a block of "single liners":
/* clean up input before passing it to flaky_external_module() */
for(; !isspace(*p); ++p);
if (!isdigit(*p)) return INPUT_ERR;
for (char *i = p; *i; i++) *i = toupper(*i);
...
flaky_external_module(p);
Basically a small block (that pretty much fits in your fovea) that does a bunch of minor tasks. Spacing them out would actually confuse the code.
> Another oddity of C that amuses me is the do/while loop without braces: [...]
> Even though do/while is a keyword bracketing pair in C, it still only lets you use a single statement (because nested whiles).
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);
Oohh... this would be so good for the underhanded C contest! Just mix is up with the comments which make the `do` look like (assuming good actor) an accidentally wrapped comment.
/* The following code does something, so here's the
// explanations of what happens. And here's what we actually
*/ do
printf("hey\n");
/* And now just count down */
while (some_check(--i));
I'd like to confess that I'm one of the return() people, and I also do if() even in cases where the language doesn't require it. This behavior isn't borne out of confusion about what is and what isn't a special language construct. Rather, it helps me prop up the illusion that programming is about using a few simple primitives instead of being the chain of compiler directives it actually entails - it's an esthetic choice if not always a logical one.
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.
No, not always. Not in Go, or in Rust, or Ruby, for example. The assumption being in these languages that if is followed by an expression and that expression is parsed to its natural end anyway - but this only works if the syntax allows for that end to be found without ambiguity.
Not even in C. You can have an if condition that is actually a macro that expands with parentheses. For example the macros in ctype.h, so you can actually write
I respect Linus greatly but the way he to talks to his fellow 'humans' is insanely confrontational, rude and disrespectful. It overshadows his arguments (which are usually very good) and throws people on the defensive. I'm grateful for all he has accomplished and shared with us but I imagine working with him is 'hell'.
I would imagine once you get used to him and thicken your skin up a bit, it's no big deal.
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.
He literally says that those who disagree with his code style should be shot. That's completely unacceptable language to use and makes him sound like he hasn't mastered English. I'm not sure anyone should take c-language style tips from such a boor.
Most systems programmers I known share similar views.
The rational is that you should only be working on systems-level code if you have an exceptional understanding of computers, the code base, and the repercussions of changes. If your understanding of multithreading isn't flawless then you should not be working on the OS scheduler, at all. And for a programmer working at this level to not recognize the importance of code clarity is to undermine the responsibility that goes with it.
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.
I find parens around a return value/expression slightly annoying, but not annoying enough to have a debate over it. It doesn't make the code harder to read, IMHO.
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:
(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.)
You do need to omit whitespace between the name and the opening parenthesis when defining a functionlike macro, but it doesn't matter when you're calling it.
I furiously looked at the C standard (C99, anyway) and the documentation to the gcc preprocessor, and have to admit somewhat embarassedly, that you are right.
The gcc preprocessor documentation clearly states:
> "If you use the macro name followed by something other than an open-parenthesis (after ignoring any spaces, tabs and comments that follow), it is not a call to the macro, and the preprocessor does not change what you have written."
I first learned that sizeof was an operator when I was confused about how it dealt with arrays compared to functions. After I learned it was an operator and not a function things made sense and were (relatively) consistent again. Several commentators here have given other examples where you will be completely thrown off if you believe that '"sizeof()" really is a function.'
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.
> Here's an example of a really bad use of "sizeof" that doesn't have
the parenthesis around the argument: sizeof(*p)->member. Quite
frankly, if you do this, you should be shot.
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!
What kind of monsters don't use parentheses on sizeof. Is it the same guys that put { on new lines and have giant comment templates for every one line function.
Ah, to join the bikeshedding: { on newlines make imho more sense, the block of code is visually balanced that way. We have high-res screens now, no reason to pretend we're still on 80 char terminals.
But work long enough in either one of those styles, and the other one will start to look strange.
Indentation to identify blocks can break down in things like switch statements:
switch( c ) {
case 'A':
simple_stuff();
break;
case 'B': {
int temporary_variable = 0;
complex_stuff( temporary_variable );
}
To me, the advantage of braces on newlines is it makes it extremely easy to tell where blocks start and end---whether you're using your favorite IDE, or reading the code on a blog, or reading the code with "cat". I actually think it's a case where Python has the design advantage on C (since if you do braces-on-newline consistently enough, you end up duplicating python but with superfluous braces added in).
Fundamentally, the problem is that braces are needed at all. Humans are bad at matching them up, so they don't match the humans' intention when indenting. Confusion.
It's all backward. The IDE should SHOW you the blocks somehow (background tone changes etc), and let you edit the blocking explicitely. If they look wrong, you select and hit a key. Now you're in agreement with the compiler.
I'm not seeing what the issue here is, besides that this won't compile because of mismatched braces. There is no need to put braces around the contents of a case block. Unless I'm switching on an enum, I rarely find a compelling reason to use switch/case, and there's usually a cleaner way to do it.
"There is no need to put braces around the contents of a case block."
There is here.
In some dialects, you can only declare a variable at the start of a block. From the perspective of the compiler, a "case block" isn't actually a block, just stuff between labels. In order to declare temporary_variable, it may be necessary to put the braces, and it is probably best practice as temporary_variable may otherwise be exposed to later cases (and in C++, a jump over a variable declaration seems to produce an error).
Most C++ codebases I've seen have the brace on a fresh line, most Java codebases at the end of the line (at a guess born of the Sun guidelines and default Eclipse formatting perhaps). I started with the former, but prefer the latter now.
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.
Which ones would that be? The Eclipse one has a million knobs and twiddles, and is bearable in 95% of the time after fiddling with them a lot, which I'm okay with.
The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. The size is determined from the type of the operand. The result is an integer. If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.
The type of an assignment expression is the type of the left operand unless the left operand has qualified type, in which case it is the unqualified version of the type of the left operand.
So it is indeed the case that `sizeof(a = 12)` is equivalent to `sizeof(a)` in the C world.
An interesting result of this (I suppose...) is that if a macro expands an argument twice, and one of those times is an operand for sizeof, you don't need to mention the double expansion in the documentation.
I learned C from 'Kernighan & Ritchie', where it's never referred to as a function but as a compile-time operator. I've never thought of it as a function.
Except algebra said that about algebraic functions and operators.
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.
Compile-time functions are a thing. Not in C, but in other languages, including C++.
This all comes down to what you think "function" means. If you're living in a C bubble, of course sizeof isn't a function. If you have a wider mapping (!) for the word, then it is a kind of function.
Yes, return can be a function. Torvalds hasn't heard of continuations, obviously, where we "return" from a function by invoking a continuation.
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.
That point is clearly made in the comment you're replying to, with the additional point that if we are talking about C, then sizeof is an operator, not a function. It doesn't require parentheses, except when its operand is a type expression. No well-considered coding convention requires superfluous parentheses with sizeof, and there are good reasons to ban them.
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.
Scheme has come up with the idea of pretending that an undelimited contination is a function, which is somewhat true-ish... in the degenerate sense of a function with empty domain.
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.
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.