A nice, little known C feature: Static array indices in parameter declarations
hamberg.no
hamberg.no
void bar(int myArray[10+]);As far as it being a bug waiting to happen, C already has plenty of traps like that - '=' vs '==', '&' vs '&&', and so on.
In terms of grammatical clarity, I shouldn't think it would be any more confusing than any other operators that have both unary and binary versions. By my count C already has three of those: '-', '&', '*'. The latter two are even examples where the unary meaning and the binary meaning are wholly unrelated.
Besides, it's a way of using + that is already well-established in everyday idiom. And there are semantically-related uses for + that are already well-established in computer languages, too - the Kleene +, for example, should be familiar to everybody.
In terms of how it would work for the grammar I realize this would be somewhat of a departure since it's repurposing a symbol that's normally an operator to be used in a manner that would be more like a keyword according to C syntax. . . but I think that's a detail that should be much more interesting to standards committees than it is to people who primarily just use the language. And in terms of the human factor I submit that it's much more workable than taking a word from the English language and repurposing it to mean something that's more-or-less the opposite of what it means in English.
And C programmers are already spending plenty of their time cursing the world because of those things. Let's not add more of that, shall we? ;)
= and == should have different meanings.
I also like how JavaScript has ===, although it is a little superfluous.
There are two operators, equality = and assignment :=
But where it is clear that assignment was meant, = is treated as assignment. this means you can:
a = 5; -- exactly equivalent to a := 5 here
if a = 6 then
raise exception 'this should never happen';
end if;
Interestingly this is entirely a Postgres-ism. Oracle's PL/SQL offers no such "feature."CLI/C++, by which Microsoft replaced Managed C++, added new operators and keywords without much regard for backwards compatibility (=without underscores).
The implication being that people value code readability over __backward __compatibility. I have no data on the popularity of Managed C++ vs CLI/C++ though. I was only a fanboy because CLI/C++ looks awesome and I generally enjoy Herb Sutter's work, but then got sucked into the Appleverse.
public function get foo():int { return 5; }
But you can also say: var get:int = foo;So:
class Foo
{
virtual void bar() final;
};
But: int final = 3;
So, for example, this is legal: class Foo
{
virtual void final() final;
}; var var = 1;
var select = var.ToString();
var from = from c in @select select c.GetHashCode();
(The @ thing, which tells the compiler, "This is an identifier and not a keyword", can help quite a bit in a pinch.)http://stackoverflow.com/questions/1091634/why-do-perl-varia...
(I merely state this as fact and offer no judgement!)
sub while
{
print "It works!\n";
}
&while();
I guess it is still the case that you can break perl code by adding keywords, if it collides with a subroutine that is called with the "&" symbol. Seems most code doesn't use "&" though, but it's a relatively quick fix.Calling it with parentheses like &foo() would make @_ empty inside of foo. (Or if you said &foo("whatever") it would pass that as @_ instead.)
sub while
{
print "@_\n";
&foo();
}
sub foo
{
print "@_\n";
}
&while("a","b","c");
produces: a b c
(blank line)
as the output, and sub while
{
print "@_\n";
&foo;
}
sub foo
{
print "@_\n";
}
&while("a","b","c");
produces: a b c
a b c
as the output. At any rate, back to the original topic: it still doesn't prevent new keywords from potentially colliding. Oh well. @static @inline @unsigned @int ilog2(@unsigned @int x)
{
@for (@unsigned @int i = 0; i < 32; i++)
@if ((x >> i) == 0)
@return i;
@return 32;
}
I'm a bit dizzy looking at that code, let me sit down for a moment. @autoreleasepool{
for (int index = 0; index < 100; index++){
int error = [MyObject performSelector:@selector(addCoolFilterToImage:)];
if (error) {
return 32;
}
}
}— Tom Duff
Not exactly the same situation, considering that compilers work.
C++, on the other hand, has a horribly complex grammar. It would be unfair to say that "nobody really knows what it is," given that it has been standardized. But I think you will find that many tweaks to your code are needed when going between different C++ compilers.
I think what a lot of commenters in this thread are missing is that while the concept of a highly context-sensitive grammar seems simple, the reality is not. And having such a grammar makes it virtually impossible to have good tools.
Before some pedant reminds me that C's grammar is also context-sensitive: yes, I know. But you can parse C with lexx and yacc anyway, whereas you have no hope of doing this in C++.
[0]: Nobody would here be an obvious exaggeration, as evidenced by the above blog post.
Then again, as far as actual grammars go, I've heard C++ is bad enough that the compilers are the standard, and that if you want to be "compliant" with real world C++ code you copy every feature [1] of GCC.
[0]: http://www.amazon.com/Expert-Programming-Peter-van-Linden/dp...
[1]: ftp://ftp.trailing-edge.com/pub/rsx11freewarev2/rsx81b/374001/jargon.txt
C and C++ are good for doing low-level stuff. If you don't want to do low-level stuff, then you should not worry about them. But in that case, you might also want to avoid commenting about them :)
clang is copying gcc's features because most of them are good features, and standards bodies move slowly. This is kind of an odd thing to criticize C++ for, since a lot of newer languages don't even have a standard, but just a reference implementation. It's hard to criticize your neighbor for living in a tent when you live in a sleeping bag.
I said books. I got that vibe in general from the people I met who claimed to be C wizards. Incredibly offputting.
>C and C++ are good for doing low-level stuff. If you don't want to do low-level stuff, then you should not worry about them. But in that case, you might also want to avoid commenting about them :)
I was going to delete my original comment because reading it over it felt like a bad idea. (I don't like jumping into ignorance induced shitstorms.)[0][1]
>This is kind of an odd thing to criticize C++ for, since a lot of newer languages don't even have a standard, but just a reference implementation.
I should have put "features" in quotes. I was specifically using the definition in the old jargon file. [2] So what I really meant to say is that the compilers end up supporting each others bugs for compatibility reasons.
As for reference implementations, if the reference implementation is for all practical reasons the only implementation, then you don't need a standard.
[0]: The comments I got in response are interesting enough that I'm actually glad I didn't.
[1]: EDIT. My ignorance.
[2]: See footnote on the last post, I should have made that more clear or used different terminology.
Example?
So that's what I'm going to do unless I suddenly get the urge to go running through clangs commit log.
Duff would apparently rather read C that was written in C.
Honestly, it might be Stockholm syndrome, but I don't find bash hard to parse at all. The only real problem I see is that people get in trouble when they try to nest quoting styles. But there's basically never any reason to do that. User-defined functions basically eliminate this (may not be POSIX compliant, but supported in all shells I know of),
I factor all my shell scripts into 2 to 10 line functions and they are super easy to maintain and are 5-10x shorter than any alternative.
User-defined functions are part of POSIX, but the function keyword is not. This bit me when I was porting a script from bash to dash (worth the effort on slower devices, as dash is significantly faster loading).
On bash you could do something like this:
function foo()
{
echo "Foo"
}
In POSIX shell (and, thus, also bash) you do this: bar()
{
echo "Bar"
}
Reference: https://wiki.ubuntu.com/DashAsBinSh#function void foo(int (&foo)[10]) {}
However, that is not "minimum of 10" but "exactly 10".You can also get the size at compile time:
template <size_t N> void foo(int (&foo)[N]) { int newarry[N+2]; }
I've used the above for something like this: template <size_t N> void safe_strcpy(char (&buf)[N], const char *src) {
strncpy(buf, src, N-1);
buf[N-1] = 0;
} template <size_t N>
typename enable_if<(N >= 10), void>::type
foo(int (&bar)[N]) { ... }
EDIT: Changed condition from N >= 0 into a more meaningful one. X-) template <size_t N>
foo(int (&bar)[N])
{
static_assert(N >= 10, "Array needs >= 10 elements!");
...
} template <size_t N>
typename enable_if<(1 < N && N < 10), void>::type
foo(int (&bar)[N])
{
// called when 1 < N && N < 10
}
template <size_t N>
typename enable_if<(N >= 10), void>::type
foo(int (&bar)[N])
{
// called when N >= 10
}
OTOH, the key advantage in static_assert is the meaningful error message.There'a an interesting analogy with structs here. K&R invented the -> operator specifically to obviate (* ptr).member. It's too bad that arrays took a different route through C's history and didn't manage to end up in a place where a similar convenience would make sense.
flags: -g -Wall -Wextra -std=c99 -pedantic
The following code compiles and runs (for me at least) with no errors. Tried with both stack and heap allocated arrays of various sizes.
#include <stdlib.h>
void foo(int array[static 10]) {
(void) array; // suppress unused var compiler warning
return;
}
int main () {
int *x = calloc(10, sizeof(int));
int *y = calloc(9, sizeof(int));
int *z = calloc(11, sizeof(int));
foo(x);
foo(y);
foo(z);
foo(NULL);
int a[9];
int b[4];
int c[11];
foo(a);
foo(b);
foo(c);
return 0;
}Unfortunately, though, I believe one of the -std=gnuXX variants is the default, so most people don't make that a conscious decision.
A declaration of a parameter as "array of type" shall be adjusted to "qualified pointer to type", where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.
Since that's a 'shall' declaration, shouldn't it at least throw out a warning?
This is an area where a compiler can (sometimes) see violations, though, so I think gcc should diagnose when possible, in the same vein as format specifier mismatches in printf().
This is not a reason to avoid the construct, but as yet gcc[1] doesn't optimize it either.
It's getting mixed reviews, but I really found it useful (even if I, like others, disagree with some of his tips). It's particularly good at sorting out what you can do in C99 and C11.
[edit: He has a really useful section on sorting out the different meanings of "static" in C, though I don't recall this being one of them.]
Compiling another language to C is good way to learn a lot about C's nooks and crannies.
You could also look at C coding standards such as the MISRA guidelines. While many things they complain about should be obvious, there will inevitably be some really obscure things they urge you not to try.
You get the point I hope.
I think Go was designed with this in mind? Also, Haskell's type system guards against this. On the other hand C does nothing to prevent you from shooting yourself in the foot and C++, while improving some things, makes it overall worse because of sheer amount of constructs in the language.
Ruby and JS are better in that they run on VMs and so won't segfault (that often), but other than that they do very little to help avoid making mistakes (implicit undefineds passed to functions in JS...).
Anyway, languages are not created equal and one thing a language designer can optimize for is to reduce the probability of programmer making mistake. That's only one of the variables however and sometimes it's the other goals that are more important and then we get languages like C++. That's not to say it's bad, it's just optimized for different things.
func foo(b bool) {
if b {
return
} else {
return
}
}
func bar(b bool) int {
if b {
return 100
} else {
return 200
}
}
func baz(b bool) int {
if b {
return 100
}
return 200
}
func qux(b bool) int {
if b {
return 100
} else {
return 200
}
panic("?")
}
// Error: function [bar] ends without a return statement"Here's this issue in Go bug tracker: https://code.google.com/p/go/issues/detail?id=65
I don't know, maybe I'm just weird, but when I see a comment replied to with a single link patterns suggest that it's a link disproving something. So I got confused until I had read the full page.
[1]: http://commandcenter.blogspot.com.au/2012/06/less-is-exponen...
For example, I do not see PL/I's choice to make none of its keywords reserved names (IIRC, defended on the argument that one cannot expect anybody to know all the reserved words) repeated much anymore in Algol-like languages.
As another example, IDE/language pairs such as Eclipse/Java have learned us the advantages of languages that are easy to parse and where there is little ambiguity even in incomplete or incorrect programs (it makes syntax coloring easier, and allows for refactoring tools that are somewhat reliable on non conforming source code)
Other examples are inline (different from C++, makes use of weird combinations with static and extern) and tgmath (compiler magic inaccessible to user-defined functions until C11).
They also seem to __barely__ improve the language without ever being "cool" or "interesting".
At least C++ has some standard data structures...
PS: Even Python has binary literals, while they were deemed "not useful enough" for C.
void bar(int myArray[static 10])
Is that standard, and if so, how long has it been?I also ran across this construct in a quick search [1]:
void bar(int myArray[const])
[1] http://stackoverflow.com/questions/3693429/c-parameter-array...6.7.5.3 point 7: “If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.”
UPDATE: Thanks. The 2005 date on the linked document threw me off.
In the rare cases you need to do this sort of check why not just write a simple sizeof test?
[eric@rangely foo]$ cat foo.c
#include <stdio.h>
int test (int arr[10])
{
printf ("%lu\n", sizeof (arr));
return 0;
}
int main (int argc, char *argv[])
{
int arr[5];
test (arr);
return 0;
}
[eric@rangely foo]$ gcc foo.c -Wall -o foo && ./foo8
(EDIT: fixed formatting)
More to a style/safety point, if I ever see a function that expects an array and doesn't also take the size of that array as another parameter, that's a bug waiting to happen.
Especially in this case since this feature seems to only throw a warning on recent versions of clang, and more or less nothing else.
So, YMMV.
What happen if you do
void bar(int fooArray[static 0]) {} ?
Is NULL allowed?
test.c:3:30: warning: 'static' has no effect on zero-length arrays [-Warray-bounds]The “expression” here refers to an expression in between [] in an array declaration; so the declaration of size 0 is a constraint violation and requires a diagnostic. You can get gcc and clang to issue a relevant diagnostic with “-std=c99 -pedantic”.
"The size of an array is part of its type. The types [10]int and [20]int are distinct."