8 ms·
1. 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 ot
by pkkm 3y ago
1. 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.
2. Relatedly, it's more explicit than almost any other language. If a line of code doesn't look like a function call, it's not calling anything. There is no hidden control flow. These statements are not true in languages which support operator overloading or exceptions. The only real competitor to C here is Zig.
3. If I give a Linux user the source of a C program, they can probably compile it with the tools they already have. This will most likely be the case 20 years from now too, as long as I keep my C mostly standard-compliant. I'm not sure that code in newer, faster-moving languages like Rust will stay compilable as long.
4. It's a lingua franca. C libraries can be used from most programming languages without too much effort.
I probably wouldn't start a large project on a tight deadline in C, but I think it's a great language for writing new command-line utilities and for rewriting tricky algorithmic code from scripting languages. I've gotten 100x and even 1000x speedups from replacing a couple of Python functions with C.
The ease of use is about to improve with the C23 standard, which I'm very happy with. On the other hand, some tricky areas like aliasing are likely to stay tricky forever.
- kaba0 3y 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.
- camel-cdr 3y ago> 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://nullprogram.com/blog/2016/10/07/ https://github.com/stclib/STC/blob/master/include/stc/cstr.h https://github.com/stclib/STC/blob/master/include/stc/cstr.h https://github.com/mystborn/sso_string/blob/master/include/sso_string.h https://github.com/mystborn/sso_string/blob/master/include/s...
- pkkm 3y ago> 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.
- kaba0 3y ago> 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.
- xyzzy_plugh 3y agoYou'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.
- 3y ago
- coliveira 3y agoPeople 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.
- tejuis 3y agoSame here, no problems with undefined behavior. Also, no memory issues either after done with code finalization using Valgrind.
- kaba0 3y ago“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.
- coliveira 3y agoThat 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.
- flohofwoe 3y agoTBF, 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.
- oblio 3y ago> 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.
- bluetomcat 3y agoC 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.
- sgt 3y agoWith C23 there's the #embed feature. Might be super useful for embedded software. How are the toolkits e.g. for ESP32 and TI in terms of C23 compatibility?
- pkkm 3y ago> How are the toolkits e.g. for ESP32 and TI in terms of C23 compatibility? C23 hasn't been released yet so it's hard to talk about compatibility. It's probably going to take a few years before it's widely supported.
- creata 3y ago> If a line of code doesn't look like a function call, it's not calling anything. Why is that important to you?
- deely3 3y agoI did not touch C for 20y but its make code easy to read and understand. And helps debugging and error searching more easy task.
- flohofwoe 3y agoIt's important for reading other people's code. When I see a function call then I know that "anything" can happen inside that function, so I better investigate. For anything that's not a function call it is obvious what happens under the hood. In languages like C++ I potentially need to check every operator if it is overloaded, and find the place where that happens (I think I haven't seen any IDE support to help with 'resolving' overloaded operators, but maybe that has improved in the meantime).
- deleted 3y ago[deleted]
- c_crank 3y agoSome of the worst bugs I have experienced are ones where code is executing without it being clear where it is executing. The front end stack is the most awful about this, where at any given moment all kinds of things might happen without notice. A clear, sequential program can be stepped through and understood.
- deleted 3y ago[deleted]
- marcosdumay 3y ago> These statements are not true in languages which support operator overloading I guess I will never understand the C and Java developers incredible fear of operator overloading. Do you have the same reaction to user-defined functions? Because they are exactly the same thing. Is it because of the bad type system that won't let you know what operator you are using?
- chongli 3y agoI guess I will never understand the C and Java developers incredible fear of operator overloading. The answer is in the sentences right before the one you quoted: Relatedly, it's more explicit than almost any other language. If a line of code doesn't look like a function call, it's not calling anything. There is no hidden control flow. Consider the use-cases for C: operating system kernels, hard real-time software, low-level libraries, databases, embedded software. What is a common desire among these? Predictable low-latency and high throughput. It's much easier to achieve these features if your language does not allow "magic." Implicit allocations, RAII, exceptions, overloaded operators; these are all examples of features which allow a library-writer to inject hidden control flow into your code. This can make it very difficult to analyze why code runs slowly or with unexpected random pauses, not to mention making it much harder to step through in a debugger.
- marcosdumay 3y agoThe control flow is the same; you evaluate the parameters, and then evaluate the operator. Just like any other function call, there's nothing implicit or hidden. The only difference is that you can't create other operators with the same name for different types. And whether something is called or run inline is always decided by the compiler. Modern C doesn't promise you any relation between the way you break down your functions on your code and the actual function calls on the assembly it generates. So, I keep seeing people complaining about overloading; always with the same reasons; that are patently not valid unless there's some implicit assumption they keep not stating. What is that assumption that breaks the equivalence between user-defined functions and operators?
- jrvarela56 3y agoThank you for the input, one point that stood out for me was that you prefer to write command-line utilities with C. Why is that? I use scripting languages mostly in my day work (Ruby and some Python bc AI) and have found my productivity using command-line utilities is amazing with Ruby. Do you do it bc of performance, ease of use bc you are proficient, a mix of both or something else?
- pkkm 3y agoOh, I don't use C for every command-line utility. If it's something IO-bound like parsing a webpage and downloading a bunch of files linked from it, I write it in Python. The convenience of modules like argparse and requests is very hard to beat, and it would take me a lot longer to do it in C. I reach for C when performance matters, for example when processing multi-GB files or looking for perceptual hashes that are similar. It can be a difference between minutes and hours of running time.
- mighmi 3y ago> The only real competitor to C here is Zig. Why only Zig?
- pkkm 3y agoIt's the only language I'm aware of that takes C's explicitness and pushes it even further: it bans some implicit conversions, and it makes you pass an allocator as an argument to functions which can allocate memory. Most languages choose to go the other way and introduce features like try/catch and operator overloading.
- ntfAX 3y ago[dead]
- chlorion 3y ago>If a line of code doesn't look like a function call, it's not calling anything. In C, if you for example write past the bounds of an array or otherwise do something that causes UB, there is no guarantee that the code you wrote in the source file is actually going to be what's ran. If an attacker can clobber the stack (for example), the control flow you see in the source code and the actual control flow of the program are not the same. In the worst case, an attacker can get your program to execute arbitrary code of their own choosing! Maybe some consider this unrelated to the no implicit control flow thing, but I think when UB caused by a trivial mistake can alter your control flow, you have much bigger worries than an operator being sugar for calling a function. I consider UB and arbitrary code execution exploits to be a case of implicit control flow!
- josefx 3y ago> If a line of code doesn't look like a function call, it's not calling anything. except maybe allocating dynamic arrays, floating point ops if those don't exist in hardware. Then you have signal handlers that can be called on math errors, segmentation faults, ... . So basically every line in your code can implicitly call a function. > If I give a Linux user the source of a C program, they can probably compile it with the tools they already have. What is win32.h and why is it missing? > This will most likely be the case 20 years from now too What is Xlib.h and what do you mean I have to rewrite the apps front end from scratch?