K&R C (2014)
spin0r.wordpress.com
spin0r.wordpress.com
add(a, b) { return a+b; }
fib(a) { return (a <= 1) ? 1 : (fib(a-1) + fib(a-2)); }
That's totally valid K&R C. The lack of boilerplate makes abstraction really cheap, conceptually --- Forth programmers will recognise the advantage here. You can write real programs out of tiny one-line definitions like the above.Of course, ANSI C is better in just about every other way, but I still regret losing the stripped-down simplicity of K&R C.
> fib(a) { return (a <= 1) ? 1 : (fib(a-1) + fib(a-2); }
>
> That's totally valid K&R C.
Apart from, presumably, the missing closing parenthesis ;)Fixed. Thanks.
ps: I have some javascript bits that are almost as dense... Hum.
Well, as long as you don't care about abstracting over anything other than int.
int func(long_type_name a, long_type_name b, long_type_name c) { ...
when I really, really should be able to type int func(long_type_name a, b, c) { ...
It would be nice to get this back and it would take away some useless noise. Sadly, this is now seen as a feature and languages like D insist on the repeated type name.I'm sure there are other places were ANSI C could be made cleaner, but perhaps at the cost of backwards compatibility.
The K&R style variable declarations are still also part of ANSI C, so you could write:
int func(a, b, c)
long_type_name a, b, c;
{ ...That never existed. Syntactically it could work; you would need semicolons in there:
int func(long_type_name a, b, c; int d);
similar to struct/enum member declarations.Speaking of which: don't have long type names in C programs, and simplify API's with structs rather than large numbers of parameters. :)
void foo(a,b,c)
long_type_name a,b,c;
{
...
} func(a, b, c long_type_name) int {...http://stackoverflow.com/questions/5885156/no-defined-type-o...
This is because in pre-ANSI C the names of struct fields were not internal to their enclosing struct but global identifiers that simply represented a type and offset. There was no ambiguity because different structs could not have the same member names. As raimue pointed out this is why some *nix struct members are still prefixed with a namespace like st_ for struct stat.
Can anyone verify this? I can't find a copy of K&R C online to check with.
Find a open source project to support. Get familiar with the code base and the community. Write a patch, send it, get it refused, work with the community, finally getting your patch in.
See, it's not that hard learning "C" and becoming a "C developer"... 21 days at most!
External libraries are a reality in any programming language. The popular ones that come with your OS of choice are likely very well documented. You don't need to understand the preprocessor, compiler, assembler, and linker to use them. In fact, I'd argue that for most programming you only need to know how to invoke the compiler as the rest of the steps are already automated. K&R C will teach you enough about how to use header files to be able to use external libraries.
A debugger is useful, but something like gdb is not necessary to write C. In fact, I'd say Valgrind is far more useful, and once again, it'll take 20 minutes to learn.
As for supporting open source projects, why start there? Do you learn Python and immediately jump to submitting patches to Django? No, you write your own code and learn from that. Same here, as you are presumably learning C/JavaScript/Ruby/Erlang/CL/etc. to use it, not to add a line to your resume that you are a contributor to some open source project.
No, you won't become an expert developer in 21 days. But since C is such a small language with so few features I'd argue that learning how to use it is more about getting the memory structure right in your head than about learning syntax. By comparison, Python (my primary language lately) has many more types and constructs to worry about.
Autotools has a steep learning curve and makes a good deal of project maintainers nervous because it's not obvious how it works and it's diffult to cut through all the abstraction steps to figure out what-goes-where. CMake is usually a pretty good alternative to figuring out m4 scripts with autotools. CMake is usually faster too.
Valgrind is good but it doesn't work on all platforms and it again needs more examples and howtos.
Dtrace too could also use some more blog howtos.
Powerful tools need to be both clearly and succinctly explained and sufficiently configurable in order to be widely usable. The clear conveyance of system behavior should be a top priority for code and supporting documentation.
All I'm saying really: Learning C is easy, becoming a halfway decent C programmer is hard.
Otherwise a few points, from the copy here (clearly a 1st ed): http://cs.upm.ro/_users/cursuri_on_line/Alte_documentatii/C%...
"void didn’t exist in the book (although it did exist at some point before C89; see here, section 2.1)" -> There are many occurrences of void in the document above.
"It appears that stdio.h was the only header that existed" -> From the document above many other headers are available (string.h, math.h, etc.)
"Note that return 0; was not necessary." (on the main function) -> return 0 is not necessary in Standard as well (even if the semantic of omitting it is different between C89 and C99).
Prior to C99, reaching the closing "}" of main() caused the program to return an undefined status to the calling environment. In C99 and later (as in C++), reaching the closing "}" does an implicit "return 0;".
As for stdio.h being only header: it makes sense, as headers like stdlib.h and string.h contain mostly function declarations, which you don't strictly need in K&R. Some of 80's UNIX C code examples in books I've seen don't include anything (and there are even some that declare things like FILE and errno directly without including it from anywhere).
Heh. Don't believe everything you read, kid.
Still, the evidence of their recent departure is all around us. vi (short for "visual", because the idea of a visual editor was still pretty novel) didn't even exist until 1976.
The whole point of this post was the original K&R C, so why wouldn't they reference/assume teletypes?
https://github.com/TazeTSchnitzel/BBCode630
(to compensate for the limitations of a budget 80's printer)
What's the most recent file system where this is possible?
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=96191
Also found others claiming that Solaris has the same behaviour.
The first UNIX I programmed on (SVR2, circa 85-86) still worked that way (no opendir/readdir) since it had the classic V7 filesystem. Each directory entry was just a fixed-size 16 byte chunk; 2 bytes for the inode number and 14 for the filename.
Of course, this meant a limitation of 64K files per filesystem -- that was still OK for us (40MB would be a large disk) but was already would have caused problems for bigger servers. It also was the cause of the "classic" UNIX 14-character filename limit.
By that time BSD4.2's FFS filesystem had already removed both of these limits (which I assume is when opendir() and friends arrived) According to wikipedia that made it to the AT&T world by SVR4 in the late 80s, which sounds correct.
As someone else mentioned some UNIXes may still let you open() a directory. However, since we're long past the days of there being just one "UNIX filesystem" it would be impossible to interpret its contents in a portable manner.
#define return(value) return debug_return(value), value
where a debugger couldn't be deployed. Or even something more fun like: #define return(value) return 0
/* Dr. Evil pinky laugh here */
This could even be used as a basis for code testing fuzzer like so (missing seeding management and iterations, of course): #define return(value) (int)arc4random()I find it interesting to see what C was like even earlier. Notably there was an incompatible break between the 6th and 7th editions, when the compound assignment operators were flipped. It used to be "=+" instead of "+=". I guess Ritchie figured it'd be better to avoid ambiguity with expressions like "x=-1;".
x=-42;
when you meant x= -42;
was just too easy. I know. I did it.Similarly, as I recall, variable initialization of the form
int i 17;
created some sort of parsing problem and was changed to the current form int i = 17;
I learned Unix on a PDP-11 running v6 and I had to learn all of the changes when we got 32V for the new VAX.Also, what language would you switch to on your Unix installation?
foo(a, b)
int a, b;
{
}
I was mildly disappointed when this was [relatively recently - maybe 10 years ago?] taken out of g++. (Not sure what its status has been in the language standard.) Sure, it's a WTF to most people. But if you have really long type names used more than once, it actually saves typing.In Verilog you can say:
module foo(a);
parameter width = 20;
input [width-1:0] a;
But "ANSI" Verilog is "improved": module foo(input [19:0] a);
Oops.. no place to put the parameter.. so later on they amended: module #(parameter width=20) foo(input [width-1:0] a);
They should have just removed the argument list: module foo;
input a;
This would have worked for C as well: int foo()
int a, b, c;
{That isn't so scary; the parenthesized version,
int x(1)
is still seen all the time, in places like C++ constructor member initializers. It's how you actually declaratively initialize a type with that value, rather than initializing the memory with a default constructor and then getting the assignment operator called on the resulting object.The real issue is that the parentheses in the above used to just be part of the expression, rather than required, so
int x 1
would have worked just as well. I'm fairly certain I'm glad of the change, but the previous state of affairs wasn't crazy.The resolution was that struct didn't introduce a new namespace - it wasn't legal to have the same member name used in two structs in the same scope.
Does any one know if that's true, or just an urban legend?
K&R style is clearly in evidence in source code from 1973 such as https://github.com/dspinellis/unix-history-repo/blob/Researc...
Looks as valid to me today as when I first read K&R C 25 years ago. In short K&R is terrible. If you hew to it, first you'll end up with code that is unsafe and sketchy. Second you won't learn about modern features in c, which make writing safe non-sketchy code a lot easier.
Hmm.. I don't remember ever having to do this, even on very old systems.