Hacker News new | ask | show | jobs
by kaba0 1073 days ago
> It gives me a lot of control over how the program works, which lets me create programs that work faster and use less memory than would be possible in most other languages.

While it is true to a degree, I would also add that due to its low level of expressivity, you often have to introduce less efficient solutions simply because language deficiencies. Things like small string optimizations in C++ are simply not possible in C.

2 is true, but it comes at the expense of bad expressivity, see the former point.

3. Well, will it really compile to what you meant? If you have UB, it might still compile but the semantics of your program could change entirely depending on which compiler and which version you use.

Also, your Python point: well, that’s because you used python in the first place, which is very slow even among scripting languages.

4 comments

> Things like small string optimizations in C++ are simply not possible in C.

I don't think this is true, I've seen a bunch of libraries implement SSO in C:

https://nullprogram.com/blog/2016/10/07/

https://github.com/stclib/STC/blob/master/include/stc/cstr.h

https://github.com/mystborn/sso_string/blob/master/include/s...

> due to its low level of expressivity, you often have to introduce less efficient solutions simply because language deficiencies. Things like small string optimizations in C++ are simply not possible in C.

You don't have to use an inefficient solution. You can always roll your own optimized solution or use a library. I agree that C++ has some nice string optimizations built into the standard library, but it's not obvious to me that they're always better than the simplicity and predictability of a simple chunk of memory.

Besides, you generally don't write C code in the same way you write C++ but with more primitive tools. You often allocate a buffer once and operate on it; you don't emulate passing strings by value from function to function, doing lots of allocations and deallocations in the process.

> Well, will it really compile to what you meant? If you have UB, it might still compile but the semantics of your program could change entirely depending on which compiler and which version you use.

I'm not sure what point you're making here. If you have bugs in your program then it may work incorrectly, yes, but that's true no matter the language.

> You don't have to use an inefficient solution. You can always roll your own optimized solution or use a library

That’s not true. You for example can’t write a generic, efficient vector implementation in C - the language itself can’t do that. You either have to copy paste the same code for different sizes, or make use of some monstrous hack of a macro. Instead projects use hacks like conventionally placing the next/prev pointer in structs (linux kernel), and the like.

C++ is the de facto language for high performance computing, so I very much question that “you don’t write C as C++ part”, if anything you don’t write C++ as C as that would be inefficient.

Generic things are rarely efficient, the most optimal code tends to be specialized and tailored to specific hardware and/or the kind of data its operating on.

std::vector (which is a really inefficient way of doing dynamic arrays btw) can be cleanly implemented with macros (see stb stretchy buf) or by splitting the element data from the housekeeping data:

  int append(void *arr, size_t elemsize, size_t *capacity, size_t *size, const void *items_to_add, size_t num_items_to_add);
How is std::vector is inefficient?

Especially that that macro-hack from stretchy buf seems to do it in an even more naive way.

Splitting the element data is a different implementation with very different performance characteristics - it’s quite a bad thing if I have to resort to that due to a language inefficiency, especially in case of a language that is supposedly close to the hardware.

There are various constraints on std::vector because of language in the standard which makes concessions for generic use that might not apply to your application. Small vector optimizations aren’t possible in std::vector, also some operations that could be done in-place can’t be. You also give up control of some meta-parameters and allocation strategies that may be more efficient for your use case.
Six arguments - seriously? Avoiding that is the point of generic programming, and probably more efficient there too
You're talking about something that isn't related to efficiency. Copy and pasting, macros, generating code -- none of these preclude producing an efficient solution.

There is nothing in C++ that is inherently more efficient than C.

Except that more efficient solutions can be implemented much more practically? Solutions that you'd need to bend over backwards for in C?
What does that have to do with efficiency? We don't appear to be debating language ergonomics, but the notion that C is somehow inferior to C++ when it comes to performance.
C++ has a lot of compile time programming features that C cannot do practically. There are sometimes alternatives to those mechanisms in C, but they rely on mangling, macros, non-portable tricks, and so on.

On the topic of performance, the best counterargument to C++ from a C perspective would be that hand rolled code generation isn't all that bad in practice. It's just language theorists don't like that approach aesthetically.

> hacks like conventionally placing the next/prev pointer in structs

This is not a hack, it is the way it should be in C.

Except that that's a linked list and not an array.
You can store your 'list items' in an array and still link to random items in the array - although an index instead of a pointer would make more sense in that case, but what else is an index than a pointer with fewer bits ;) The main advantage being that you don't need to alloc/free individual items.
Great, so now we're writing our own memory allocator?
> I very much question that “you don’t write C as C++ part”, if anything you don’t write C++ as C as that would be inefficient.

Yeah, I was thinking about your string example when I wrote that. For high performance numerical code, I can see the advantages in using C++.

People who know how to use C rarely if ever have problems with undefined behavior. I particularly have written a huge amount of C code and my bugs have never been related to undefined behavior. This is an idea that has been spread to make people even more afraid of using C/C++. While there is a possibility of finding these problems, in practice it is almost a non-issue.
TBF, did you ever run your code with UBSAN enabled? There's a couple of UB cases which don't trigger any bugs until one of the popular C compilers changes some details in their optimizer passes, and which then only manifest with a specific combination of compiler options.
> People who know how to use C rarely if ever have problems with

I think we call this "No True Scotsman".

In real life lots of people write C because they want to or have to and they generate tons of bugs from bug classes that just aren't present at all in other languages.

> People who know how to use C rarely if ever have problems with undefined behavior.

I think the CVE database would disagree with that statement.

Same here, no problems with undefined behavior. Also, no memory issues either after done with code finalization using Valgrind.
“no memory issues in the tested state space”. That’s the only thing Valgrind can say. But it says nothing how a run with different input would behave, it just might segfault/leak/use after free/UB.
That is always the case in any platform. Just because something works on a Mac it will not necessarily work on a PC or vice-versa. If a language has multiple compilers, you also need to test in different compilers to make sure your code works there too. You're trying to make this as a C-only issue, when it is a general issue, maybe with different names.
C is an expressive language when you’re not working with strings and memory the way you do in most HLLs. Almost all operators return a value and can be nested in sub-expressions. Assignments and pre/post increments/decrements are expressions. The comma operator evaluates expressions in the given order and returns a value. There is a GNU extension called “statement expressions”, allowing you to define function-like macros.