8 ms·
People who still write C, honest question: Why? C is full of quirks. From cryptic "undefined behaviors" to a type system that isn't really a type system (more
by babarock 3y ago
People who still write C, honest question: Why?
C is full of quirks. From cryptic "undefined behaviors" to a type system that isn't really a type system (more like "size hints for the compiler"), the language doesn't feel easy to use/debug. Add to this CPP macros, a universally recognized bad idea, a clunky import system, and lack of a single reference implementation of the compiler/libC, and you have a language that is harsh to defend.
Also, documentation is all over the place. If a function isn't described in `man`, I have no idea where else to actually look for it.
I used to think "C presents the most honest representation of the low-level mechanisms of the computer", but... even this is shaky. I've been programming for almost 15 years now, and I don't think I've ever seen a computer where memory is actually a continuous array of bits sorted by memory address. The C representation of memory (and all the pointer arithmetic) is not a real representation of your hardware, and this too is an abstraction.
So, setting aside the need to maintain 30+ year old code, what would be modern reasons to start a new project in C?
- aneeshpoth 3y agoThe last time I wrote C was for my OS class
- zabzonk 3y ago> I don't think I've ever seen a computer where memory is actually a continuous array of bits sorted by memory address well, that is not the C memory model. C does not allow you to access bits in memory directly. maybe you meant bytes? or words? if so, many cpus have exactly that architecture.
- heywhatupboys 3y ago> C does not allow you to access bits in memory directly. of course it does what are you talking about?
- zabzonk 3y agobits are not addressable in C and are thus not directly accessible.
- jacquesm 3y agoThey are also not normally directly addressable by the CPU, you'll have to do some combining and splitting with separate instructions. Some CPUs are better at this than others.
- messe 3y agoI wouldn’t quite count it as bit-addressing, but x86, for example, can load bits directly into the carry flag using the BT instruction which can take a register or memory address as it’s first argument, with the bit being given as the second.
- jacquesm 3y agoThere have been all kinds of variations on that theme. One of the nicest is 'bit test and set' as an atomic instruction, that one enables a whole raft of nice stuff.
- pm215 3y agoTangent: some Arm Cortex-M class CPUs had a feature called "bit-banding" where you could do byte accesses to an area of the address map and the CPU would turn these into bit accesses to a different part of memory. So the alias word at 0x23FFFFFC maps to bit [7] of the byte at of RAM at 0x200FFFFF, for example, and you can do a word write to 0x23FFFFFC to change just that bit 7, saving having to do it by hand (which is particularly awkward if you need to ensure the atomicity of the bit update). https://developer.arm.com/documentation/100165/0201/Programmers-Model/Bit-banding/About-bit-banding https://developer.arm.com/documentation/100165/0201/Programm...
- messe 3y agoNot the commenter you're replying to, but I suspect what they mean is that the C memory model is byte-addressable not bit-addressable. You can't point/refer to a specific bit in memory, instead you have to first read the byte and then select an individual bit using bitwise operations, much like most modern processors.
- heywhatupboys 3y agoThat has nothing to do with the C memory model, but how the CPU is structured. No modern CPU has an interface for bit-address accessing as far as I am aware... C makes no assumptions about the size of a byte
- Kamq 3y agoC doesn't really know about bytes. It has chars, but I believe there are some constraints on char, specifically, they have to be big enough to hold the ASCII charset. (I'm pulling real deep here, someone correct me if I'm wrong)
- nitrix 3y agoC11 3.6p1 byte "addressable unit of data storage large enough to hold any member of the basic character set of the execution environment"
- messe 3y agoIf I remember correctly, it assumes the size of a char is greater than or equal to seven bits, and a char is defined to be the smallest addressable unit. C does not support bit-addressing.
- nitrix 3y agoThe width is defined as CHAR_BIT >= 8 (C11 5.2.4.2.1p1). The size, sizeof (char), is always 1.
- marcus0x62 3y agoI wouldn't consider accessibility (via masking & shifting or struct bit fields) to be the on the same order as the byte-level addressing you get with pointers.
- qsort 3y agoBecause everything speaks C. If you write a library in C, it can be easily exposed to a variety of high-level languages and platforms. You might argue this is more a property of the C ABI than of C itself, but unless the project is large enough that it's worth doing it in C++ or Rust instead, it's still a very reasonable choice. Also not everything is web. Sure, if you're writing API endpoints in C you're just shooting yourself in the foot, just use Python or Ruby or Go and call it a day. For things like embedded it's often your only reasonable choice.
- moffkalast 3y agoSo we're gonna be stuck writing a precambrian prototype language till the end of time because there's so much legacy code already written in it? Never seemed to stop people moving from Pascal, or Perl or literally all other languages that are now obsolete. I really hate how for microcontrollers the only two choices are either C++ or Micropython, I mean how about some fucking middle ground instead of two polar opposites? At least eventually everything will be rewritten in Rust I guess.
- Philip-J-Fry 3y ago>I really hate how for microcontrollers the only two choices are either C++ or Micropython There's TinyGo as well. https://tinygo.org/ https://tinygo.org/ I'd say that's the middle ground for me.
- rcarmo 3y agoIt is nice, but nowhere near as complete feature-wise than C/C++. The fact that it exists does not mean you can use it to achieve the same thing.
- Philip-J-Fry 3y agoWhat do you mean no where near as complete feature wise? Go or specifically the TinyGo implementation? Seems to do exactly what 99% of people need.
- WanderPanda 3y agoIt‘s the only language supported by basically all platforms, microcontrollers, GPUs, web-browsers. Although that is also almost true for C++ nowadays. I‘m also curious which memory model would be superior in your opinion?
- archerx 3y agoI like making things (air quality monitors, web nfc login, automated garden, power monitor and etc) with microcontrollers like the Raspberry Pi Pico, the only real choices are C/C++ or some flavor of Python. I really do not like Python, it rubs me the wrong way for some reason and also I can find libraries for all the components/sensors in C/C++. It's not so bad. Manipulating strings is a pain in the ass so everything becomes a char and managing types is so annoying, especially dealing functions that could easily take an int or float, you either have to make a template or different versions of the function for each type. This makes me appreciate dynamically typed languages a lot. Those two issues are the only problems I seem to have, everything else has been easy and breezy Besides those two things it's pretty nice. My code is a bit verbose because I'm not that great at it but I'm sure I could reduce the lines of code in my projects (the biggest one has 4000+ lines of code, but it does a lot) by using structs and more loops, but that's mostly a skill/experience issue.
- mytailorisrich 3y ago> The C representation of memory (and all the pointer arithmetic) is not a real representation of your hardware, and this too is an abstraction. By and large memory is a contiguous array and the C representation closely matches what is actually happening, so I am curious about which platforms you have worked on.
- adwn 3y ago> the C representation closely matches what is actually happening It really doesn't, though. Although your CPU might present system RAM as one contiguous array of bytes to your program, the C compiler follows different rules – see strict aliasing and other pointer dereference rules. For example, the following is Undefined Behavior and your C compiler may or may not generate the assembly you expect: int x = *(int *)0x1234568; Your CPU would happily execute the equivalent machine instructions and load from address 0x12345678, while a C compiler is free to replace your entire program with return 0;
- zabzonk 3y ago> and load from address 0x12345678 and most likely seg fault, or similar
- adwn 3y ago1. If the CPU lacks an MMU and the address falls into an accessible address space, it won't segfault. 2. If the CPU has an MMU, it won't segfault if the address is mapped to an accessible region of memory. 3. This is besides the point, because the CPU will execute the instruction and attempt to load from that address. A C compiler might emit the load instruction, or it might assume that this code branch will never be executed and can therefore be replaced with code that sends an angry email to your mother.
- xscott 3y agoThe original author was talking about hardware not behaving like linear memory, and other than caches and maybe some thread local tricks, I'm not sure what he meant. However, it seems pretty clear that CPUs do try really hard to make: mov rax, qword ptr [0x12345678] do what you think it would/should. And as for the C memory model, aliasing, and optimizations, I'm firmly in the camp that thinks the standards originally gave the compiler writers an inch to work on weird platforms and they've taken a mile when they work on reasonable ones. The intent of your integer to pointer cast is very clear, but it's been undefined to insanity. So now there is some variant of the following, which doesn't have UB but does the exact same thing less clearly: uintptr_t i = 0x12345678; int* p = 0; memcpy(&p, &i, sizeof(int*)); int x = *p; I'm sure some language lawyer will correct me on some obscure detail of the standard, but it could be fixed with some modification. The point to me is that using memcpy instead of pointer casts is NOT an improvement. The good compilers will generate the same code as the assembly above, so all they've done is made the C source less readable.
- lnsru 3y agoExisting C examples from semiconductor vendors do not allow other languages. Ok, C++ is also used, but that’s it. So it’s no brainer taking available drivers and building logic around them. That’s current state in embedded development. Client does not pay for use of modern languages.
- rcarmo 3y agoExtremely minimal runtime, portability, and very low overhead when compared to other languages. I have a tiny statistics daemon that scrapes /proc and sends out multicast packets, and it builds and runs on everything from ARMv5 to Xeons, barely showing up on any kind of resource meter and with an absurdly small binary size. I considered rewriting it in Go a couple of times but just didn’t see the point.
- blix 3y agoI needed to improve perfomance of some numerical computations in an existing Python script. The only choices felt like C and Fortran. I tried Rust at first but went back to C when I realized I was spending more time appeasing Rust than solving the actual problem, which wasn't really complicated enough to gain significant benefit from Rust's features.
- anonymous_sorry 3y agoIt's more that it's the most honest representation of the assembly/machine code. We can't really get closer to the hardware than the interface the CPU offers, and C then sticks pretty close to that (or a subset of it, I suppose). It's the simplicity and power of C that I find attractive. I don't write it professionally at the moment, but I enjoy it. It's obviously not the right tool for the job most of the time for the reasons you give, but I miss its elegance. I am a big fan of rust, but it's massive compared to C. I'd like to explore Zig some day.
- badsectoracula 3y agoI don't write C as much as i used to but i still write a lot of it, including new code. The reasons are: 1. C is relatively simple. Sure, not as simple as it could be (e.g. compared to something like Oberon-07) but in the grand scheme of language things, it is far on the simpler side of the spectrum. I can write a C parser relatively easy if i want to for example (and at some point years ago i did that to transpile a C project to C# to run under Sony's PSM platform that was based on Mono and allowed only C#). 2. Undefined behavior is annoying as it can break previously working code with newer versions of the same compiler (though language lawyers playing word games like the code already being broken are way more annoying - the code did the thing i wanted previously so as far as i am concerned it was not broken), but this is something that aside from "obvious" things (accessing invalid memory) i can probably count in my fingers the times i encountered in practice (i write "probably" because right now i can't remember any case, but i've being writing C for more than 20 years). Valgrind and Ubsan help with these so they are not much of a practical concern. 3. I find CPP macros to actually be very useful and a feature that a) i'd actually like expanded instead of being stuck in the 80s (let me store some state or have a loop, FFS) and b) were available on languages too (Free Pascal is a language i also use and does have some C-like macro support, which is more than what you'd find in other languages but still not to the same extent as C). D's mixins essentially being ubermacros are a thing that i liked with that language but sadly their stance on breaking things is something that kept me away from it. 4. A C compiler is available on pretty much everything that can compute things - or at least on pretty much everything i might think on targeting with C anyway (and chances are there are multiple C compilers instead of just one). If not, i can probably write a compiler myself - it'd be rather simple and not that great but i'd be more likely to finish it than a compiler for some other language. 4b. Very related, so it gets a "4b" instead of 5 :-P, but there are a bunch of IDEs and editors that "understand" C. I like IDEs, i like syntax completion, i like semantic highlighting, i like being able to easily rename an identifier, etc and C being easy to parse (see #2) means it has a lot of those. Let me correct that, i don't "like" IDEs, i love IDEs. 5. Most modern computers might not technically be like how C presents them to be, but they're close enough where any differences only matter if you're trying to perform microoptimizations to your microoptimizations - at which point you'd most likely be using a combination of compiler-specific heuristics and assembly code anyway. 6. In most systems where that'd be a concern, the C ABI is pretty much stable or at least there is a stable C ABI, allowing any code written in C to be usable by other languages as well as shared libraries to be able to expose an ABI that will remain backwards compatible and usable by other languages. Of course other languages can do that but they pretty much always do it through a C-fication of their APIs. 7. C compilers - even those that perform a dangerous (see #2) number of optimizations - tend to be very fast. I hate waiting the computer to finish doing things so i tend to prefer languages with fast compilers. 8. While i don't (always) need to maintain 30+ year old code, i do have existing C code that (seemingly, see #2) works and i don't see a reason to waste time rewriting that code in some other language. Even if it'd be broken chances are it'll be faster to fix it than rewrite it. 9. I am comfortable with C. For me being comfortable with a language important because it lets me focus on the thing i'm trying to use the language for instead of the language itself. There might be other stuff i forgot, but the above should give you an idea why i personally write C. Though note that i don't see as any sort of perfect language, there are a lot of things i'd like it to do better - including the type system you mentioned as well as the compile-time code evaluation i wrote above, be it via CPP or by some other means - but it is good enough.
- xvedejas 3y agoI target microcontroller platforms, some of which only have a single compiler, usually some patched 20 year old version of GCC. The only possible alternative to C would be something that transpiles to ANSI C, given that some of these platforms don't quite have full C99 support.
- davidhs 3y agoIt's kind of like English. English is in some sense a simple language (grammar), a poor mixture of other languages and its orthography is not good (not an elegant language), but everyone speaks it.
- rightbyte 3y ago> Add to this CPP macros, a universally recognized bad idea I don't think is not a bad idea. You can't solve language incompatibilities in the language it self. Textual macro languages solves this nicely. CPP is what makes C and C++ work for projects aimed at multiple platforms or compiler vendors.
- yakubin 3y agoYes, you can. Two approaches: 1. Multiple implementations providing a unified interface, selected by the build system. Aka the Henry Spencer approach: <https://www.usenix.org/legacy/publications/library/proceedings/sa92/spencer.pdf https://www.usenix.org/legacy/publications/library/proceedin...> 2. Less-bad macros, e.g. cond-expand: <https://weinholt.se/articles/cond-expand-and-ifdef/ https://weinholt.se/articles/cond-expand-and-ifdef/>
- dzaima 3y agoOption 1 only works if there is a sensible unified interface, and if you feel like spending the time making that for what could be just one line per target. And it just won't work for things that don't really "have an interface", i.e. conditionally adding an __attribute__((optnone)) to a function that a specific compiler version gets stuck in an infinite loop optimizing, or macros that expand to some _Pragma-s that apply to a loop following it for controlling unrolling/vectorization if available, or managing custom inlining configurations for functions based on the optimization/debug levels, or defining a type as either 32-bit or 64-bit depending on requirements, or redefining all printf & fprintf usages to something mingw-friendly. Many of those could be solved by some other means, but C macros neatly encompass all of those.
- dzaima 3y ago> cryptic "undefined behaviors" It's not really that cryptic (aside from like strict aliasing, but -fno-strict-aliasing). There's some UB that might be considered unnecessary/too strict, but it still makes sense in its own right, and, if understood, is quite powerful, and leads to a bunch of neat optimizations. > the language doesn't feel easy to use/debug If debugging at the assembly level, stepping by instructions, it's actually quite nice (despite what everyone says about it not mapping well to hardware, in my experience there's still a pretty clear & immediately obvious correspondence between each C thing and assembly subsection, and vice versa) > CPP macros, a universally recognized bad idea I don't know, they're quite neat for things I have to do. Sure, a turing-complete compile-time language would be nice (I'm not saying that sarcastically, I even use a DSL for writing SIMD that is exactly that!), but it'd add a ton of complexity to mapping C source to assembly. > Also, documentation is all over the place. If a function isn't described in `man`, I have no idea where else to actually look for it. Use of the standard library grows less and less significant as the size of the C project grows. Besides that, cppreference.com has pretty much everything. And yeah, as others have said, a linear sequence of bytes is still a thing every CPU presents. Yes, there's cache & whatnot, but there's like precisely no way to usefully map that to any user-controllable/visible thing, because it's pretty much not user-controllable and intended to be invisible (and varies across all hardware).
- Hirrolot 3y ago> Sure, a turing-complete compile-time language would be nice I wrote Metalang99 [1] as a compile-time language that is able to perform loops, recursion, etc. It's not Turing-complete though, as the C preprocessor is not Turing-complete. [1] https://github.com/Hirrolot/metalang99 https://github.com/Hirrolot/metalang99
- sharikous 3y agoHonestly because I don't want to learn another language. And because most of the world uses C for low level stuff. You can say that Esperanto is a much better international language than English but what good does it do if nobody speaks it?
- idlewords 3y agoVeering off topic, there is a great rant about why Esperanto is a horrible international language: https://web.archive.org/web/20110515155117/http://www.xibalba.demon.co.uk/jbr/ranto/ https://web.archive.org/web/20110515155117/http://www.xibalb...
- Typhon 3y agoJustin Rye's site is now at http://jbr.me.uk/ http://jbr.me.uk/ (and the espe-ranto at http://jbr.me.uk/ranto/ http://jbr.me.uk/ranto/ )
- EternalCarnage 3y agoHistory. You do what your operating system vendor does. Not few operating systems have a C interface. The implementation of binaries (see also application binary interfaces) depends on the operating system. Shared libraries (e.g., DLL) are binaries, too. C compiler developers have the ability to generate consistent[1] binary outputs. In simpler terms, vendors of these compilers can reach a consensus on how to convert C code into binary files, known as Application Binary Interfaces (ABI). It is not uncommon[2] to have a foreign function interface in C. 1. http://yosefk.com/c++fqa/defective.html http://yosefk.com/c++fqa/defective.html 2. https://learn.microsoft.com/en-us/cpp/dotnet/calling-native-functions-from-managed-code?view=msvc-170 https://learn.microsoft.com/en-us/cpp/dotnet/calling-native-...
- falcrist 3y agoMicrocontrollers exist. Their libraries are written in/for C. The programs running on them are small and need tight, efficient memory management. I also like the minimalist nature of the language itself. I get that for desktop applications, you usually want more integration with the operating system so you can say "I want a window here and a button here" rather than having to manually build the window from scratch, but that's not something that's a concern in most embedded systems. I'm operating in a world of voltage inputs and outputs, memory mapped devices, registers, flags, and timings... with almost nothing between me and the hardware. A simple language makes a lot of sense here.
- pjmlp 3y agoAre the Arduino and ESP32 microcontrollers? Hint, might check their libraries/SDKs before answering.
- bathMarm0t 3y agoDon't think I'm too crazy but last time I checked: 1. Yes they are microcontrollers. 2. Yes they use C/C++. (check the libraries/SDKs, 1 layer under the hood it's all .h/.cpp files, and most of the arduino calls are just #defines)
- anovikov 3y agoSmall binary and a toolchain that's small and older than most programmers using it, and known to be bug-free. Top tool if you want to write something that a real human being can "get under the hood of" and understand throughout. As for low-level, sure that's no longer the case. It was a low-level language for K&R and their PDP-11 where they could tell precisely what will be assembler code for each line of their C code and how many CPU cycles it will take. That's no longer the case indeed.
- silvestrov 3y ago> toolchain [...] known to be bug-free You cannot be serious. "Well known list of bugs" would be more in line with the state of affairs.
- lordnacho 3y ago> I used to think "C presents the most honest representation of the low-level mechanisms of the computer", but... even this is shaky. I've been programming for almost 15 years now, and I don't think I've ever seen a computer where memory is actually a continuous array of bits sorted by memory address. The C representation of memory (and all the pointer arithmetic) is not a real representation of your hardware, and this too is an abstraction. It's true that almost nothing works the way it's presented: the computer doesn't necessarily actually do the instructions you specify, it does its machine commands that are compiled. It also doesn't necessarily even do them in the order they are specified. The memory isn't actually a big continuous space, it's mapped as virtual memory. The actual memory isn't used in that way either, there's a hierarchy of NUMAed caches between the CPUs and the actual memory. But it's a useful abstraction. Partly because a lot of the above things are built so that the abstraction works. But also because we want it to look that way, and it's kinda natural to let programmers imagine a virtual machine that works that way.
- TheOtherHobbes 3y agoWhy do programmers in 2023 need to imagine a virtual machine (basically a PDP-11 from 1970-something) at all? You only need that abstraction if you're doing low level bit/byte bashing and I/O, or there's some chance you may run out of memory and need to handle that manually. That applies to a tiny slice of all possible applications. There are far more useful modern abstractions that don't need to make those assumptions.
- lordnacho 3y agoYeah that's true, and that's why people don't use C for stuff that isn't close to the metal. If you're just serving some web page you can just think about the business logic and a higher level language will deal with the rest for you. But someone's got to write drivers and someone's got to write the thing that connects the higher levels to the metal.
- Joker_vD 3y ago> basically a PDP-11 from 1970-something That PDP-11 from the seventies had ADC/SBC (addition/subtraction with carry) in its instruction set, the result of MUL was twice the size of the inputs (i.e., multiplying two ints produced a long), and DIV produced both the quoitient and the remainder. None of that is visible from C and yet people keep clamoring that "C is close to the metal". Bah, humbug: while " * p++" and " * --p" idioms translate directly into an addressing mode particular for PDP-11 — most other architectures don't have autoincrement/decrements — there is no specific support for " * ++p " or " * p--" in the machine itself.
- _fizz_buzz_ 3y agoI use C for microcontrollers. I think Rust is making some inroads, but the libraries/tooling is not there yet.
- synergy20 3y agoneither does the IDE tools I feel, it's going to take a while, and Rust has been here for 17 years.
- aldanor 3y agoIDE tools like what? LSPs for Rust are on par / better than that of C/C++, partially because of language being stricter, no #include nonsense etc. Unlike C/C++, sane build system and dependency management system that are universally agreed upon actually exist. What exactly is "going take a while"?
- estebank 3y agoI would assume debugger support. Rust is in a tough spot because a lot of code gets compiled away, and debuggers need to understand some Rust-isms for good experience, like enum support. I don't think this is an insurmountable situation, though.
- c_crank 3y agoPeople universally agree that replicating NPM's dependency hell was a good idea?
- Lapha 3y ago>it's going to take a while, and Rust has been here for 17 years. Technically correct, but Rust was changing significantly from version to version prior to the 1.0 release some 8 years ago, notably the green thread runtime was removed.
- benreesman 3y agoWith all respect I think there’s a kind of false dichotomy implicit in your comment. The availability of new tools with significant advantages over the old tools is almost always a reason to consider the new tools for certain use cases, but the new tools are rarely just strictly better on literally everything, there are generally now use cases when you say “the new tool is a solid fit here” and other cases where you say “the old tool still hits the sweet spot better”. And that’s before you consider massive existing code and infrastructure and and tooling and investment: which is very, very often a far higher order bit than C vs not-C. A great example would be a JVM-caliber GC? Thats just such a win over malloc/free so often, but it doesn’t obsolete malloc and free across the board: it gives a thoughtful and mature team a whole new set of options. Rust would be a (comparatively) recent example of a language that hits a lot of the sweet spots of e.g. C/C++ and brings some cool new stuff to the party, and might even represent a better default these days, but the idea that it strictly crushes them in full-stop everything is a political-style conversation not a reasoned engineering tradeoff conversation. Even C++ which has been around forever and is give or take backwards compatible with C with good tools? Hasn’t obsoleted C. More options is generally a good thing (there are exceptions).
- jjgreen 3y agoLongevity. As sure as eggs is eggs, reasonable C that I write today will be compilable in 30 years time. Python? Breakage every couple of minor versions.
- pkkm 3y ago1. 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.
- 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.
- 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.
- pravus 3y ago> C is full of quirks. From cryptic "undefined behaviors" to a type system that isn't really a type system (more like "size hints for the compiler"), the language doesn't feel easy to use/debug. I guess because I just don't agree with this viewpoint at all. I've been writing C on and off for over 20 years now and I simply haven't encountered the amount of distress and pain that I see others deal with, especially when related to memory handling or undefined behavior. I wrote a piece of software in Win32 C for a gas integration company many years ago that did tons of string manipulation to recalculate reports coming out of another piece of software. It even included a custom built on-disk database which basically ended up being my own version of BDB. Scratch that, I wrote this software twice because my first version was lost in a disk crash and I had to hex dump the database format to recover my original implementation. Last I recall that software ran at that company for over a decade and probably helped them make millions in revenue. I didn't have a single support ticket and to be honest the last time I talked to the owner I thought they had just stopped using it. I was very surprised that they were still very happy with it and it was working fine. That's just one of many examples of projects I've built or debugged in C. I've regularly been able to fix issues in OS drivers, large projects like Asterisk, and things like deadlocks in toolkit-based GUI programs. It's actually easier for me to use C than most other programming languages because it's clearer to me what should be happening, especially when dealing with anything systems-related. That's just my experience. I totally get that others don't share that same experience but to be honest I'm pretty tired of seeing all of the confused hatred for C.
- habibur 3y agoAdding that anything I wrote in C++/MFC at that time is now obsolete. Everything I wrote in C/Win32 is as much fresh as it had been 30 years back.
- pjmlp 3y agoWhile I undertstand the sentiment, MFC is still being maintained, and is in fact still the only C++ GUI framework worth using, being shipped in Visual Studio latest (2022).
- 3y ago
- mhaberl 3y agoThere are a few reasons: The Lindy effect. You can run C code from anywhere. There are places where it is much easier and better to run C code than anything else. All of these are related to how long C has been around. I think that's also the reason why we use JavaScript extensively.
- enriquto 3y ago> People who still write C, honest question: Why? Because loops are fast. I do scientific computing, where many people use python nowadays, and a few years ago it was matlab/octave. These languages feel "cramped" because they artificially force you to program in a certain way in order to avoid loops. While such a "vectorial" notation is often useful, many algorithms are better expressed using a loop notation, and C does not impose an artificial distinction between the two notations: both are as fast as they can be. The fact that python is not an appropriate language for low-level numerical computation is evident when you notice that most numeric algorithms in python are just interfaces to code written in other languages (C, C++ and Fortran). Of course, C is not the right tool for the job either... Modern Fortran is, objectively, the ideal language for low-level numerical computing: it has native multidimensional arrays and a lot of other goodies, which C lacks. Julia would also be a nice alternative, and I check it regularly. But I find the current interpreter too quirky. I would love to see different interpreters/compilers for this lovely language!
- kaba0 3y agoC has no in-built way to deal with SIMD, which is essential for high-performance computing over loads of data. On that count alone it is already out of the game.
- ok123456 3y agogcc had emitted simd instructions since the egcs days.
- kaba0 3y agoSo does JS, Java, whatnot. That’s not the point.
- xyzzy_plugh 3y agoWhat are you talking about? "in-built"? Have you ever written SIMD assembly before? It's comically easy to integrate SIMD optimizations into a C program.
- flohofwoe 3y agoQuite simply there haven't been any candidates so far which both got the "essence" of C and had the momentum to actually replace C. Zig looks like the most promising so far, if they don't fuck up on their way to 1.0 (disclaimer: I switched back from C++ to C as my language of choice for writing libraries ca 2017, but also continue to write C++ (if necessary to talk to C++ libs) and a lot of Python and Typescript for simple cmdline tools and web stuff, also ObjC on Mac of course for talking to system frameworks, in recent years dabbled with Rust, Odin and Nim, in the long distant past also with C#, Java, Lisp and some Forth, and eventually hope to transition over to Zig for the stuff I currently use C for (maybe in 3..5 years?) TL;DR: use the language that suits a problem best, and C is a very good tool to have in any language toolbox, because it can usually provide a solution where other languages have to give up or just become to much of a hassle (for various reasons)
- TheLoafOfBread 3y agoBecause FreeRTOS is written in C.
- jokoon 3y agoYou had many answers. You don't really start a project in C unless you target limited hardware or some low-level library that can be embedded in other things and interact with other language that can make us of C-style APIs. C became the "new assembly", meaning it sort of replaces the role assembly had. The chips that are sold are not programmed in assembly, because they're sold with a C compiler target directly. C is more than a programming language, it's an universal glue, so it often makes sense to use C because it gives access to everything. It's like english: you can't expect to use esperanto just because it's a superior language. Programming languages are the same. Disclaimer: I mainly use python and C++.
- deleted 3y ago[deleted]
- synergy20 3y agoC can be coded much safer as long as I don't code in 'odd' ways, e.g. trying to be really smart with it. By following common-sense coding rules it seems pretty safe to me. Like it or not, C might still be the most widely used language after 50 years, it will not go away, instead, future AI code review tools, static analyzers, more powerful compilers will evolve fast to make C safe and alive. Why, the price to replace it will be much higher in practice, it might simply be impossible.
- Keyframe 3y agoIT's an old war-time friend that when battle is up we both know how to shoot and be effective at it - both towards enemies as well as our feet.
- msla 3y agoBecause there's no easier way to access various libraries. Yes, that library does things with pointers the new language can't prove are safe. It's been used for longer than you've been alive and it isn't changing. If a new language can't express what it's doing, well, the library isn't going to move, the language is. Therefore, I either have odd shims and contortions or I have C. I await a Buzz Language to eventually have "inline C" the way C has inline assembly.
- Icathian 3y agoBecause most of the projects I want to work on are in C. Postgres, the Linux kernel, lots of legacy systems stuff. All the foundations of our field are in C, so that's what I use when I want to contribute or study them.
- Patrickmi 3y agoSometimes Safety suffers performance
- markus_zhang 3y agoFor me I use C because it's the de facto system programming language for botb Linux and Windows. Another reason is that C grammar is simple (but has a lot of quirks I do admit.)
- zer8k 3y agoProfessionally I do Python. From my experience the breakages occur due to an over-reliance on libraries to do trivial tasks. Do you find a different case?
- markus_zhang 3y agoUnfortunately I'm not professional enough to answer this question. I use C to learn system programming only and I never had the capacity to look at the kernel.
- c_crank 3y agoWriting C code is fun and enjoyable. C programs are typically fast due to the use of primitives and low overhead. C's set of tools and abstractions typically forces you to think about how best to implement a particular data structure or interface, which is the kind of problem I most enjoy. >I used to think "C presents the most honest representation of the low-level mechanisms of the computer", but... even this is shaky. I've been programming for almost 15 years now, and I don't think I've ever seen a computer where memory is actually a continuous array of bits sorted by memory address. The C representation of memory (and all the pointer arithmetic) is not a real representation of your hardware, and this too is an abstraction. Pointers are an abstraction, but they are less abstract than most languages simply assuming there is just one giant sheet of memory to take from.
- aap_ 3y ago> People who still write C, honest question: Why? It was my first programming language and I still think it's a simple and fun language. Also many things have a native C interface so it's a natural choice in those cases. It's certainly not the only language I use, but for many things my default. What's nice is that I don't have to consciously think much about the language when I use it because I know it well.
- cdelsolar 3y agoI am working on a translation of a game engine from Go to C with another coder. One of our end goals is to make it easily available via WASM in a web browser. As to why work in C - it’s incredibly fast, it feels very powerful as long as we manage memory correctly. We use fsanitize, which is an amazing library that can find memory leaks, buffer overruns, etc etc and run it on all unit tests. I think fsanitize is essential to have in your tool belt if you’re doing any C programming at all. A pretty direct translation from Go to C resulted in about a 125% speed up (ie the C code was 25% faster) and this was already very optimized Go code with no allocations. From Go to WASM the results were disappointing to say the least - WASM was about 32% the speed of Go and not at all easy to multithread (and a gigantic file). From C to WASM I got a much better 79% of native speed - would have wanted a little bit more, but this is much more doable, and we haven’t begun to optimize some parts of this engine yet. And Emscripten seems to have very good pthread support, which I will try soon.
- sneed_chucker 3y agoMany people still write C because tons of crucial software, probably things you use every day, are written in it and that software needs to be maintained and improved.
- torstenvl 3y ago> From cryptic "undefined behaviors" to . . . [the] lack of a single reference implementation of the compiler/libC, and you have a language that is harsh to defend. I think you're confused, because this is internally incoherent. In single reference implementation languages, all behavior is undefined behavior. Undefined behavior is just behavior for which there are no requirements imposed by the international standard. It's an unbounded form of implementation-defined behavior. Undefined behavior does not mean that the behavior is completely unpredictable. It does mean you should read your compiler's documentation (including tweaking what happens with certain common UB). For example, if you want signed integer overflow to always wrap, and you read the GCC or Clang documentation, you'll know to use -fwrapv. If overflow could cause catastrophic failure and the program should abort if it happens (e.g., Therac-25), you'll know to use -ftrapv. There's nothing wrong with writing to an arbitrary memory address, either, if you've read your documentation and that's how your environment communicates with a particular I/O port.
- variadix 3y agoIt’s a very simple and explicit language that is easy to write high performance code with and can be used as high level, portable assembly which integrates easily with actual assembly due to a simple and stable ABI. It compiles extremely quickly, its tooling is mature and robust, and you can write it for any platform and do basically everything with it because it is a lingua franca where almost everything has a native API that uses the C ABI. C’s type system is lacking, I wish it was more strict, and sometimes I wish it had some features from C++ (operator overloading for mathematical types, templates for generic programming) and features of other languages (multiple return values especially), but overall I’m okay with its limitations and have become used to working around them. Sometimes I compile C code with a C++ compiler just to take advantage of stricter typing, templates, etc. but for a lot of projects this isn’t a necessity.
- 634636346 3y ago> So, setting aside the need to maintain 30+ year old code, what would be modern reasons to start a new project in C? C code written today will still be runnable 30+ years from now, and likely on whatever platform you're using, unlike code written in some flavor of the month language. C is standardized, has been ported to every architecture, and is easy to port in general, and there's so much code that's already been written in it that the inertia behind it is virtually insurmountable. I've invested significant time in other language ecosystems (like Perl, coincidentally also on the front page) only to see them eventually declared "uncool" (however productive) and killed-off by faddish HN types. But I'm confident they won't have similar success against C. C is the real Hundred Year Language: http://www.paulgraham.com/hundred.html http://www.paulgraham.com/hundred.html
- matheusmoreira 3y agoC just feels good to read and write. Every other language suffers from not being C.
- 0xfedbee 3y agoBecause I don't like "magic". I can understand the appeal of one-liners that do the work of 100 (or more) lines of C but that's just not what I like to do. I like to be in control. I don't like side effects. "Undefined behaviors" is a propaganda. In my 15 years of programming in C, I never had an issue with "undefined behaviors". Things I created a decade ago still run like a champ on a damn coin cell.
- Aromasin 3y agoIf you're doing anything in embedded systems/hardware, expect to be using C. Yes, Embedded Rust and MicroPython are a thing now, but if I need to work with any partner or customer I'll be in a world of pain, because 99% of that industry uses C. My customers start new projects in C every other week. If you need to be Processor independent, Portable, Performant, have access to Bit manipulation, and need direct control the Memory management, along with a massive ecosystem, C is almost the only option.