Another joy of C syntax are expressions: 1,010^2 == 10 Well, obviously one thousand and ten squared is not equal to ten, so this evaluates to 0? Wait, 1,010 is not the literal 1010, that's just a...
Another joy of C syntax are expressions:
1,010^2 == 10
Well, obviously one thousand and ten squared is not equal to ten, so this evaluates to 0? Wait, 1,010 is not the literal 1010, that's just a comma expression, which means the part before the comma gets ignored (after being evaluated) and the second part is the value!
We can reduce this expression to
010^2 == 10
Well, 10 squared is 100 and not 10, so this is still 0. Wait, 010 is an octal literal, since it begins with a zero, so the actual value is eight!
Ugh, so we have this
8^2 == 10
`Well, that still is 0, since 8 squared is 64 and not 10, right?
Wait, ^ is not power, but xor, so we don't have 64, but 8^2 = 10.
This is the same as the rhs of the ==, so this should evaluate to 1.
So we are finally d- Wait, == has a higher operator precedence than ^, so we actually have to evaluate 2 == 10 first (which is 0) and then xor that with 8!
Ok, so after evaluating 2 == 10, we have left
8 ^ 0
which is just 8.
While this example is a bit contrived, it is very short and I think this very beatifully showcases C's syntax.
If this was an issue for someone it would indicate to me more than that person was not familiar with the language than it would any issue with the language in its own right. I've never assumed (or...
If this was an issue for someone it would indicate to me more than that person was not familiar with the language than it would any issue with the language in its own right. I've never assumed (or seen someone assume) that you should use commas when writing numbers in a program, for example. And the ^ character to mean exponentiation isn't really much more intuitive than something like 10**2 or pow(10, 2.0f) anyway.
I guess I'm a little confused what this is supposed to showcase about C's syntax other than, well, it having a syntax in the first place.
Agreed, I never thought about inserting commas into numbers because I know how you can initialize multiple values on the same line using them, it's bound to cause confusion even if syntactically...
Agreed, I never thought about inserting commas into numbers because I know how you can initialize multiple values on the same line using them, it's bound to cause confusion even if syntactically valid. And you wouldn't use 10^2 if you were familiar with C, C++, or Java. Heck, even Python doesn't have ^ as an operator (edit: it is an operator, just not for exponentiation) - you use **. (Does any language use ^? I don't know.)
C has a lot of footguns to criticize (off the top of my head: the mutability of strings allocated like char str[6] = "Hello"; vs char *str = "Hello";, strcpy will always insert a null terminator (even if it means overwriting the last character) while strncpy does not unless the source has one within the specified size, having no way to detect when free fails, and all the implementation-specific behaviors, like how realloc always returns NULL when given size 0 in C90, but could return either NULL or a non-dereferenceable location depending on your C99/C11 implementation), I don't think this is really one of them.
Speaking of char str[6] = "Hello";, did you know that char str[5] = "Hello"; is legal C according to the standard and will allocate the string "Hello" without zero-terminator? (I always find...
Speaking of char str[6] = "Hello";, did you know that char str[5] = "Hello"; is legal C according to the standard and will allocate the string "Hello" without zero-terminator? (I always find obscure language features fascinating and can't help but to bring them into my own projects to the dismay of others)
Non-terminated strings are useful in low-memory environments. It's not a very obscure feature since you can easily overwrite a null terminator (e.g. char str[6] = "Hello"; str[5] = '!';), just an...
Non-terminated strings are useful in low-memory environments. It's not a very obscure feature since you can easily overwrite a null terminator (e.g. char str[6] = "Hello"; str[5] = '!';), just an obscure use that can easily turn into a serious footgun, even for experienced programmers, as it only takes 1 <= instead of a < in a for loop or an improper use of strncpy to destroy your terminator and cause weird bugs since GCC assumes you'll never do that on accident.
can't help but to bring them into my own projects to the dismay of others
You beat me by 22 seconds and said exactly what I was going to say-especially the last point! This is more like people doing awful things in Perl (or, these days, JS) that no programmer would ever...
You beat me by 22 seconds and said exactly what I was going to say-especially the last point! This is more like people doing awful things in Perl (or, these days, JS) that no programmer would ever do and using that to criticize the language (for those following along at home, that's an example of the strawman fallacy).
As you said, C (and of course Perl and JS) have flaws, but pointing out ridiculous things that you'll never see outside of an obfuscated code contest (and, in case you were wondering, even Python has one) doesn't move the discussion forward in terms of language design or providing safeguards about how the languages work at a deep level. It's just more tribalism and feces-flinging.
What I wanted to showcase are the places where C expressions - despite being clearly inspired by classical mathematical notation - deviates from it. Like you say, an experienced C programmer...
What I wanted to showcase are the places where C expressions - despite being clearly inspired by classical mathematical notation - deviates from it. Like you say, an experienced C programmer normally would be able to see what the expression means (and in most cases except ioccc wouldn't write it), but a beginner might not, because they see that C expressions are mostly like conventional math notation and will assume and it will compile. Additionally, on a code review it might not get caught because it looks just right.
And I still sometimes get caught by bitwise operators having a lower precedence than comparison operators, plus it causes paren-pain when doing a lot of bitwise operations.
Also, I really just like this expression because it's confusing.
I agree that this might be the most 'unexpected' aspect of how that expression evaluates, and also one you wouldn't be familiar with until you encounter it. Languages have to choose a precedence I...
And I still sometimes get caught by bitwise operators having a lower precedence than comparison operators, plus it causes paren-pain when doing a lot of bitwise operations.
I agree that this might be the most 'unexpected' aspect of how that expression evaluates, and also one you wouldn't be familiar with until you encounter it. Languages have to choose a precedence I suppose though.
Also, I really just like this expression because it's confusing.
Definitely, it's not immediately evident how it evaluates.
I think my point of disagreement is mainly that this type of thing is not unique to C. The only reason one might make those syntax choices is unfamiliarity with the language, and that happens regardless of the choice of language. I don't think ^ is a particularly more intuitive operator for exponentiation especially when so few languages use it, for example.
...and this is yet another example of why people are flocking to Rust and Go from C and C++ because of weird shorthand that was essential decades ago to keep code small and concise because we...
...and this is yet another example of why people are flocking to Rust and Go from C and C++ because of weird shorthand that was essential decades ago to keep code small and concise because we didn't have disk space but is just confusing and painful now.
1,010^2==10 evaluates to True in Go, but doesn't compile in Rust (by doesn't compile I mean you can't use it in an if block). Is that better or worse than compiling and giving a weird result?...
1,010^2==10 evaluates to True in Go, but doesn't compile in Rust (by doesn't compile I mean you can't use it in an if block). Is that better or worse than compiling and giving a weird result? Probably better.
Another joy of C syntax are expressions:
Well, obviously one thousand and ten squared is not equal to ten, so this evaluates to 0?
Wait,
1,010
is not the literal 1010, that's just a comma expression, which means the part before the comma gets ignored (after being evaluated) and the second part is the value!We can reduce this expression to
Well, 10 squared is 100 and not 10, so this is still 0.
Wait,
010
is an octal literal, since it begins with a zero, so the actual value is eight!Ugh, so we have this
`Well, that still is 0, since 8 squared is 64 and not 10, right?
Wait,
^
is not power, but xor, so we don't have 64, but8^2
= 10.This is the same as the rhs of the ==, so this should evaluate to 1.
So we are finally d-
Wait,
==
has a higher operator precedence than^
, so we actually have to evaluate2 == 10
first (which is 0) and then xor that with 8!Ok, so after evaluating
2 == 10
, we have leftwhich is just 8.
While this example is a bit contrived, it is very short and I think this very beatifully showcases C's syntax.
If this was an issue for someone it would indicate to me more than that person was not familiar with the language than it would any issue with the language in its own right. I've never assumed (or seen someone assume) that you should use commas when writing numbers in a program, for example. And the
^
character to mean exponentiation isn't really much more intuitive than something like10**2
orpow(10, 2.0f)
anyway.I guess I'm a little confused what this is supposed to showcase about C's syntax other than, well, it having a syntax in the first place.
Agreed, I never thought about inserting commas into numbers because I know how you can initialize multiple values on the same line using them, it's bound to cause confusion even if syntactically valid. And you wouldn't use
10^2
if you were familiar with C, C++, or Java. Heck, even Python doesn't have ^ as an operator (edit: it is an operator, just not for exponentiation) - you use **. (Does any language use ^? I don't know.)C has a lot of footguns to criticize (off the top of my head: the mutability of strings allocated like
char str[6] = "Hello";
vschar *str = "Hello";
,strcpy
will always insert a null terminator (even if it means overwriting the last character) whilestrncpy
does not unless the source has one within the specified size, having no way to detect whenfree
fails, and all the implementation-specific behaviors, like howrealloc
always returnsNULL
when given size 0 in C90, but could return eitherNULL
or a non-dereferenceable location depending on your C99/C11 implementation), I don't think this is really one of them.Edit: added some footguns.
It looks like BASIC does (or did?) according to this very interesting Stack Exchange post on the history of using
^
in programming languages.Speaking of
char str[6] = "Hello";
, did you know thatchar str[5] = "Hello";
is legal C according to the standard and will allocate the string "Hello" without zero-terminator? (I always find obscure language features fascinating and can't help but to bring them into my own projects to the dismay of others)Non-terminated strings are useful in low-memory environments. It's not a very obscure feature since you can easily overwrite a null terminator (e.g.
char str[6] = "Hello"; str[5] = '!';
), just an obscure use that can easily turn into a serious footgun, even for experienced programmers, as it only takes 1 <= instead of a < in a for loop or an improper use ofstrncpy
to destroy your terminator and cause weird bugs since GCC assumes you'll never do that on accident.cries in undefined behavior
You beat me by 22 seconds and said exactly what I was going to say-especially the last point! This is more like people doing awful things in Perl (or, these days, JS) that no programmer would ever do and using that to criticize the language (for those following along at home, that's an example of the strawman fallacy).
As you said, C (and of course Perl and JS) have flaws, but pointing out ridiculous things that you'll never see outside of an obfuscated code contest (and, in case you were wondering, even Python has one) doesn't move the discussion forward in terms of language design or providing safeguards about how the languages work at a deep level. It's just more tribalism and feces-flinging.
I thought it was just a funny comment with no intention of criticizing the language.
What I wanted to showcase are the places where C expressions - despite being clearly inspired by classical mathematical notation - deviates from it. Like you say, an experienced C programmer normally would be able to see what the expression means (and in most cases except ioccc wouldn't write it), but a beginner might not, because they see that C expressions are mostly like conventional math notation and will assume and it will compile. Additionally, on a code review it might not get caught because it looks just right.
And I still sometimes get caught by bitwise operators having a lower precedence than comparison operators, plus it causes paren-pain when doing a lot of bitwise operations.
Also, I really just like this expression because it's confusing.
I agree that this might be the most 'unexpected' aspect of how that expression evaluates, and also one you wouldn't be familiar with until you encounter it. Languages have to choose a precedence I suppose though.
Definitely, it's not immediately evident how it evaluates.
I think my point of disagreement is mainly that this type of thing is not unique to C. The only reason one might make those syntax choices is unfamiliarity with the language, and that happens regardless of the choice of language. I don't think
^
is a particularly more intuitive operator for exponentiation especially when so few languages use it, for example.Okay. I know very little about programming. I know how to do some scripting and such. But what the fuck
...and this is yet another example of why people are flocking to Rust and Go from C and C++ because of weird shorthand that was essential decades ago to keep code small and concise because we didn't have disk space but is just confusing and painful now.
1,010^2==10 evaluates to
True
in Go, but doesn't compile in Rust (by doesn't compile I mean you can't use it in an if block). Is that better or worse than compiling and giving a weird result? Probably better.