Best Practices for Programming in C
An anonymous reader writes "Although the C language has been around for close to 30 years, its appeal has not yet worn off. It continues to attract a large number of people who must develop new skills for writing new applications, or for porting or maintaining existing applications. This article provides a set of guidelines that can help you with your coding."
Although the C language has been around for close to 30 years, its appeal has not yet worn off.
I'm sure that in some ways that statement may be true, but I've been trying for MONTHS to get people interested in the C/C++ Meetups! Compared to Python you'd think we were trying to start a Sanskrit study group!
btw..fp? hee hee!
Quod scripsi, scripsi.
From the article:
for(i=0 to 100)
array[i]=0
Maybe I missed something but since when is that C?
Whitespace
"When a ball dreams, it dreams it's a frisbee"
If you reply, do so only to what I explicitly wrote. If I didn't write it, don't assume or infer it.
(please note: I'm still learning C) So what should I be using instead of scanf? The article recommends against it, but doesn't say whether there is a better way to get some input and get interesting chunks out of it.
I do not have a signature
But for embeded systems, where your resources may be very limited, wouldn't a good optimizing compiler do a better job than an OO one?
I'm sure an assembly code monkey could do better than a c junkie, but I wouldn't know the rankings between the compiled code now-a-days as a desktop programmer.
-
ping -f 255.255.255.255 # if only
If you reply, do so only to what I explicitly wrote. If I didn't write it, don't assume or infer it.
Here is a much better article about coding practices. It also covers broad topics, not just C in specific.
I've read many times that modern compiled C code or well written C++ code is typically far better than hand written assembly code. I don't know that definitively, however. I'd assume that for small snippets, like specific loops etc., that an assembly programmer could still look at the compiler output and perhaps improve it slightly (if they could understand it!).
C++ is a super-set of C, so in theory you can get the same output if you want. However, typically program efficiency is at a higher level than specifically optimized loops etc. - e.g. at the level of algorithm choice. In addition, a good C++ matrix class (for example) can do constant folding, loop unrolling and ensure the memory access order of common matrix/vector operations is appropriate for your CPU (via templates) - that you'd be hard pressed to do in C except for very small projects.
/..sig file not found - permission denied.
Dont programm in C unless you have a really good reason to.
Good reasons are:
- You are extending an operating system kernel or other huge system that is written in C.
- You are writing a library and expect it to be used from several other languages. C is the lowest common denominator, every language has a C interface.
In all other cases, use a high-level language. Users and admins will be grateful for not having to download patched versions every time a buffer overflow or format-string error is found. You will like being able to concentrate on the core problem you want to solve instead of dealing with all the detail C and its libraries left for you to care about. If you need speed, use a high-level language with a good compiler. If you need to bit-fiddling, use a high-level language with good bit-fiddling support. They exist, and there's even some overlap between them.Programming can be fun again. Film at 11.
Ah.. but with C++, you have complications like inheritence and polymophism that can make C++ more complex. I'd suspect OOP systems to be a little less efficient memory-wise at least, to be a bit more ineffficent for this dynamic type thing. But then again, what do I know?
-
ping -f 255.255.255.255 # if only
This article really does not say anything that any decent programmer does not know. Why we need an article to tell us the basic rules of C is beyond me.
1. Indent properly.
2. Make your code readable.
3. Use good variable names.
4. Avoid buffer overflows.
5. Avoid using statements like goto.
They missed the most basic rule of them all though, convert to C++.
Programming can be fun again. Film at 11.
When writing C++ you have full control over the use of dynamic type info, static or polymorphic method lookup, memory use and object lifetime, etc.
So, a good programmer can ensure that for performance critical code, none of those expensive features are used (selectively).
/..sig file not found - permission denied.
Well, in my job it's common to see C code for almost all our embedded firmware. The only exceptions to this that I've seen is the stuff written in assembly. It's also used to write most of the test software we use. Our department has consciously chosen to limit us to C or VB for development unless you can come up with a truly good reason for using another language. Our department is concerned mostly with testing the products manufactured in our factories. Programming was an interesting little course taught on the side in our electronics courses. From experience we've found that it's easier to train people on one or two languages, rather than have them try to remember (and purchase licenced copies of) VB, VC++, LabWindows, LabView, QuickBasic, HP Vee, Perl... I think that the deficiencies in any language are usually minor. It's more important to build up good techniques and practices like the original author had intended.
The old saying "It's a poor workman who blames his tools." applies here too.
Apparently it was too obfuscated for IBM. The first code example is missing a } at the end of the line.
=================
Unix is very user friendly, it's just picky about who its friends are.
FYI, the article seems to be something like a company "basic C coding standards" rather than anything to do with what I understand as "best practices"; maybe a coding tips sheet for an introductory C programming class.
Larry
Not sure about other "real programmers" (yes, I'm 40 years old, and old enough to know what a real programmer is :-)) but I can write buggy code much more quickly in assembler than in C... :-)
For example, for the C program that I'm writing right now, I decided to use GLib -- the base utillity library used by GTK.
I initially chose it for portability reasons, but soon discovered it had a wealth of cool stuff in it. In addition to providing the standard data structures (trees, hashes, linked lists), it also has a string type ( GString, ) which handles a lot of the string issues that C programmers get bogged down with.
A lot of the gotchas (buffer overflows, et. al.) mentioned in this article have to do with these string issues, and using GLib's GString data type has enabled me to avoid those.
There is another library similar to GLib, The Apache Portable Runtime, used in the Apache webserver, and also in Subversion.
In addition to all this, I'm using XML as the storage format for my program, mostly because libxml takes care of the file parsing issues so I don't have to.
Bottom line, choose your libraries carefully, they can make a world of difference.
Thomas
Oh what enlightenment!
- When you've learned all there is to know about C, find out how to simplify it a bit in C++. Notice the job security and look of awe when you master the ++.
- After mucking around in these low-density languages, step up to Perl and see how a language built by a task-oriented person stomps one built by a system-oriented person. See your project file sizes shrink before your very eyes!
- Now take your newfound magic ability to learn new languages and apply it to whipping out pages with PHP and MySQL for all your friends quickly! You'll be the talk of the C crawlers crowd! Hey! Gimmie some content! Aw, forget it - let me just play!
- Now plumb the depths of (supposed) machine-independent laguanges by writing some Java and finding out what "Sun-certified" means! (hint: Sun owns it)
- Optional: Head back to school to get a PhD in autoprogramming theory and self-construction methods. Sequester yourself away to your dorm room for endless hours of experiments training a neural net to convert tasks to code using the most efficient method possible.
- Finally, wrap up your technical life by examining all these related language nuances holistically and achieve the zen of programming: "there is no language"
I thought you could use NULL to calculate structure offsets, so with something like
struct _x {
int a;
int b;
}
it was legal to say &(((struct _x *)NULL)->b)
to figure out the offset of b within the structure?
So I agree, it seems like pretty basic tips, not any sort of "best practices" thing.
- adam
You can find many more of these here...
pb Reply or e-mail; don't vaguely moderate.
In the old days, many CPUs were simple, they only concerned themselves with the current instruction. And, with their lower speeds, compiler optimizations were too slow (and required too much memory) to be worth it. These days, it's not an issue.
This is getting a little rediculous. I'm seeing people confuse exploits with all other bugs. Buffer overruns and the like, which are a side effect of sloppy coding, make up a minority of the total bugs I see on a daily basis in production code. The only reason they are so well known is the consequences. Higher level languages, such as Java and Python, may protect the programmer against the effects of such sloppyness, but they do nothing to protect you against algorithmic bugs, which make up an overwhelming percentage of the bugs I see. Coding in a higher level language may protect you from easily writing exploits, but they do not guarantee you bug free, stable code. Only paying attention to every line you write, and making it as bullet proof and stable as possible, will help safeguard you from all of the many bugs we see. C and C++ are entirely appropriate languages to work in, if they fit the needs of your project.
don't right code that sucks.
...it also has a string type ( GString, ) which handles a lot of the string issues...
G-String?? Could they have come up with a different name for that type of string? Issues like butt-flossing and the like..
http://github.com/gbook/nidb
What I want to see is material on setting up a project in an intelligent way.
I've got Lakos' book, but there just isn't much out there on non-trivial stuff.
How about articles that show intelligent uses of CVS, Doxygen, and autotools?
What about using C++, boost.python, mod_python, and apache for that killer website?
Get thee glass eyes, and, like a scurvy politician, seem to see things thou dost not.--King Lear
C is a fairly complex language, e.g.:
some_func( (void (*)(void*))x );
I would rather say that C allows you to do some pretty complex things than to say C is complex. While there is the obfusicated C coding contest which will show you much worse (complex?) code, it is not that the language is at fault.
C is really quite simple. The number of keywords is few, the syntax is not terribley difficult. I'd rather think of C like legos, very simple, but powerful enough to build awesome creations.
Norris/Palin 2012
Fact: We deserve leaders who can kick your ass and field dress your carcass.
But code that sucks *should* be righted...
I want to drag this out as long as possible. Bring me my protractor.
Argh! No it isn't! If C++ were a superset, all valid C programs would be valid C++ programs. This is obvious tripe.
Presumably, this doesn't compile with your C++ compiler.
I work with a guy who is a total guru who has actually done what you suggest, i.e. sat down with C++ code and improved the assembly output of parts of code. He said he would sometimes get a 5-10% improvement in some areas.
In order of best to worst:
1) Code that works
2) Code that doesn't work
3) Code that endangers my computer
If Java can eliminate the bottom class, more power to it. If a program doesn't work, you don't use it. If a program is unsafe, you may get screwed before you figure out not to use it. Again, which is the real danger here?
I've had this sig for three days.
Consider C++: you can screw up in all the ways you can screw up with C, and many more besides. I don't think anybody would consider C++ a low-level language.
I actually agree with your point. I'd just express it differently: C supports the kind of low-level tweaking most programmers shouldn't bother with any more. You should avoid C unless you really need to do this stuff. Otherwise, use a language/library combination that does the low level stuff for you, and probably does it better than you could.
C++ is effectively a super-set of C, though not a strict superset of C.
Good enough for you?
Who modded him informative? Of course it won't work because he uses C++ keywords as variable names! IT'S A JOKE! LAUGH!!!
This could be rewritten as:
I believe this is clearer, and less error prone."Who modded him informative? Of course it won't work because he uses C++ keywords as variable names!" Err, that was the whole point. It's a valid C program but not a valid C++ program, therefore C++ isn't a superset of C.
*sigh*, common myth. The standards, which are available for a small fee from the ISO, say otherwise.
C++ is absolutely, postively not a superset of C. It is quite easy to create valid C code that a standards-compliant C++ compiler can't digest, even by accident.
Although the C language has been around for close to 30 years, its appeal has not yet worn off
UNIX is the same age, and it hasn't stopped people from thinking that UNIX-like operating systems are the pinnacle of good design.
don't think this has ever been in distpute by anybody. However, it's still far easier to write in C than assembly.
IMO it isn't. Assembly is trivially easy to write. But assemblers have gotten progressively crappier over the years, as much more development effort has been put into C compilers. Even popular assemblers like NASM are barely passable compared to what could be done.
I'm an electrical engineer first and a programmer second. I write everything from BIOS-esque firmware clear up to applications that do data analysis from huge mainframe databases for my company. My language of choice? As close to ANSI C as I can get, though usually I get lazy and use some GNU extensions.
Now, why on earth would I do something like that? Just to piss off the comp sci types around me? Well, no, but that's a nice side effect.
Reasons I love/use C:
- It IS glorified assembly. As such, the compiler tries to be minimally helpful and just lets me do what I want to do.
- It doesn't allow OOP crap. I won't argue that OOP doesn't have its place, somewhere, but that place isn't in anything I've needed to build yet.
Why do I hate OOP so much? First and foremost I find it only a tool of obfuscation in the stuff I write. It's much easier to debug procedural code by just following the execution path than wondering what the hell the object that was just instantiated invoked in the constructor, etc. Too much jumping around in the code - basically causes me to forget to look, because it's not intuitive. Same exact reason I hate FORTH (but I'm still fluent in it anyway). I'd rather just have an initializer function called immediately following the declaration of a new variable than having behaviours tacked on.
- C is faster than C++ if you do everything according to the OOP model that C++ leads you toward. I guarantee my pointer math blows away any "safer" solution in terms of raw speed. It's also just as safe, because all the inputs are bounds checked once so I know they can't exceed certain limits. However, for intelligent mixes of OOP-model code and C-style fun, there's no significant speed difference.
- Also, C is standard. C is everywhere. I can use it (with code appropriate to the task) on everything from a PIC12xxx series part clear up to our IBM big iron (though that lack of curly braces it quite annoying). As long as I hang out close to ANSI, my code tends to be very portable. As such, I have a library that is used on everything from small, embedded M68k-series processors up to 16-proc Unix boxes and even larger yet mainframes. Completely threadsafe, no memory leaks, and very, very portable.
Basically, don't use bloat-o-language unless you have a good reason. Remember - CPUs still essentially execute one instruction at a time, mostly in order. Programming is all about moving register 1 to register 2, possibly while performing an arithmetic operation involving register 3.
I would assert that inappropriate use of OOP model programming, a complete disregard for effeciency in the face of beauty-of-design, and an unholy fascination with language du jour has lead to the sorry, bloated, buggy, slow state of modern applications. IMHO, OOP has lead us down this path, without buying us much. Much except the ability to hire less competent programmers to build their tiny chunk of the application, because everything's overdesigned and all the interfaces are pre-specified, because that's how humans would model the problem. Usually there's little regard for how a machine could best handle the problem.
atoi has undefined behavior for some error conditions. strtol is a safe version. You should only consider using atoi if you are entirely sure that the input is valid. And even then you should choose to use strtol to make the code rubust in the face of changes.
(For C++ heads, just read an int from a stream of whatever kind with >>, just as you do for any other type, and then check the stream status afterwards to make sure the operation succeeded, as in if (mystream >> myint) it_succeeded(); )
Buy a copy of "Code Complete". Scrawl "coding standards" on the front in big black marker. Live & breathe it.
A classic
Possibly, but he'd have to be a very good monkey. Many instruction sets and processor architectures are so complex now that writing things the "intuitive" way in assembler does not yield the optimal result. It takes a lot of specialist knowledge to get a good assembly implementation of non-trivial algorithms on such systems, and the best place for those specialists is... writing compilers. IOWs, Joe C Newbie may well get a better implementation of some common algorithm than the average assembler programmer, because Joe has John C Compiler on his side.
As for C against C++, right now, optimising compilers for C do better than for C++, and hence C is faster than C++-used-as-C, though the gap is closing as C++ optimising compilers catch up. OTOH, C++ has much more potential, principally because it supports higher level constructions like exceptions and polymorphism natively, and because templates are a tool for improving efficiency that C will never match. Overall, well-written C++ that uses the language's features effectively is already faster than similarly well-written C, and the gap is widening.
If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.
And processing of large data volumes in a fairly straight forward manner is also very good to do in straight and "simple" code. And C is really good for that. (I'm think eg cryto code, which is both quite math intensive and highly sequential.)
I won't comment on your views on OOP, while I don't agree with them I don't think anything good will come out of the discussion. (The general idea with all higher level languages is however to map the language as closely as possible to the algorithms as possible. It becomes obvious when you compare eg sorting algorithms in different languages.)
Now this is just plain wrong. Perhaps you work mainly will micro-controllers but the types of CPUs that are in a typical PC today are not this easy to use at a low level.
First off,
Second, a typical PC today doesn't even execute x86 asm. It's translated internally to it's own micro code (typically more towards RISC) and executes that. Furthermore it is super-scalar and as such executes several instructions at once. It also moves code around and a bunch of things that will make your brain go numb if you start trying to optimize it by hand. Hell, you can't even access all the registers manually in many cases.
And that's just looking at the instructions. If you start taking the memory hierarchy into consideration then you'll soon find yourself in a world of confusion.
Fortunately compilers don't get confused by this. So the way I see it a big importance in the future will be not only how well a language maps to hardware and how it maps to the "algorithms". Of even bigger importance could be how clear things are to the compilers so they can do a proper job. (And in many cases C is very suitable for this today.)
It depends on the cpu architecture.
For a neat learning experience, try programming the Texas Instruments C6701 family of VLIW DSP's. 8 different execution units allowing you to specifically designate 8 instructions to execute in parallel - depending on the instruction type - 'flying' registers, manually specified wait states - NOP takes a parameter! Multiple busses, 256 bit wide program memory, etc, etc. The TI tools include an 'Optimizing Assembler' which is totally amazing - without it you would end up spending days on a 10 cycle loop plotting out flying register dependancies and opcodes in a 2 dimensional matrix...
--jeffy++
ipv6 is my vpn
Wow, no down-mods as a troll for you yet?
Well, let's pretend you didn't mean to start a "better language" war, and address your points.
Don't even get me started on the 'C is faster than C++ myth'. Only in the hands of an idiot.
MYTH??? Bring it on, Java-boy. Pick any CPU intensive task, and my C code will DESTROY your best Java code for speed.
Java does have its uses, including decent type checking and memory management, as you mentioned. Those carry a cost, however. You don't get something for nothing, and with Java, the cost comes in the form of a performance hit.
most programmers don't have enough training to use a more complex language.
A curious statement... Would you care to give a few examples of more "complex" languages? Particularly with regards to their greater-than-Turing-completeness compared to C?
Or do you mean syntactically, in the sense that you find Brainfuck and Malbolge the epitome of a perfect programming language?
C is really just glorified assembly language.
Yes and no. For the most part, you can trivially convert C to assembler line-by-line, without too much effort. However, the nice neat flow control mechanisms of C makes it almost immeasurably easier to work with than assembler. Don't get me wrong, I do like asm (just can't beat it for speed), but it works best for drop-in function replacements, not for creating the entire framework of a large application.
I think C is only still alive because it is supported on most systems,
Heh... Which, ironically enough, counts as the ONLY reason Java came into existance, and even now that it has "matured", I can build an ANSI-C compatible program on about 6x more platforms than you can run a standard Java app on (whichver "standard" of compatibility you want to pretend exists for Java).
Now go ahead, suck up the humility and grudgingly point out that, since most Java implementations come written in C, you could build a VM on any machine with a C compiler. But that kinda defeats your own point. (And, at least half of those platforms lack the resources to actually *use* the Java VM, although I have to admit I'd like to see someone try using Java on an embedded 68HC05 with 4k ROM and 256 bytes of RAM, if just for the amusement value).
Java has its place. C has its place. Scheme has its place. Perl and Python have their place. Tcl/Tk have their place. I won't pretend people can (or rather, "should", since technically anything the machine can do, you can use C to make it do so) use C for everything, if you don't pretend that C hasn't made it as the single most widely supported and generally hardware-wise powerful language for a reason - Namely, it works, and what it does, it does well. You can try to use a hacksaw to cut lumber, but don't blame the hacksaw for doing the job poorly.
>Java (and other "restrictive" languages) only prevent a small class of errors, e.g., memory leaks.
perhaps you and I should form a PAC on this subject!
so true.
And the funny part is this does not prevent memory mismanagement.
-pyrrho
Jeeze, there's nothing like taking a bunch of idosyncratic preferences and treating them as best practice, let alone enforcing them at a workplace.
I note that Pike doesn't even come close to labeling his piece "Best Practice For C Coding" - for which I thank him.
He does have points that are at least arguably valid, but he lost me at the end.
"Include files should never include files" ?! Pure bull-freaking-crap hogwash. Rather the opposite is the case! Every include file should include every dependency include file. And order dependency of include files is a no-no. It is quite easy to eliminate all order dependencies, and if I have a religious rule, this is it.
I think the best practice of C programming is to use it only when absolutely necessary. Otherwise, use modern language which will boost your productivity and make your applications easier to write and with fewer bugs (and no presence on Bugtraq due to a buffer overflow)
Everyone defends higher level things like Java, C# and other scripting languages on the basis that we should have tons of little dynamically allocated little objects floating around, and that you need to have a beefy memory manager floating around to deal with it.
Why do you have to have tons of tiny objects floating around? All that tiny little allocating over and over again just slows everything down, and probably makes your programs more complex. I say, screw dynamic allocation, just use big virtual memory backed vectors of fixed length structures. It's simpler, more reliable, and faster.
This is my sig.
I believe the most common names are:
() parentheses
[] square brackets
{} curly braces
<> angle brackets (although true angle brackets would have a less acute angle)
That way you can be accurate and people who don't know what the difference is will still understand you.
Java does not elimitate any of these errors, it just relinquishes responsiblity away to the JVM. Supposedly that can make things safer if the JVM is of high quality, but this is not always the case.
In general, programs that `endanger the computer' are system components. It's going to be a while before they are written in Java.
Maybe C coders just don't cotton to "meetups". Maybe newer languages are more likely to give rise to such things. Mayber newer technologies give rise to such things. Trying to have a C "meetup" today might be a lot like trying to re-create the Homebrew Computer Club or Woodstock.
Of course hardware hacking and Jimi Hendrix music still attract a lot of people. They just don't attract them in the same way.
For all intensive purposes, "whom" is no longer a word. That begs the question, "who cares"?
Where did it come from?
Does it mean ANYTHING different from"recommendations?"
Does anyone ever give any evidence that the "best practices" are actually best?
"How to Do Nothing," kids activities, back in print!
"Java is as fast as C++"
"C++ is as fast as C"
"C is as fast as assembly"
tell us some more funny ones santa claus.
I'm not sure if it's easier to write, I suppose it might be for some, but I think it is a lot easier to learn. In order, the first 4 languages I learned were Basic, Assembly, Pascal, and then C.
Why write C when you can write assembler? After all your C compiler can't be as efficient as hand-tuned assembly!
You can draw this efficiency line ANYWHERE -- it is basically the language you learned on that you are predisposed towards and accept the runtime penalty for.
You sound like you learned C first -- mainly because you refer to OOP as crap and everything else as a "bloat-o-language." Realize that there is no one true language. Don't let yourself be defined by the tools you have at your disposal. Generally, you probably used classes in your applications without realizing it, and ironically while patting yourself on the back about not using them. You just didn't use the class keyword.
a complete disregard for effeciency in the face of beauty-of-design
Except that beauty of design often leads to efficiency and simplicity which drastically reduces the number of bugs. Beautiful designs are minimal, yet complete and impose very small overhead for their usage.
Usually there's little regard for how a machine could best handle the problem.
This is a really dangerous statement, almost as if you are justifying writing obscure code in the name of performance. Most C optimizers will do the necessary pointer arithmetic that you pride yourself on automatically. Software is written to be maintained by humans and be used by humans. The computer only executes it. A lot of requirements allow more code clarity over raw performance, and using a higher level language is an important step in that regard.
I am only a college student who is working my 5th or 6th summer doing programming work, and cannot fathom how a 'professional' can make statements like these.
Most languages have some kind of foreign function interface, so they can link to C/C++ libraries. Most languages have good mailing lists where, in some cases, even the designers of the language hang out to answer any questions you have and (if you're lucky) to take on board your suggestions for _improving_ the language.
And nearly all of them are more maintainable than C++ (which, as the saying goes, combines the power of Java with the readability of Brainfuck).
Java? Ruby? Perl? Ada? O'Caml?
In British English, make those
...and guess what...
() brackets
[] square brackets
{} curly brackets.