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.
So, for the sake of good practices, write if with parenthesis.
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.
/* 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));
Now spot that in a large file of real code!> "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."
Thanks for pointing this out to me!
Edit: Via the C99 spec (http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf).
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.
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.
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.
if (condition)
Foo(x)
Bar(x)
Expecting Bar(x) to be part of the conditional. This can and does happen. 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. int *foo = NULL;
foo = malloc(sizeof(*foo));
would be undefined behavior (dereferencing NULL)!That's why it's perfectly legal in C to do this (&*foo), even if foo is a null pointer.
And it does not happen in that snippet of code because sizeof is nothing like a function.
typedef int thing_t;
...
thing_t *foo;
// code
foo = malloc(sizeof(thing_t)); typedef int thing_t;
...
thing_t *foo_internal;
thing_wrapper_t *foo;
// code
foo = malloc(sizeof(thing_t));
If you do: foo = malloc(sizeof(*foo));
That's at least always on the same line.This, on the other hand, always allocates one object of foo's pointed-to-size, whatever its type:
foo = malloc(sizeof(*foo)); typedef struct { int value; } thing_t;
That way the compiler catches it when you try to pass the wrong thing (at least, more of the time). int *foo;
foo = malloc(sizeof(*foo));
And avoid the brittleness mentioned.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.
In C89, sizeof is always compile-time.
Edit, except it doesn't treat arguments the same, so no.
sizeof belongs in its own category of unary compile-time(-ish) operators, whose only other member is _Alignof.
If you're so inclined, you might add _Generic and _Pragma to that mix as well, but they aren't really a close fit.
Spacing code out makes it more readable and maintainable, not less. It brings consistency, and it is more prepared for the inevitable change. I think maintenance is the driver of all code style. When you make tight one liners or forgo braces, or use the ? And : operators instead of if and else, you're not really saving time, you are deferring work, in a lot of cases to another programmer.
I prefer that blocks of code hold together -- think of them as paragraphs. Spacing each sentence of a paragraph out is similarly confusing. But as you say, YMMV.
I find return is_valid(result) ? result : ERR_CODE; common and clear, but perhaps you don't.
I do think that any style guide should forbid while and do..while simply because for has become by far the looping construct of choice in C.
In all cases the point should be correctness and clarity, not showing off that you use unusual language features.
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).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 don't know what the point is in pretending IDEs or advanced text editors don't exist.
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).
So really the argument should be that everyone should use a linter.
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.
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.
[1] https://github.com/feross/standard
Update: Now I learned about the JavaScript Semi-Standard Style [2] which accually enforces semi-colons. Quite hilarious.
Please stop spreading FUD.
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
In javascript you may omit semicolons, but the interpreter will guess your intention when it is ambiguous and it will probably guess it wrong.
Anyway it nicely illustrates the need to get braces out of there altogether. The programmers' intent is obvious; let the IDE 'make it so' by emitting braces in the generated code.
It was the cause of the "goto fail" SSL bug that affected both of Apple's operating systems last year: https://nakedsecurity.sophos.com/2014/02/24/anatomy-of-a-got...
So I'd say it's rare, but it happens. And when it happens, the effects can be pretty big.
if (condition)
Foo(x);
Bar(x);
But I have seen if(condition1)
if(condition2)
if(condition3 && condition4)
Foo();
This upset the old ARM compiler I was working on and it decided to skip some of the conditions. I fixed the bug, related to this code, by adding braces: if(condition1)
{
if(condition2)
{
if(condition3 && condition4)
{
Foo();
}
}
}
So this is why I always put braces to define scope. Although, in general, both of these types of bugs are uncommon.Honestly, the more pervasive problem that comes up is the eventual addition of new code adds noise to diffs. Like if I have a condition with a single statement
if (condition)
Foo(x);
And I add something to it, I have to add braces and it pollutes the diff with stuff that isn't really related to what I'm changing. if (condition)
{
Foo(x);
Bar();
}Do what you need to work around a known compiler bug, of course, but that is definitively a compiler bug. The meaning is unambiguous and consistent in the C standard and every implementation I've encountered. I'm not comfortable with the assertion that changing your coding style here makes you less susceptible to compiler bugs in general.
There seems to be a logical error to me. An indentation mistake - something that can be caught trivially by a linter - is not significantly different by nature than any other single-character mistake, like an incorrect constant or misspelled identifier (harder to find with a linter). But because it was at the root of a specific flaw, it's become larger than life.
The previous poster didn't say he was unwilling to learn JS semicolon rules, he was saying he couldn't trust everyone he ever works with to learn them. Replying with an accusation of FUD and a link to someone ranting that it's unprofessional for JS devs to not know these rules is unnecessarily inflammatory while missing his point.
One quick example is the very contrived example that follows.
return {a:1, b:2}
That is valid JavaScript. It is evaluated as an empty return and an unreachable expression. Probably not what was intended.
I've sent copies of the rules to people and explained them numerous times, but at the end of the day it is more productive to just use them and move on to something else IMHO.