tags:

views:

522

answers:

10

Recently i had a discussion with my boss (a long time C developer) who discouraged me in using C++ streams and stick to "good old" printf & friends. Now i can understand why he is saying this and believe me i did not follow his advice.

But still this is bugging me - are there things in C that are still better in some cases than newer C++ implementations of the same/similar thing? By better i mean for example performance, stability or even code readability/maintainability. And if so, can someone give me examples? I'm mainly talking about similar differences like printf/streams, not about features like inheritance or OOP for that matter. The reason why i'm asking all this is that i consider myself a C++ developer and as such I always try to code the C++ way.

+4  A: 

When you're writing C++, write C++. When you're writing C, write C. Whoever says different is probably uncomfortable with the differences, or thinks of C++ as a "better C". That isn't the case; C++ is its own language with its own features, and is mostly C-compatible for the sole purpose of easing conversion.

cHao
the sole glaring exception of course being IOStreams, which really does suck. I can't blame anyone for preferring `printf`.
jalf
@jalf: OK, i'll give you that it's slow. It's also easy to use the same code to write to a file, or the console, or some other stream that compresses/encrypts/encodes/whatever, without special support from the OS -- or even an object's having to know where it's "printing" to. But the normal case should definitely be faster.
cHao
It's slow, it's verbose as hell, it's not obvious how to use it, and it's downright painful to extend. Yes, it *can* be extended, but ordinary mortals aren't able to. I'm not saying that `printf` is a particularly nice solution either. Just that given that IOStreams is broken on so many levels, I don't blame people for looking to C for alternatives.
jalf
@jalf: I always thought that IOStreams were excellent. I mean, I've never run a performance comparison, but they seemed pretty intuitive and easy to use for me, and the one or two operators can do virtually all iostreams. Compared to using format strings and .. ewwww.
DeadMG
@cHao: It's not just about performance. C++ iostreams are just painful to use compared to C's printf/scanf family of functions. Just one example: one function hidden away somewhere might change `cout`'s fill character to an 'X'. Now, every function everywhere else that specifies a width format, also needs to be absolutely sure to set the fill character back to ' ' (space); otherwise `cout << setw(6) << "foo";` might produce `XXXfoo`, not what was intended. That becomes real tedious real fast.
Dan Moulding
(The following is just a joke, don't take it too much seriously): In fact, people should start thinking about C++ as a worse C plus OO features...
ShinTakezou
@ShinTakezou: I've already been thinking about C++ that way for several years now ;)
Dan Moulding
+4  A: 

As far as performance goes, I used to be a USACO competitor. I quickly found that 98% of one of my programs' runtime was spent using C++ IOStreams. Changing to fscanf reduced the overhead by a factor of ten. Performance-wise, there's no contest at all.

Borealid
so much?! interesting!
ShinTakezou
+2  A: 

I think C style is better when you need raw memory management. It is a bit cumbersome to do that with C++ constructs and you don't have realloc() for example.

Someone who down voted that, probably never tried to explore the topic.

doc
downvoted after having messed 2 whole months debugging memory leaks.
Alexandre C.
@Alexandre C. what does your memory leaks have to my answer? You can still have memory leaks in C++, don't you? And I am focusing on completely different topic.
doc
+1 @Alexandre C: Poor reason to downvote; the memory leaks were caused by humans not C malloc implementation.
DRL
@doc: No, you have to be an exceptionally poor programmer to leak dynamic memory in C++, since RAII frees it automatically for you. As for raw management, C++ can use and call every function in the CRT. Technically, a C++ compiler isn't required to provide this sort of function, but in reality, virtually all do.
DeadMG
@DeadMG Technically, it is required to do so.
anon
Btw `realloc` is mentioned in the book `C++ Coding Standards - 101 Rules ..` (by Herb Sutter and Alex Alexandrescu) as a worst-case example in rule #5 "Give one entity ONE cohesive responsibility". So code which uses realloc is most certain not very readable.
smerlin
@Neil: Really? I thought that a C++ compiler was required to provide very, very few functions out of the CRT. In any case, I've never seen a C++ compiler that doesn't provide the whole CRT (usually and then some).
DeadMG
@doc "It is a bit cumbersome to do that with C++ constructs ": not at all, you have RAII, and you *don't never never never write new and delete yourself* (or you get pretty quickly damned into hell)
Alexandre C.
@Alexandre C. WTF RAII has to that? You are misplacing conceptions.`you don't never never never write new and delete yourself (or you get pretty quickly damned into hell)` - so the standard library was written in the hell as well as many garbage collector libraries, memory pools etc and I was in there too since I was experimenting with new and delete.
doc
@doc: once the hell is securely placed into classes (including some you may have to write), like smart pointers, vectors, allocators and the like, you don't need to micromanage memory by hand. C++ is all about delegation.
Alexandre C.
I think the "you have to be an exceptionally poor programmer to leak dynamic memory in C" too... If C++ makes it harder, this does not mean C++ makes you a less poor programmer anyway
ShinTakezou
C memory management is OK in C, but in C only ! - DONT use C memory managment in C++ or the first exception will most likely cause havoc.
smerlin
+9  A: 

C printf()-style output is typically faster than C++ ostream output. But of course it can't handle all the types that C++ output can. That's the only advantage I'm aware of - typically, because of aggressive inlining, C++ can be a lot faster than C.

anon
How come `printf` is faster? It has to interpret the format string at runtime, whereas streaming into `cout` has no such overhead...
FredOverflow
@Fred: because most dumb developers use `endl` too often
smerlin
Writing to `cout` has a whole other kind of overhead -- a "stream" actually ends up being at least two classes (the stream itself and a streambuf) passing data back and forth, generally having to copy it a couple of times. And parsing a format string is a lot easier than it sounds; basically you just have to search for % chars.
cHao
@Fred I'm just reporting my experience.
anon
@cHao Buffered streams in C do their own share of copying.
anon
The thing with `endl` is really common, most people think it behaves the same as `\n`. I'm used to write `\n` in c++ stream output though :)
PeterK
From experience, C style output often is faster. While C++ could take advantage not having to parse the format string, seems like the C++ version often still doesn't take advantage from it.
Axel
printf is just calling a func, which parses the fmt (not too much work) and "gets" (which usually is just a "get a value from the stack") args according to it. Then, everything "streams" to the buffered mechanism behind the scenes. In C++, `cout << something` is a call to an overloaded method. After the data passed the clogs of the object-orientation, the "final part" could look "similar" to the final printf part. So if we avoid in both cases to _flush_ the buffer (invented to make output faster...) without reason, C++ should be always a bit slower.I think it makes no the diff in this case.
ShinTakezou
I correct myself: instead of "a bit slower" I would put "slower". And "I think it makes no diff in many cases" instead of what it there now. (But this also means: where it makes difference, stick to printf!)
ShinTakezou
On the other hand, `std::ostream` allows to write to many different outputs while `printf` is especially directed at the screen, the functionality isn't equivalent either.
Matthieu M.
A: 

I sometimes prefer pointers and memcpy over iterators and std::copy when I don't need generic code.

Same for iostreams, they are convenient and extensible but there are a lot of situations when [f|s]printf / scanf are as simple.

Alexandre C.
Do you know that some STL implementations optimze and use memcpy in std::copy for primitive types? ;)
Marcus Lindblom
last time I checked on my favorite STL implementation, it was not the case... Actually the point is that memcpy is what I was used to when I was doing C, and it is kind of a reflex. And most people know C, don't they ;) ?
Alexandre C.
+1  A: 

Good old C! Ah, the pre-ANSI days... <sarcasm>I certainly miss having practically no type checking on arguments and returns values or having the compiler assume anything untyped is an int and not an error.</sarcasm>

Seriously, though - there is a fairly good argument against using exceptions as error handling. I read a fairly decent argument against exceptions for system level work and mostly I think the problem is that you can't simply read a block of code and know it won't throw in C++, whereas you can read most C and say "all the errors (at this level) are trapped" or "the ones that aren't don't matter".

plinth
Put it in a try catch(...). Now you know that it hasn't thrown.
DeadMG
+1  A: 

I couldnt give you a conclusive answer; however i found this rather dated comparison interesting.

http://unthought.net/c++/c_vs_c++.html

DRL
+8  A: 

There is one thing that C programmers sometimes point out and that is worth considering: If you stay away from macros, then it's mostly obvious what a line of C code does. Take for example this:

x = y;

In C, this is an assignment and only an assignment. The value of y is (after a possible conversion) copied into x.

In C++ this could literally mean anything.

  • A simple assignment,
  • a user defined conversion operator in y which deletes the internet and returns a value that is of the same type as x
  • There is a constructor which makes an object of x's type from y, after melting down a nuclear power plant. This value is assigned to x.
  • There is a user defined assigment operator which allows assignment from a bunch of other types, for which y has a conversion operator or which are in some other ways obtainable from y. The assignment operator has a bug which might create a black hole, because its a part of the LHC operation software.
  • more of the above.

To make it even more interesting, every single operation might throw an exception in C++, which means that every line must be written in a way that it can rollback what it changed, which is sometimes hard when you can't say what a line actually does. And to make it worse, your program might crash instantly, because the exception happens because the assignment is called during a exception unwind. In C++ things tend to become "vertically complex", which poses its own requirements to the capabilities and the communication skills of the developers.

Luther Blissett
I've mixed feelings on this argument. If you're going to assume malicious code, then tell me how C code like this is tolerated: `foo(0)`? What if `foo` is a function which deletes the internet, melts down a nuclear power plant *and* creates a black hole?Just like a C programmer assumes that a function call will do what it says on the box, why shouldn't a C++ programmer be able to assume that the assignment operator will perform an assignment, and nothing else?
jalf
@jalf: Absolutely agreed. Overloaded operators are no different to any other user-defined function in terms of the potential to not do what they say on the box.
DeadMG
+1 LOL-upvote;)
el.pescado
@jalf: A C programmer knows that foo(0) might destroy the moon and x=1 might probably not, he can easily partition a program in "safe" leaf areas (which do not contain function calls) and unsafe "branch" areas (which do require knowledge of what the functions do and what can go wrong with them) and he can tell from the *syntactic features alone* if he is in a leaf or branch part. You can't do that with C++ anymore, almost every expression might invoke a branch to a function in a long forgotten library...And bang! Ctuhluh eats our souls!
Luther Blissett
+1 However, some of the reasons you mention are exactly why programmers dont name stuff `x` and `y`, and instead have "clever" naming standards that supply extra info about what the names are representing.
MattBianco
@MattBianco: Absolutely. Which is why giving user-defined functions generic, non-descript names such as `=` is a really, really bad idea.
Dan Moulding
+1 Great answer. I've never actually really considered that difference in any great detail - although it is quite obvious when pointed out.
David Relihan
+1 and +1 to the comment because of itself, and because of Ctuhluh!
ShinTakezou
Since declaration and assignment may allocate memory, this argument rings true for anyone who has written interrupt handlers when any allocation could corrupt your heap.
plinth
+1  A: 

I dont think using printf style functions generally over iostreams is justified. iostreams just greatly speed up development time and debugging time, and are much less error prone (e.g. think of buffer overflows, wrong % type specifiers, wrong number of arguments ... and the biggest problem is that the compiler cant help you at all). And if you dont use endl when it isnt needed, cout isnt that much slower than printf.

So generally you should go with C++ iostreams, and only if profiling shows that critical sections take too much time because of iostream calls, then optimize those sections with C style functions, but make sure to use the safer versions of the functions like snprintf instead of sprintf.

Examples:

Consider you have a int foo variable, which you printf in a number of places, later during development, you realize you need foo to be a double instead. Now you have to change the type specifiers in every printf style call which uses foo. And if you miss one single line, welcome in the land of undefined behaviour.

Recently i had a case where my program crashed because i missed a simple comma, and because of the great printf-style command, my compiler didnt help me: printf("i will crash %s" /*,*/ "here");. This wouldnt have happened with iostreams either.

And of course you cant extend the behaviour of printf and friend to work with your own classes like you can with iostreams.

smerlin
gcc at least will check printf-style arguments and issue a warning, preventing these kinds of defect.
reece
when you generate the format string programmatically, it won't.
Alexandre C.
additionally gcc wont issue warnings for format strings of type `extern const char*`, when the string is defined in separate translation unit - even if the string is defined as a literal. (and all format strings in the legacy app i have to work with, are declared as `extern const char*` -.-)
smerlin
+1  A: 

Where using C++ features might be problematic:

  • portability: IMHO C is still more portable
  • mixed language programming: calling a C function from another language is almost never problematic, with C++ you quickly get in trouble because of name mangling etc.
  • performance issues: features like templates may lead to code bloat, temporary object creation may have a huge impact too, etc...
  • maintainability: Since C++ is more complex than C, Restrict use to language features you expect the person who is later maintaining your code to be capable of.

However, some/most of C++ features are quite handy and useful if used with care. Remember the saying "With C++ it's harder to shoot yourself in the knee, but if you do, it will cost you the entire leg".

Axel