Learning C when you already know C++?
I think I have an advanced knowledge of C++, and I'd like to learn C.
There are a lot of resources to help people going from C to C++, but I've not found anything useful to do the opposite of that.
Specifically:
- Are there widely used general purpose libraries every C programmer should know about (like boost for C++) ?
- What are the most important C idioms (like RAII for C++) ?
- Should I learn C99 and use it, or stick to C89 ?
- Any pi开发者_如何学Ctfalls/traps for a C++ developer ?
- Anything else useful to know ?
There's a lot here already, so maybe this is just a minor addition but here's what I find to be the biggest differences.
Library:
- I put this first, because this in my opinion this is the biggest difference in practice. The C standard library is very(!) sparse. It offers a bare minimum of services. For everything else you have to roll your own or find a library to use (and many people do). You have file I/O and some very basic string functions and math. For everything else you have to roll your own or find a library to use. I find I miss extended containers (especially maps) heavily when moving from C++ to C, but there are a lot of other ones.
Idioms:
- Both languages have manual memory (resource) management, but C++ gives you some tools to hide the need. In C you will find yourself tracking resources by hand much more often, and you have to get used to that. Particular examples are arrays and strings (C++
vector
andstring
save you a lot of work), smart pointers (you can't really do "smart pointers" as such in C. You can do reference counting, but you have to up and down the reference counts yourself, which is very error prone -- the reason smart pointers were added to C++ in the first place), and the lack of RAII generally which you will notice everywhere if you are used to the modern style of C++ programming.- You have to be explicit about construction and destruction. You can argue about the merits of flaws of this, but there's a lot more explicit code as a result.
- Error handling. C++ exceptions can be tricky to get right so not everyone uses them, but if you do use them you will find you have to pay a lot of attention to how you do error notification. Needing to check for return values on all important calls (some would argue all calls) takes a lot of discipline and a lot of C code out there doesn't do it.
- Strings (and arrays in general) don't carry their sizes around. You have to pass a lot of extra parameters in C to deal with this.
- Without namespaces you have to manage your global namespace carefully.
- There's no explicit tying of functions to types as there is with
class
in C++. You have to maintain a convention of prefixing everything you want associated with a type.
- There's no explicit tying of functions to types as there is with
- You will see a lot more macros. Macros are used in C in many places where C++ has language features to do the same, especially symbolic constants (C has
enum
but lots of older code uses#define
instead), and for generics (where C++ uses templates).
Advice:
- Consider finding an extended library for general use. Take a look at GLib or APR.
- Even if you don't want a full library consider finding a map / dictionary / hashtable for general use. Also consider bundling up a bare bones "string" type that contains a size.
- Get used to putting module or "class" prefixes on all public names. This is a little tedious but it will save you a lot of headaches.
Make heavy use of forward declaration to make types opaque. Where in C++ you might have private data in a header and rely on
private
is preventing access, in C you want to push implementation details into the source files as much as possible. (You actually want to do this in C++ too in my opinion, but C makes it easier, so more people do it.)C++ reveals the implementation in the header, even though it technically hides it from access outside the class.
// C.hh class C { public: void method1(); int method2(); private: int value1; char * value2; };
C pushes the 'class' definition into the source file. The header is all forward declarations.
// C.h typedef struct C C; // forward declaration void c_method1(C *); int c_method2(C *); // C.c struct C { int value1; char * value2; };
Glib is a good starting point for modern C and gets you used to concepts like opaque types and semi-object orientation, which are common stylistically in modern C. On the other end of the spectrum standard POSIX APIs are kind of "classical" C.
The biggest gap in going from C++ to C isn't syntax, it's idiom and there, like C++, there are different schools of programming. You'll write fairly different C if you doing a device driver vs., say, an XML parser.
Q5. Anything else useful to know?
Buy a copy of K&R2 and read it through. On a cost per page basis it'll probably be the most expensive book on computing you'll ever buy with your own money but it will give you a deep appreciation for C and the thought processes that went into it. Doing the exercises will also hone your skills and get you used to what is available in the language as opposed to C++.
Taking your questions in order:
- Unfortunately, there's nothing like Boost for C.
- Nothing that's really on the order of RAII either.
- The only compiler that tries to implement C99 is Comeau.
- Lots of them all over the place, I'm afraid.
- Quite a bit. C takes quite a different mindset than C.
Some of those may seem rather terse, but such is life. There are some good libraries for C, but no one place like Boost that they've been collected together or given a relatively uniform interface like Boost has done for C++.
There are lots of idioms, but many of them are in how you edit your code, such as sort of imitating RAII by writing an fopen()
and a matching fclose()
in quick succession, and only afterwards writing the code in between to process the data.
The pitfalls/traps that wait around every corner mostly stem from lack of dynamic data structures like string and vector, so you frequently have to write such things yourself. Without operator overloading, constructors, etc., it's considerably more difficult to make them really general purpose. Lots of libraries have them, but you end up rolling your own anyway because:
- the library doesn't do quite what you want, or
- using the library is more work than it's worth.
The difference in mindset is almost certainly the biggest thing, at least for me. When I'm writing C++, I concentrate almost all my real effort on designing the cleanest possible interfaces, and I tend to treat the implementation of an interface as almost throwaway code. For the most part, I don't plan on making minor tweaks to that part of the code -- as long as the interface is good, replacing the entire implementation is usually easy enough that I don't worry about it much.
In C, it seems (at least to me) much more difficult to separate the interface from the implementation nearly as thoroughly or cleanly. As such, I tend to spend a lot more time trying to implement every part of the code as cleanly as possible, because later changes tend to be more difficult and throwing away and replacing pieces that aren't very good is substantially less likely to work out very well.
Edit (since people have raised questions about C99 support): While my statement about lack of C99 support may seem harsh, the fact is that it's true.
MS VC++: supports C95, and has a couple C99 features (e.g. C++ style comment delimiters), mostly because C99 standardized what they'd previously had as an extension.
Gnu: According to C99 Features Status page, the most recent iteration of gcc (4.4) has some C99 features, but some (including VLAs) are characterized as "broken", and others as "missing". Some of the missing "features" are really whole areas, not individual features.
PCC: The PCC site claims C99 conformance only as a goal for the future, not as a present reality.
Embarcadero Technologies (nee Borland) don't seem to say anything about conformance with C99 at all -- from the looks of things, the last time they worked on the C compiler may well have been before C99 was even released.
Microsoft openly states that they have no current plans for supporting C99, and they're not going to even consider it until VS 2010 is released. Though I can't find any public statements about it, Embarcadero appears about the same: no hint of a current plan, and nor even that they're going to consider working on it anytime soon.
While gcc and pcc both seem to have plans, they're currently just that: plans. They both openly admit that at the present time, they aren't really even very close to conforming with C99.
Here's a quick reference of some of the major things you'll want to know.
This is advice you didn't ask for: I think most potential employers take it as a given that if you C++ you know C. Learning the finer points of C, while an interesting academic exercise, will IMO not earn you a lot of eligibility points.
If you ever end up in a position of needing to do C, you'll catch on to the differences quickly enough.
But don't listen to me. I was too lazy and stupid to learn C++ :)
Anything else useful to know ?
C99 is not subset of c++ any revision, but separate language.
Just about the biggest shock I had when I went back to C was that variables are defined at the function level - i.e. you can't scope variables inside a block(if
statement or for
loop) inside a function.
Except for very few cases, any C code is valid C++, so there isn't actually anything new you should learn.
It's more a matter of unlearning.
Not using new, not using classes, defining variables at the beginning of a code block, etc.
In a strict sense, C++ is not object-oriented, but it's still procedural with support for classes. That said, you are actually using procedural programming in C++ already, the most shocking change will be not having classes, inheritance, polymorphism, etc.
As C++ is almost a superset of C89, you should know just about all of C89 already. You probably want to concentrate on the differences between C89 and C99.
精彩评论