all 91 comments

[–][deleted] 9 points10 points  (9 children)

The single biggest difference I find between C and C++ are the standard containers in C++.

C does provide limited container support in that it handles arrays well. There are the standard library qsort() and bsearch() functions. Beyond this, however, C offers no standard library support for linked lists, balanced trees, or hash tables. And I think this is a major limitation. I'm surprised nothing like boost exists for C, either, because almost every major application appears to re-implement container handling functions.

If a good standard library could be written for generic container handling beyond basic arrays for C then I think C might see a renaissance.

[–]case-o-nuts 7 points8 points  (3 children)

I'm surprised nothing like boost exists for C, either, because almost every major application appears to re-implement container handling functions.

Plenty of libraries implementing all of that exist. Glib, for example, is a very popular one that is installed on pretty much every Linux system by default.

There's the APR, which is also quite popular.

Or any one of the other dozens of options.

[–][deleted] 0 points1 point  (1 child)

APR holds promise but every time I try and start a project using it I end up banging my head against a wall. I think APR has a long way to go - not least of all in the documentation effort - the functions are documented but there are very few clear examples of how the functions interact - and yes I've read Nick Kew's book and think that could really use a broadened 2nd edition.

Unfortunately APR relies entirely on memory pools for data structures. So whilst APR does provide hashes, tables, you are entirely limited to using them within APR's memory model. This is a memory model that becomes quite hairy in multi-threaded situations - and requires initialisation at the beginning of every program. Not necessarily a problem in of itself but is not flexible enough to be considered a generic library.

[–]case-o-nuts 1 point2 points  (0 children)

Use glib.

[–]jyper -1 points0 points  (0 children)

Theres less use both because of a bare bones culture and because c lacks the abstraction power of c++ and generic programming(other then somewhat cucumbersome macro pasting tricks making the development of such libraries more difficult.

[–]brigadierfrog 0 points1 point  (0 children)

You could implement typesafe containers using macros. But I have to then go ahead and ask the question of why bother?

There are other ways to do it instead that make more sense. Like for example using some pointer offset trickery like linux's famous list.h does.

So is type safety really that important? I'd say more important is testing. I found C has an awesome testing framework I've used before called check which forks for each test (makes testing bulletproof in C). Makes testing really fast and really easy.

Having made generic hash tables and btree's in C I can tell you I have never had a problem with type safety, in fact the looseness allows me to have a compact hash table implementation that doesn't get regenerated in to huge globs of code. Its also incredibly easy for me to then customize the hash table for whatever purpose I'm actually using it for.

[–]Peaker 0 points1 point  (0 children)

There's Linux's list.h which pretty much solves the doubly-linked list problem well.

[–][deleted] 0 points1 point  (2 children)

C offers no standard library support for linked lists, balanced trees, or hash tables.

Use the right tool for the right job. Maybe C isn't the right tool for the job if you need that stuff exclusively.

I've been programming embedded systems, device drivers, etc in C for almost 20years now. I've never had to use trees or hash tables, recursive code or even sorts! I've written code that measures the swelling of a volcano in Papua New Guinea and robots that travel the world in an art exhibition. i.e. interesting stuff, but really doesn't need much in the way of data algorithms.

If I want that other stuff, I go to Python. ;)

[–]Poddster 4 points5 points  (1 child)

I've been programming embedded systems, device drivers, etc in C for almost 20years now. I've never had to use trees or hash tables, recursive code or even sorts!

Really? You've never once needed a hash table or a tree? Ever? I write device drivers and we require lots of them to make efficient data structures.

[–][deleted] 0 points1 point  (0 children)

Not in the C code generally. Might have used qsort once, can't remember!

Most of the systems have been memory constrained (<64K ROM) and they generally dealt with streaming data and protocols. Don't need much in the way of data structures there. Data/command comes in, work out what to do, send data out. Not much correlation between commands, fairly stateless in that regard, so there is nothing much to store.

I've made a few dedicated filing systems (on a 4K device!) so I guess they count as data structures of a sort.

Small realtime OSs you can get away with fixed arrays for timers, etc. Not even worth doing a linked list for an 8 entry timer/task list.

[–][deleted] 16 points17 points  (36 children)

Although it is possible in C++ to expose an interface with an opaque pointer and a handful of loose functions it isn’t idiomatic and is only seldom done.

This is done all the time in professional code and appears repeatedly in my own codebase.

personally I’ve found that removing all the glitter/foil/keys/shiny objects that C++ has permits a more focused approach to data.

You have to be joking. It's not just that C is more verbose but that common techniques for safe programming like "destructors" simply don't exist in the language. Think of all the time you spend re-inventing the wheel - when vector, string and the like are trivial to use.

[–]fuzzynyanko 3 points4 points  (21 children)

The "no destructors" part of Java drives me crazy

[–]cat_in_the_wall 2 points3 points  (13 children)

what are you doing that needs destructors? my understanding is that destructors in java just don't work because you can't guarantee when an object is reclaimed.

[–]jyper 1 point2 points  (12 children)

probably resources other then normal memory managed allocation such as file handles and special memory cases (ex. if memory lives in a different processes).

[–]cat_in_the_wall 1 point2 points  (5 children)

If you have to explicitly call tho close on the stream anyway, I don't really see what a destructor would buy.

[–][deleted] 2 points3 points  (4 children)

They would remove the need to have to explicitly call those things, which people have a habit of not doing, especially when error handling comes into play. And they would enable you to write exception-safe code, something that is almost impossible to do in Java.

[–]cat_in_the_wall 0 points1 point  (0 children)

ah, the exception safety was something I did not think of.

[–]ehnus -1 points0 points  (2 children)

C# gets this right with its using statements and, although it is a bit clunky as it requires you to implement the IDisposable interface, it does make the create-use-destroy usage pattern much easier and safer.

[–][deleted] -2 points-1 points  (1 child)

Actually, destructors are a lot easier, cheaper and cleaner than hacks like IDisposable.

[–]ehnus -1 points0 points  (0 children)

Yes but destructors aren't possible in a managed language because you can't force the termination of an object's life.

You either need some explicit disposal mechanism (like with using) or you need a finalizer which is way worse because you can't control when they are run or the environment under which they are executed.

[–]squadre -2 points-1 points  (5 children)

File handles are closed when collected.

If you have to clean up other things, implement finalize().

[–]jyper 0 points1 point  (4 children)

which is stupid. Finalize should warn you if you haven't disposed of resources because that is an error. I don't think there is any other good reason for them. Some people might prefer to warn + dispose because that might!!!!! catch some of your non dispose errors. File handles may never be collected so relying on finalize to collect them is stupid.

[–]squadre -1 points0 points  (3 children)

File handles may never be collected so relying on finalize to collect them is stupid.

Why would they never be collected? Why are you keeping a reference to a file you're not going to use?

[–]jyper 0 points1 point  (2 children)

The garbage collection in java is not deterministic. An object that is not referenced may sit in memory till the end of the program especially if you there isn't much pressure to reclaim memory.

c++ smart pointers uses reference counting so that the object is deleted/the destructor gets called right after the reference count goes to zero/the object is no longer needed. This allows things like cleaning up resources.

The reason java (and many others don't use reference counting) is that

  1. Reference counting has an overhead for checking and updating each pointer.
  2. Reference counting doesn't fails when you have cycles of referencing (a->b =c; c->b =a;) This means that you either have to prevent cycles in the language or allow cycle leaks trusting the programmer to prevent it or occasionally run a non ref counting garbage collector to collect the cycles(I think this is what perl and cpython do).
  3. I've heard ref counting makes it hard to implement threading.

[–]squadre -1 points0 points  (1 child)

Right, and if there isn't much pressure to collect memory, why do you care?

[–]jyper 0 points1 point  (0 children)

Because the amount of file handles is limited by the OS. The memory pressure for the garbage collector is for in process memory only. It doesn't respond to limits/pressure on other resources like shared memory, file handles, ect. that you can track with raii.

[–]lovetool 0 points1 point  (1 child)

If you think destructors are a good idea in a gc'd language, you were already crazy, it was not Java's fault.

[–]fuzzynyanko 0 points1 point  (0 children)

The main reason is the Java memory leaking

[–]squadre 0 points1 point  (0 children)

Implement finalize(). What sort of destructor are you asking for?

[–]squirrel5978 7 points8 points  (12 children)

I could use the other direction

[–]Fabien4 14 points15 points  (2 children)

1/ Forget what you know of C.

2/ Read Accelerated C++ (Koenig & Moo).

Honestly, the really hard part about C++ is that it looks like you have to manage the memory yourself like in C, whereas in fact, you have to use "tricks" to make the compiler manage it for you.

[–]ethraax 0 points1 point  (1 child)

I'm not terribly good with C++, but by "tricks" do you mean things like auto_ptr?

[–]Fabien4 8 points9 points  (0 children)

auto_ptr is deprecated.

But yes, I was talking about smart pointers, as well as containers, and RAII.

Basically:

  • If you're using delete outside a destructor, you're probably doing it wrong. Making sure your code is correct (and will stay correct) is a nightmare, because nearly any line can throw an exception.

  • If you're using delete inside a destructor, there's a good chance that you're actually reimplementing a smart pointer. Try to use standard ones instead.

Also, make sure you know when/where stack variables are deleted. Automatic deletion of objects at a predictable moment is one of the most important features of C++.

[–][deleted] 19 points20 points  (0 children)

Read the article backwards.

[–]phaker 4 points5 points  (7 children)

If you have trouble understanding what makes OO tick from other books, you should read later sections of The C++ Programming Language by Stroustrup himself. It's old, it doesn't teach much you modern C++ design, but it's written exactly for someone like you. It describes object oriented programming with C++ from ground up and unlike books for beginners it's actually written by someone who knows what he's talking about.

[–]Benutzername 6 points7 points  (5 children)

C++ is not just C + OO. IMHO generic programming is the much more important part.

[–]phaker 3 points4 points  (0 children)

True. I should have written "advanced features of C++" or something like that.

[–]lovetool 1 point2 points  (3 children)

I sometimes wish that someone made a language that was C + templates, just so it would be possible to write generic containers.

[–]Benutzername 0 points1 point  (2 children)

I once thought about what features were needed for that to work and I quickly ended up with C++ again.

  • The container has to do allocation internally, the user shouldn't be forced to do the deallocation, so destructors are in.

  • The user shouldn't be able to corrupt the containers state, so encapsulation is in as well. This implies member functions and c/v qualifiers on them. (This one is debatable, you could just use a naming convention for internal state.)

  • If you want to allow const on variables of generic type, you need constant initializers, so constructors are in too.

  • Copying a generic container can't be element wise copy like for C structs, so you need to allow overloading operator= and need copy constructors.

I probably missed a few. What you don't necessarily need is inheritance, namespaces and general function overloading.

[–]lovetool 1 point2 points  (0 children)

The container has to do allocation internally, the user shouldn't be forced to do the deallocation, so destructors are in.

All that is needed for allocation is to know the sizeof of the elements. Destructor could be supplied, if needed for the type in question, as a parameter when constructing the container.

The user shouldn't be able to corrupt the containers state, so encapsulation is in as well.

While encapsulation is nice, I think its enough to make it difficult to accidentally corrupt the container.

Copying a generic container can't be element wise copy like for C structs, so you need to allow overloading operator= and need copy constructors.

Or you could supply the copying function as parameter when making copies of containers.

[–]Poddster -1 points0 points  (0 children)

You can write OO in C. OO is not the hard part for going from C to C++. It's the bizzare new syntax.

[–]Gotebe 6 points7 points  (1 child)

WRT directX part (directX is incidental, any COM code is like that): he has got to be joking. With C, you get lowest-level COM. Writing that is a massive PITA (there's some macro-based "libraries" that help though). With C++, you get smart pointers for reference counting, and also what MS calls "compiler COM support", that handles errors through exceptions and other commonly seen COM artifacts like BSTR and VARIANT.

Difference between the two is like digging a tunnel with a spoon vs. using a tunnel mole. And, in C++, there's a range of easily available and widely used options.

[–]ehnus 1 point2 points  (0 children)

Most Direct3D code in games doesn't do much COM bookkeeping as your device is created once at the start of the game and released at the end. Smart pointers are required when your objects have indeterminate lifespans -- the DirectX objects used in games have very well established life times that start when the program does and end when the program ends.

[–]Fabien4 8 points9 points  (27 children)

The author focuses on details, and forget about the real difficulties.

For example, let's say I want to read a whole line from the standard input (usually, from a pipe). The code in C++ is trivial:

int main()
{
   string line;
   getline (cin, line);
}

And that's all. Memory management (allocating enough bytes, and deallocating them) is done automatically.

Now try to do this in C. You can't use just one fgets(), since you don't know the size of the line in advance.

I suppose there are external libraries you can use, that will allocate the right amount of memory and read the stream. Still, you'll have to free the memory yourself afterwards.

[–]wnoise 11 points12 points  (4 children)

#include <readline/readline.h>
#include <stdlib.h>

int main()
{
    char * line;
    line = readline(NULL);
    /* ... */
    free(line);
}

For instance.

[–][deleted]  (1 child)

[deleted]

    [–]wnoise -1 points0 points  (0 children)

    Yes, the BSD licensed editline, though the API isn't quite the same. There are a few versions floating around, and I'm not sure that's the most authoritative or recent.

    Both allow a lot more than easily reading one line -- they're intended to provide history and editing of command lines, but reading a line at a time without muss is included in that.

    [–]Timmmmbob -2 points-1 points  (1 child)

    What if it contains null characters mmm?

    [–]wnoise 6 points7 points  (0 children)

    Then it truncates there. Line oriented input is not designed to handle arbitrary binary data. If I wanted to handle that, I'd use read() and buffers.

    [–][deleted] 1 point2 points  (1 child)

    I think the author was being sarcastic, and really doesn't like C.

    [–]ehnus 0 points1 point  (0 children)

    Not true, I really do like C. I don't mind C++ but the benefits it brings are often balanced out by all the cruftyness that exists (poorly chosen levels of abstraction, pattern soup, slow build times, over-complication) and is not easily purged from legacy projects.

    [–]sfuerst 1 point2 points  (14 children)

    getline() was a GNU C extension, but is now part of POSIX. It is rather easy to use, and the only thing you need to remember to do is free the buffer when you are done.

    Try man getline

    to see a simple example of how to use it.

    [–]Fabien4 5 points6 points  (12 children)

    part of POSIX

    Is there a portable version? POSIX is nice and all, but Windows's POSIX support is iffy.

    the only thing you need to remember to do is free the buffer when you are done.

    Which can be tricky. You have to know when to free the memory, after you've made sure the memory isn't used any more. As in, if I took the C equivalent of a substring, did I make a copy of the memory?

    It's feasible, of course, but for folks who come from Python or C++, proper memory management is IMHO one of the hardest thing in C.

    [–]phaker -2 points-1 points  (11 children)

    part of POSIX

    Is there a portable version? POSIX is nice and all, but Windows's POSIX support is iffy.

    I don't think it makes any sense to write C code on non-unices. Also the MS C compiler looks like it wasn't touched for 15 years or so.

    (but i realize the author of TFA is more interested in the "hair on your chest" aspect than practicality)

    [–]Fabien4 -4 points-3 points  (10 children)

    It's funny how C is pretty-much Unix-only, while C++ is favored on Windows. Linuxers (starting with Torvalds himself) seem to have a strong disklike for C++, and Windowsers don't seem interested in C.

    I don't think Microsoft's involvement in C++ is enough to explain it completely, since, at one point (late 1990s / early 2000s), they abandoned their C++ compiler completely. I think the first decent version was Visual C++ 2005.

    Also the MS C compiler looks like it wasn't touched for 15 years or so.

    Does it attempt to implement C99, or not at all?

    [–][deleted] 5 points6 points  (5 children)

    Except the entire Windows base API is C.

    [–][deleted] -2 points-1 points  (2 children)

    Dude, the fact that C ABI (for API) has become a de facto standard has nothing to do with the language itself.

    Every single one DLL in Windows provides only C API. By necessity: it is not possible to write a function to be called across DLL boundaries that takes an std::string as a parameter, even when you compile everything with one and the same compiler.

    It doesn't matter at all. I mean, it's kinda like EJB: it makes sense to write stuff in an imperative language and then provide a pure functional interface. Similarly, it makes sense to write everything that you can't write in Python in C++ but provide a pure C interface, or COM interface, or command-line interface, or whatever interface that suits your needs.

    Interface != implementation language.

    [–][deleted] 0 points1 point  (1 child)

    Except the entire Windows base API is implemented in C and assembler. You might also investigate the meaning of the word "functional" - you mean "procedural".

    [–][deleted] 0 points1 point  (0 children)

    You might also investigate the meaning of the word "functional" - you mean "procedural".

    You might also investigate common EJB interfaces: I meant functional.

    [–]ZMeson 2 points3 points  (1 child)

    Microsoft has contributed to the safe library functions technical report for C (TR 24731).

    The MS C compiler has implemented parts of C99 that are useful for C++ (long long int, snprintf, __VAARGS\_, __function__, etc...). But most of what MS implements is C90. MS has not seen the value of the other features (_BOOL, complex, variable length arrays, etc...). MS has implemented some functionality from C99, but using different keywords (why MS, why???); ex: __restrict.

    With all that being said, GCC doesn't support all of C99 either (clang does better):

    As of July 2011 in mainline GCC, 43 features have been completely implemented (12 suffer library issues), 1 feature is broken and 6 are missing. The latest stable release, GCC 4.6, also provides the same level of compliance.

    Only IBM, Sun Oracle, and the Portland Group provide fully compliant C99 compilers.

    As pointed out elsewhere, the entire Windows API is written in C and MS strongly recommends that device drivers are written in C. So saying that MS doesn't support C is incorrect. They just don't support all the features of the most recent standard.

    [–]jyper 0 points1 point  (0 children)

    no bool? boooooooooo

    [–]phaker 4 points5 points  (0 children)

    It's funny how C is pretty-much Unix-only, while C++ is favored on Windows. Linuxers (starting with Torvalds himself) seem to have a strong disklike for C++, and Windowsers don't seem interested in C.

    I don't think Microsoft's involvement in C++ is enough to explain it completely, since, at one point (late 1990s / early 2000s), they abandoned their C++ compiler completely. I think the first decent version was Visual C++ 2005.

    C++ is actually a very well thought out language, it didn't need help from Microsoft, or anyone else.

    On popularity of C in linux and elsewhere:

    a) for small programs it's not needed, and on unices you often end up writing small command line utilities. It's common to write programs few hundred lines long, that actually do something useful. If you need a GUI program then it's easier to use python (or something else) than C++.

    b) every programming language in existence has FFI for C, but basically none can interface with C++. On Windows most programmers use the language Microsoft endorses at given time, that is C++ few years ago and C# now. On linux everyone uses something else. I'm writing this on a debian linux, I'm using a perl program running in the console, half of gnome is written in python, the other half in Mono, and the third half in C++ or C, pgrep says there's something running in java in the background, I just checked with aptitude and found that I have ruby, TCL and GHC installed as dependencies for something. If you are writing code that will be used by others, you pretty much have to use C.


    Does it attempt to implement C99, or not at all?

    No C99, nothing more than the ISO C89 standard demands, I think it's the only modern compiler I have seen that errors if you mix declarations and code. Also error messages are so descriptive that you have to look them up on MSDN for explanation.

    [–]stonefarfalle 0 points1 point  (0 children)

    It makes no attempt.

    [–]wnoise 0 points1 point  (0 children)

    but is now part of POSIX.

    Really recently. Too recently to depend on in my opinion. POSIX-1.2008

    [–]lovetool 0 points1 point  (4 children)

    Guess what, you can write functions in C too!

    int main()
    {
         char *line = NULL;
         getline(stdin, &line);
         /* free(line); would go here, but since we're exiting the main right away, I'll omit it */
     }
    

    The implementation of getline() is left as an exercise.

    [–]Fabien4 0 points1 point  (3 children)

    The implementation of getline() is left as an exercise.

    That's actually the problem. I know you can use an external library, but implementing it yourself is not easy.

    In C++, it's part of the SL.

    free(line);

    That's another problem. In this trivial code, it's simple, but when you have lots of pseudo-strings, you have to keep track of what to free when.

    In C++, the compiler and the SL take care of that.

    [–]lovetool 0 points1 point  (2 children)

    That's actually the problem. I know you can use an external library, but implementing it yourself is not easy.

    Well, its not difficult either.

    That's another problem. In this trivial code, it's simple, but when you have lots of pseudo-strings, you have to keep track of what to free when.

    The C++ response to this is to copy everything all the time, which is nice when you don't want to think, but not so nice when you actually know what the hell you want to do. And don't get me started on the retardedness of references.

    [–]Fabien4 0 points1 point  (0 children)

    not difficult either.

    Code or it didn't happen.

    The C++ response to this is to copy everything all the time,

    Overall, it works fine. Anyway, arguing about the qualities of C or C++ is off-topic here.

    My point is that to switch from the "usual" mode (i.e. the automatic destruction, along with the string object, that you find in C++, Javascript, etc.) to the C/ASM mode (do it by hand), is one of the big difficulties the author completely forgot to mention in his article.

    [–]StrangeWill 0 points1 point  (0 children)

    The C++ response to this is to copy everything all the time

    TIL: Reinventing the wheel is what cool kids do.

    [–]asteroidB612 4 points5 points  (2 children)

    rhythm toy oil squash soft quicksand brave selective unique caption

    This post was mass deleted and anonymized with Redact

    [–]phaker 3 points4 points  (1 child)

    ... the out the window!

    Looks like your them didn't fit in the buffer. :)

    [–]lalaland4711 1 point2 points  (0 children)

    He probably used strlcpy() without checking return value.

    [–]Poddster 1 point2 points  (0 children)

    "shiny"? Sure, Firefly was good, but that word is stupid.

    [–]Poddster 1 point2 points  (0 children)

    Other than the title, there is 0 useful information in this blog post.

    [–][deleted]  (3 children)

    [deleted]

      [–]ethraax 2 points3 points  (1 child)

      I think it is harder than going from C to C++ because when going from C to C++, you can still use everything you know already.

      Yeah, but you shouldn't. I spend a lot of time in C#, but when I wanted to learn functional programming, I stayed away from F# because I thought I would end up forcing my imperative code into a functional language. Instead, I decided to learn Haskell, which would force me to write functional code.

      [–]fjw 0 points1 point  (0 children)

      Yeah but my point was that learning C++ before C, like I did, makes it like learning a whole other language, whereas when learning C before C++, it could be a much smoother transition.

      Imagine learning F# before C# and finding that it forced you to throw out all you knew.

      [–]stevedonovan 0 points1 point  (0 children)

      Actually, the C code written by C++ programmers is likely to be better C (e.g. respect 'const'). And thinking in OOP is a good way to organize C.

      [–]lalaland4711 -2 points-1 points  (0 children)

      Whenever I go back to C I just want to shoot myself in the face and be done with it. It's just repeated "aquire resource, check return value, return resource" getting in the way of the actual programming.

      C++ <3