Guess I grew up with old-school C. Looking at the question with example code made me think: "Am I missing something? Looks like pretty ordinary C to me. Why wouldn't it compile?"
If you were a programmer in the days before C had prototypes then you are definitely from the old school. If you're working on code that still looks like this in 2012 then maybe it's time to brush up on modern C. ;-)
He didn't say he still works on code like this, but like me, this program is obviously fine, there's no point in asking why it works because c is backward compatible and it's pretty clear to anyone who knows c that this will work. It doesn't mean we'd write code like that...
This is what bothers me about a lot of programmer communities these days. What is a very mundane detail of K&R legacy, that C programmers know very well either from looking at old source trees or making sense of error messages, suddenly turns into something where 116 people (current stack overflow +1 count) are totally amazed and breathless that they've discovered something deep. I suppose it's fair if they just don't know C, so they've not seen it and find it interesting, but could this person's dilemma not be resolved with a simple google search? Or even taking a guess?
It can be really hard to Google things when you don't know the terms involved. Until, someone posts source code and you get directed to stack overflow etc. Thus, the fact that out of millions of people with some familiarity with C 100 or 1000 chose to up vote an obvious post does not really seem that strange.
Thing is, the C standard (compared to many other standards) isn't even that complicated. Googling for the C standard doc, and then grepping through for "function declaration" would likely clear that up.
But it's way easier to consult the lazyweb than to do your own research.
Why do you assume 116 people are "amazed and breathless"? It's an interesting question and I don't believe the answer is obvious unless you've been exposed to legacy C code before, and such users may arguably constitute a large part of SO's audience.
You may be able to solve the question by a simple search, but it still fits in on StackOverflow. The fact that it was upvoted a lot is irrelevant to me, it just means a few people bothered to press a button.
Maybe "amazed and breathless" is a bit hyperbolic, but I assume the parent is referring to the number of people who had up-voted it at the time of his posting.
Perhaps for people who don't really know C, that's interesting information, but anyone who calls themselves a "C programmer" should not be surprised by that bit of code (well, you should be surprised if anyone still writes code like that, but not surprised that it's valid).
It's not even just an "exposed to legacy code" thing: C is a fairly simple language in both syntax and concepts (esp. when compared to most other languages people are using today), and I don't believe you can call yourself fluent in C without this kind of knowledge.
(I could also argue from the other angle: if you haven't been exposed to that sort of legacy C code, you probably haven't done it enough to call yourself fluent.)
I think it's an interesting question from the perspective of "what are some cool language gotchas that people not familiar with the language would find neat?"... or from the perspective of someone who is new to C, came across some code like that, and is trying to learn what's going on (and is apparently too lazy to google the C specification and spend 2 minutes reading it).
I agree with you. Maybe the better question is why does anyone think it shouldn't compile? And then we can ask how much programming history they know.
I think it's useful to know how C compilers, the C specification and C coding styles have evolved over the years. For one, it helps put some of the arguments for/against certain styles and syntax in perspective.
In short, make an effort to learn from history before you make the minimal effort required to learn from a contemporary forum like stackexchange.
It's been a while since I've done this(usually by accident), but I believe that GCC and Visual Studio both throw up a warning along the lines of "Type not defined, assuming int".
I see questions online all the time that make me wonder just how old the compiler being used is. Modern versions of GCC, Clang and Visual Studio all have warnings enabled by default for an awful lot of valid, but probably wrong, code. Apparently people learning to code are somehow winding up on old compilers much more often than those of us who have been writing C and C++ for a long time.
Or, I guess, the people having problems learning to code are the people who happen to be on ancient compilers. Maybe it's not so surprising after all.
It might be the default settings on the compilers as well. I believe that in our environment, we set our warning level higher than the default, because a lot of the warnings(signed/unsigned mismatch, 32/64-bit pointers, etc.) can be an indication of an actual bug.
Many C programmers cannot make valid judgments as to the severity of a warning ('warning: bla bla may ...' => does it or doesn't it? I wouldn't know)
Also, those programmers typically would not know how to change their code to get rid of warnings. If they do, or if their compiler shoves a solution in their face (for example, a compiler might suggest to do "if((y=f(x)))") to get rid of an 'assignment inside if' warning), they do not see the advantage of the extra work.
The end result is code that produces hundreds of warnings. If a code change brings in a new one, people don't notice and/or don't care.
Some IDEs make things worse by allowing you to filter out warnings and messages from their output, so that you can focus on compiler errors. That is good for good programmers (they will fix errors, compile again, and then study every single warning), but bad for those who are happy to have code that compiles at all, as they will run into 'do not show me warnings' mode.
The first reaction to an unfamiliar warning in any language should be to consult the manual (or Google it). In the case of VisualStudio at least, this is trivial (these days) due to the warning numbers you can paste into Google and usually get not just an explanation, but a way to fix it (usually via SO).
Really, if a programmer can't fix warnings (and that includes taking the time to learn why they are there), what are you paying them for? My policy on projects I work on is to enable all warnings, treat them as errors, then only disable warnings after trying to fix them, researching them, and finally discussing them, and usually then only on a case by case basis (only occasionally do I eschew certain warnings altogether (such as -Waggregate-return for g++), and I leave them commented out with the reason they are disbled).
(Edit: a quick tip for gcc, BTW: use -fdiagnostic-show-option for easier search of a warning, and the name of it in case you have to pragma it out).
I've just tried it. That code compiles without warnings by the default configuration of gcc version 4.4.5 (Debian 4.4.5-8) in my desktop (it's an up-to-date Debian Stable install).
If the default settings of gcc included throuwing out warnings for every time a function is declared without parameters and missing the void, lots of people would complain, and loud. It's a very common construct.
Wow, that's crazy. I just tried it too (gcc 4.6.3). No warning options generate no warnings at all. -Wall only warns about the lack of return statement in main(). You only get the parameter-type default warning if you add -Wextra.
Interestingly, adding -std=c99 oddly removes the warning about the missing return statement in main().
I can't find a way to warn about the empty param list in the prototype, but it's possible that it's just not considered "bad enough style" to warrant one.
C++ is, of course, more strict. Try to compile that with g++, and you get several errors. The compiler needs to know how to mangle the symbol when it first encounters it, so you can't have "unspecified" parameter lists anywhere.
foo()-with the parenthesis- is a function. foo-without the parenthesis-is a variable. That means that:
Foo foo();
is a function pointer declaration within the scope of the main function. Since it's a function, which does not have member types, a is not within the scope of Foo().
Now, if there was a 0 argument constructor for struct Foo, this would be valid:
defines a macro called yell. The preprocessor operator ## concatenates it's operands together so something like ma##in gets transformed into main.
>#define billy_bob yell(i,n,m,a,t,e)
defines a macro called billy_bob that simply expands to the macro yell. When we call yell with the given arguments we get m##a##i##n (look at the order of the arguments for yell) which gets transformed into main.
>billy_bob()
Expanding the macro turns this line into main().
>{
> printf("no main in da house!");
>}
So now we've declared a main function that just prints "no main in da house!".
There's the "##" operator with which you can glue together identifiers with the preprocessor.
#define yell(i,l,o,v,e,u) o##v##i##l
yell(i,l,o,v,e,u) will turn into the identifier ovil.
And billy_bot just uses the yell-macro to produce a "main" identifier.
#define billy_bob yell(i,n,m,a,t,e)
yell(i,l,o,v,e,u) --> o ## v ## i ## l
yell(i,n,m,a,t,e) --> m ## a ## i ## n
So billy_bop is main.
Why is this features useful at all? When you actually want to programmatically create function names, type names, struct names in preprocessor generated code.
#define ENCAPS_STRUCT(type,name) struct encaps_##type { type name; }
ENCAPS_STRUCT(int,a);
It's just a fancy way of assembling the identifier "main" without writing it explicitly. Inside a preprocessor macro definition, the double hash mark is an operator which concatenates its left and right operands. This is a lexical operation. The "yell" macro takes six arguments and returns the concatenation of arguments 2, 3, 0, and 1, ignoring 4 and 5.
The billy_bob macro calls the yell macro, passing in some letters - if you look at which letters from "i,n,m,a,t,e" end up attached to which arguments in "yell", you'll see that "yell' rearranges 'i,n,m,a' to spell the word "main".
I'm going to take a guess at this... the macros compile down to #define billy_bob main?
Let's see... yell() takes six 'bare' strings and appends four of them together in a certain order while ignoring the other two. This turns (i,n,m,a,t,e) into (m,a,i,n). The second define sets billy_bob as the output of yell.
See, this is why I like HN. The discussion here is more interesting than the OP (which is, indeed, a very basic question that a little bit of research into the history of C would answer). I thought I knew about preprocessor magic (including stringification and variadic macros), but I'm glad to have run across this post (and the ensuing discussion) as I've learned something very useful from it (eg, how to construct more dynamic code in C/C++).
The first C project that I worked on used this a lot. Its definitely not the easiest to understand when coming from a C++ background (because no parameter declaration means no parameters) or from other descendant languages.
I think it's a case of not having the compiler warning level set to it's highest level (which it should be) or the bad habit of some programmer's ignoring warning messages.
52 comments
[ 2.7 ms ] story [ 49.8 ms ] threadhttps://en.wikipedia.org/wiki/C_%28programming_language%29#K...
But it's way easier to consult the lazyweb than to do your own research.
"C99 standard doc" let's you find http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf but searching for "function declaration" does not take you anywhere useful.
That's not to say reading the whole thing is a waste of time, just beyond the scope of most new programmers.
You may be able to solve the question by a simple search, but it still fits in on StackOverflow. The fact that it was upvoted a lot is irrelevant to me, it just means a few people bothered to press a button.
Perhaps for people who don't really know C, that's interesting information, but anyone who calls themselves a "C programmer" should not be surprised by that bit of code (well, you should be surprised if anyone still writes code like that, but not surprised that it's valid).
It's not even just an "exposed to legacy code" thing: C is a fairly simple language in both syntax and concepts (esp. when compared to most other languages people are using today), and I don't believe you can call yourself fluent in C without this kind of knowledge.
(I could also argue from the other angle: if you haven't been exposed to that sort of legacy C code, you probably haven't done it enough to call yourself fluent.)
I think it's an interesting question from the perspective of "what are some cool language gotchas that people not familiar with the language would find neat?"... or from the perspective of someone who is new to C, came across some code like that, and is trying to learn what's going on (and is apparently too lazy to google the C specification and spend 2 minutes reading it).
I think it's useful to know how C compilers, the C specification and C coding styles have evolved over the years. For one, it helps put some of the arguments for/against certain styles and syntax in perspective.
In short, make an effort to learn from history before you make the minimal effort required to learn from a contemporary forum like stackexchange.
Or, I guess, the people having problems learning to code are the people who happen to be on ancient compilers. Maybe it's not so surprising after all.
Many C programmers cannot make valid judgments as to the severity of a warning ('warning: bla bla may ...' => does it or doesn't it? I wouldn't know)
Also, those programmers typically would not know how to change their code to get rid of warnings. If they do, or if their compiler shoves a solution in their face (for example, a compiler might suggest to do "if((y=f(x)))") to get rid of an 'assignment inside if' warning), they do not see the advantage of the extra work.
The end result is code that produces hundreds of warnings. If a code change brings in a new one, people don't notice and/or don't care.
Some IDEs make things worse by allowing you to filter out warnings and messages from their output, so that you can focus on compiler errors. That is good for good programmers (they will fix errors, compile again, and then study every single warning), but bad for those who are happy to have code that compiles at all, as they will run into 'do not show me warnings' mode.
Really, if a programmer can't fix warnings (and that includes taking the time to learn why they are there), what are you paying them for? My policy on projects I work on is to enable all warnings, treat them as errors, then only disable warnings after trying to fix them, researching them, and finally discussing them, and usually then only on a case by case basis (only occasionally do I eschew certain warnings altogether (such as -Waggregate-return for g++), and I leave them commented out with the reason they are disbled).
(Edit: a quick tip for gcc, BTW: use -fdiagnostic-show-option for easier search of a warning, and the name of it in case you have to pragma it out).
If the default settings of gcc included throuwing out warnings for every time a function is declared without parameters and missing the void, lots of people would complain, and loud. It's a very common construct.
Interestingly, adding -std=c99 oddly removes the warning about the missing return statement in main().
I can't find a way to warn about the empty param list in the prototype, but it's possible that it's just not considered "bad enough style" to warrant one.
C++ is, of course, more strict. Try to compile that with g++, and you get several errors. The compiler needs to know how to mangle the symbol when it first encounters it, so you can't have "unspecified" parameter lists anywhere.
(And I'm not even that old! I've only been writing C for 12 years or so!)
I think you missed the point of the post.
http://en.wikipedia.org/wiki/Most_vexing_parse
Now, if there was a 0 argument constructor for struct Foo, this would be valid:
Foo foo = Foo();
>#define yell(i,l,o,v,e,u) o##v##i##l
defines a macro called yell. The preprocessor operator ## concatenates it's operands together so something like ma##in gets transformed into main.
>#define billy_bob yell(i,n,m,a,t,e)
defines a macro called billy_bob that simply expands to the macro yell. When we call yell with the given arguments we get m##a##i##n (look at the order of the arguments for yell) which gets transformed into main.
>billy_bob()
Expanding the macro turns this line into main().
>{
> printf("no main in da house!");
>}
So now we've declared a main function that just prints "no main in da house!".
Why is this features useful at all? When you actually want to programmatically create function names, type names, struct names in preprocessor generated code.
will generate...The billy_bob macro calls the yell macro, passing in some letters - if you look at which letters from "i,n,m,a,t,e" end up attached to which arguments in "yell", you'll see that "yell' rearranges 'i,n,m,a' to spell the word "main".
Let's see... yell() takes six 'bare' strings and appends four of them together in a certain order while ignoring the other two. This turns (i,n,m,a,t,e) into (m,a,i,n). The second define sets billy_bob as the output of yell.
I don't know C very well, is this correct?
In C++ it means no parameters.
To declare a C function parameterless you need to say int func(void).
Also, C will assume int if you leave off param or return types.
...I'm surprised I know that.
The first C project that I worked on used this a lot. Its definitely not the easiest to understand when coming from a C++ background (because no parameter declaration means no parameters) or from other descendant languages.
What it is, however, is hard-to-read and -maintain code.
It was quite ok.
This is another proof that as programmers we need to learn and improve constantly :)