22 ms·
Best aspects of C
- adwn 6y ago> There is little of needless code in standard library. And what there is, believe me, over the decades was really polished. Wrong. Counter-example: locales [1] Also: String handling, which is responsible for so, so, so many security vulnerabilities. The underlying cause is zero-terminated strings (instead of using start/end pairs or start/length pairs). You can't even tokenize a string without either copying or modifying it! Also also: just a single, apologetic mention of undefined behavior. Responsible – in cooperation with over-zealous compiler writers – for so many more bugs not already caused by improper string handling. [1] https://github.com/mpv-player/mpv/commit/1e70e82baa9193f6f027338b0fab0f5078971fbe https://github.com/mpv-player/mpv/commit/1e70e82baa9193f6f02...
- andi999 6y agothe over-zealous compiler writers is getting a big problem. I remember a while ago there was a proposal of a friendly c standard.
- wool_gather 6y agoIndeed; by John Regehr: https://blog.regehr.org/archives/1180 https://blog.regehr.org/archives/1180 I don't think it has really been proposed anywhere though, unfortunately.
- andi999 6y agoI have been surmising there might be copyright issue of the actual standard?
- Cloudef 6y agoLocale handling and most of the str* function in libc indeed are quite bad. The only sane function for "C strings" is snprintf.
- kazinator 6y agoOnes like strspn, strcspn, strpbrk, strchr are also sane.
- asveikau 6y agoExcept when you consider what a char is. Those don't handle multi-byte sequences.
- kazinator 6y agoYes they do. What they don't do is search for multi-byte characters. You would not use them for that; you use them to look for ASCII delimiters. You can tokenize a UTF-8 string on ASCII delimiters using functions that are oblivious to UTF-8, and this is in fact a preferred technique in many programs.
- asveikau 6y agoI think I was going for more like: strpbrk and friends can't use a multi-byte sequence as a delimiter. And if a delimiter appears as part of a multi byte sequence, you may see strange results. In a localized world searching for delimiters also starts to make less sense. Eg. I have been told it doesn't make sense to break on whitespace for Chinese text. To be clear, I am not saying these functions are bad or evil or to blame for their limitations (a bunch of the problem space wasn't invented yet when they were introduced), just noting they have limits. A bunch of more recent languages and libraries have the same or similar issues, too.
- jesuscyborg 6y agoUse wcspbrk. UTF-8 continuation characters are limited to the range \200 through \300 so there's basically zero chance that if you choose something like comma as your delimiter that it's going to tokenize the middle of a multibyte sequence. Also take into consideration that, under the hood, functions like strpbrk() are typically accelerated by CPU instructions such as PCMPISTRI which doesn't support UTF-8 natively but it does support UCS-2.
- deleted 6y ago[deleted]
- jjgreen 6y agoThat [1] is a top-quality rant, but I fear for the author's blood-pressure
- flohofwoe 6y agoI love C but I think most of the C standard library is useless and outdated rubbish (pretty much everything above the mem...() and math functions). The entire IO and memory-management areas should have been either thrown out of the standard library, or updated 20 years ago to keep up with low-level operating system features. But one of the best features of C is that you can mostly ignore the standard library and still enjoy "C the language", e.g. nobody ever choose C for its standard library ;) (also re UB etc...: use the mighty trio UBSAN, ASAN and TSAN!)
- bhaak 6y agoWhat do you use then if you don't use the standard library? What is the standard library for the C which ends all the other standards libs?
- adrift 6y agoYour own.
- flohofwoe 6y agoFor IO, memory management and similar "low-level services" it's often fine to call the underlying operating system functions directly and with this also make use of more powerful features than the standard library can provide, such as async IO, virtual memory etc (or wrap those OS calls into your own cross-platform wrapper functions). There's also plenty of libraries which do this for you, like libuv (but integrating such big libs is often not trivial). Same for string handling/processing, there are specialized libraries out there which do a specific job better than the rather generic standard library functions (this is also true for the C++ stdlib), one just has to find those libraries. PS: I don't think that a "batteries included" standard library (like python has) would even make sense for C, such a library would need to be opinionated by definition, better to keep (too many) opinions out of the standard, as this road just leads to another C++ ;)
- arcticbull 6y agoC++ is IMO defined by a complete lack of opinion. They just standardized every opinion. For everything. C++ would be a much better language if it showed some opinion, even if I disagreed with it.
- pornel 6y agoThere's a lot of subtly broken stuff in C stdlib. For example, time.h calls that may touch the TZ env var aren't thread-safe, because getenv is unsafe (it gives a pointer to its internal globally mutable data structure without locks). Because C is so bare-bones, it usually leans on POSIX as its extended standard library, and that is also full of old cruft.
- einpoklum 6y agoOne thing that is not-so-subtly broken is re-entrancy (e.g. in functions like `strtok()`; fixed later with `strtok_s()` in C11: https://en.cppreference.com/w/c/string/byte/strtok https://en.cppreference.com/w/c/string/byte/strtok About getenv: This sort-of fixed with `getenv_s()` in C11 https://en.cppreference.com/w/c/program/getenv https://en.cppreference.com/w/c/program/getenv
- loeg 6y agoRe: getenv_s: Annex K is (still) optional. Compliant C11+ implementations may not have getenv_s.
- josephcsible 6y agoThis is an understatement. The only relevant platform that supports Annex K is MSVC, and this is unlikely to change any time soon.
- loeg 6y agoAnd even MSVC does not actually implement the API specified in Annex K. Annex K was inspired by MSVC's design, but they diverged: > Microsoft Visual Studio implements an early version of the APIs. However, the implementation is incomplete and conforms neither to C11 nor to the original TR 24731-1. For example, it doesn't provide the set_constraint_handler_s function but instead defines a _invalid_parameter_handler _set_invalid_parameter_handler(_invalid_parameter_handler) function with similar behavior but a slightly different and incompatible signature. It also doesn't define the abort_handler_s and ignore_handler_s functions, the memset_s function (which isn't part of the TR), or the RSIZE_MAX macro.The Microsoft implementation also doesn't treat overlapping source and destination sequences as runtime-constraint violations and instead has undefined behavior in such cases. [0]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1967.htm http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1967.htm
- donquichotte 6y agoExhibit B: yes, the command that repeatedly prints `y` to stdout. Exhibit C: all handling of time, with time_t, struct timespec, timeval etc. We should just have a 64 bit uint representing nanoseconds from epoch, which would give us 585 years and get rid of much nonsense. [1] https://github.com/coreutils/coreutils/blob/master/src/yes.c https://github.com/coreutils/coreutils/blob/master/src/yes.c
- throwhypothetic 6y agoTime should be a signed int with enough bits to go backward from the epoch at t=0 to the big bang at t=negative whatever.
- marcosdumay 6y agoIn what resolution? What do people that need higher resolution use? And the people that don't care about that amount, do they pay the performance penalty? Every physical or humane value is hard to some extent.
- aw1621107 6y ago> enough bits to go backward from the epoch at t=0 to the big bang at t=negative whatever. For reference, the universe is about 13.787 billion years old [0]. That's about 13.787 * 10^9 * 365 * 24 * 3600 = 4.348 * 10^17 seconds, which (I think?) is a 59-bit number [1]. 10ths of a second will require 62 bits, which is right about at the edge of what a 64-bit signed integer will allow. If you want milliseconds, you'll need at least 69 bits. For nanoseconds, you'll need at least 89 bits. So you'll either need an integer type that's wider than what's natively supported in most hardware (thus potentially sacrificing performance), or you'll have to sacrifice precision. [0]: https://en.wikipedia.org/wiki/Age_of_the_universe https://en.wikipedia.org/wiki/Age_of_the_universe [1]: https://www.wolframalpha.com/input/?i=13.787+*+10%5E9+*+365+*+24+*+3600+to+binary https://www.wolframalpha.com/input/?i=13.787+*+10%5E9+*+365+...
- AnimalMuppet 6y agoJust go for it. In the larger scheme of things, it's not that long until 128-bit CPUs. I'm mostly kidding. But if you're thinking of representing nanoseconds since the big bang, the wait for 128-bit CPUs is not very long...
- adamnemecek 6y ago> You can't even tokenize a string without either copying or modifying rust-lang saves the day, yet again https://www.brandonsmith.ninja/blog/favorite-rust-function https://www.brandonsmith.ninja/blog/favorite-rust-function
- deleted 6y ago[deleted]
- mrkeen 6y ago> The underlying cause is zero-terminated strings (instead of using start/end pairs or start/length pairs). What type (and how big) should length be?
- mikepurvis 6y agoProbably something like how msgpack (or even variable length unicode encoding) works, where the low 127 bytes is a single uint8 char in place, and then the next 121 signals that a string of length 1-121 bytes follows, and then after that are a handful of sentinel values for "length follows and it's 1/2/4/8 bytes" plus maybe some special cases like zero-length string. I can understand that this might have been too much implementation complexity/risk to contemplate 40 years ago, but this kind of pattern is very well established at this point, especially in scripting languages with loose typing.
- masklinn 6y ago> What type (and how big) should length be? size_t.
- hazeii 6y agoA single counter-example doesn't make it all wrong. Perhaps a committee was involved.
- Jorengarenar 6y agoOK, updated
- ncmncm 6y agoOne word: strtok It takes more code to use it correctly than to hand-code what you think it is supposed to be doing for you. strtok is Cursed. strlcpy is similar. If you don't write that much more code, you are not using it correctly, and it is not giving you the value that is the reason you thought was why you were using it.
- jjav 6y ago> Also: String handling, which is responsible for so, so, so many security vulnerabilities. That's not really a counter-example. Sure the string handling was an unfortunately choice but it is the standard. The standard library must implement it as defined. Being standard-compliant is not lack of polish.
- aw1621107 6y agoDepends on what one means by "polished", I suppose. You can have a polished implementation of a not-so-polished specification, and vice versa.
- andi999 6y agoThe part 'if you want to programm video games use C++' is a bit weird though. I remember a talk from a videogame designer, when asked:'why do you use c++' the answer was actually 'because we have to' in the sense everybody does it and management requires it. The features used we basically C.
- johnnycerberus 6y agoOne reason would be template metaprogramming. In game development and simulations we use numerical computing most of the time, a domain where templates come in handy. The rest is just C. Modern C++ for highly efficient systems is not C with classes anymore, it is more like C with templates. And also functional constructs for specific components (physics) where it makes sense to use it. I have high hopes that a language like Zig will prove less complex and as powerful as C++ for game dev. It won't be a replacement for C++, just an alternative, hard to believe that anything will dethrone the king.
- simiones 6y agoFrom what I've heard in presentations, many game developers seem to be some of the biggest opponents of templates, especially because of the horrible performance on non-optimized builds of template-heavy code, and in general because of the opaqueness of performance of template-heavy code.
- andi999 6y agoFor numerical computing:yes. For games, dont you just use float (for non-integer values)? Found the video I was thinking about: https://www.youtube.com/watch?v=rX0ItVEVjHc https://www.youtube.com/watch?v=rX0ItVEVjHc from 1:24:00 onwards.
- jasonwatkinspdx 6y agoC++ templates have a poor reputation in the gamedev community. Years ago some companies went too far with it and got burned. EA has their own fork of the STL that's gotten increasingly crufty and behind in performance. AAA game engines also get big, so there's a lot of pressure on avoiding explosions of build time with more advanced template techniques. Gamedev tends to use its own patterns, particularly arena allocation for long lived fixed size tables, or their own internal object / entity / component model . It's not really c with classes or c with templates, just kind of its own dialect.
- timw4mail 6y agoAs a programmer, I'm not very fond of C, but C has two huge advantages over other languages: * It's everywhere * There's a standard ABI
- jcelerier 6y ago> * There's a standard ABI there's one ? the "C" ABI is just the ABI of whatever platform it's running on, which may or may not have funky behaviour that vendor-provided compilers kindly hide for you - functions being prepended with '_' on macOS, the two-dozen calling conventions on windows with i386, sysv and itanium ABIs... Do you think you can tell what's the ABI of struct foo some_function(struct bar); ? will bar be passed in a register, on the stack ? who knows, that depends on your platform, your compiler, etc etc. Things going cleanly on the stack is just a convenient lie that your first year comp. sci teachers tell you because it's too early to talk about how the real world works yet.
- pavlov 6y ago> the "C" ABI is just the ABI of whatever platform it's running on But typically the C ABI is the only stable ABI those platforms provide. That's a huge benefit.
- brandmeyer 6y agoApple's custom AArch64 ABI has entered the chat... https://developer.r-project.org/Blog/public/2020/11/02/will-r-work-on-apple-silicon/ https://developer.r-project.org/Blog/public/2020/11/02/will-... https://developer.apple.com/documentation/xcode/writing_arm64_code_for_apple_platforms https://developer.apple.com/documentation/xcode/writing_arm6...
- setpatchaddress 6y ago"platform", not "ISA". Windows on Intel doesn't use the same ABI as linux.
- Cloudef 6y agoSorta related: http://damienkatz.net/2013/01/the_unreasonable_effectiveness_of_c.html http://damienkatz.net/2013/01/the_unreasonable_effectiveness...
- olivierestsage 6y agoWould anyone have an up-to-date book recommendation for someone looking to learn C? Is K&R still the best way, or are much more recent things like Modern C [1] the way to go? I am concerned that if I learn techniques that are too "modern," I might not be able to contribute to open source projects that employ older conventions. [1] https://modernc.gforge.inria.fr/ https://modernc.gforge.inria.fr/
- rightbyte 6y agoJust go with K&R of the latest edition. It gets you into the mood. Concerning modern techniques I don't really understand what that would be.
- shrimp_emoji 6y agoModern C: "use Rust instead".
- ghostwriter 6y agoModern C: don't use Rust, use regular C and incrementally wrap & migrate it to ATS[1][2] [1] http://ats-lang.sourceforge.net/DOCUMENT/INT2PROGINATS/HTML/INT2PROGINATS-BOOK-onechunk.html http://ats-lang.sourceforge.net/DOCUMENT/INT2PROGINATS/HTML/... [2] http://ats-lang.sourceforge.net/DOCUMENT/INT2PROGINATS/HTML/INT2PROGINATS-BOOK-onechunk.html#Cinteraction http://ats-lang.sourceforge.net/DOCUMENT/INT2PROGINATS/HTML/...
- flohofwoe 6y agoThe latest K&R edition is from 1988, C has changed quite a lot since then... especially with C99 which almost feels like a new and much friendlier language because of the new initialization features (compound literals and designated init).
- Ericson2314 6y agoLearn Rust first, then go read someone's C codebase. Maybe consult https://en.wikibooks.org/wiki/C_Programming https://en.wikibooks.org/wiki/C_Programming for any syntax you don't recognize.
- jcranmer 6y ago> There is little of needless code in standard library. And what there is, believe me, over the decades was really polished. Ha ha ha ha ha ha ha ha ha ha. Locales are a massive clusterfuck, basically too simple to handle localization if you actually care about it, but supports enough of it to screw you over if you don't care about it. The "wide character" support is also a nightmare. The time library support is also quite a bit wonky (years are measured as years since 1900 because Y2K is definitely not a pressing issue in 1989!). > inline Assembly Fun fact, here is the C specification's entire mention of inline assembly: > The asm keyword may be used to insert assembly language directly into the translator output (6.8). The most common implementation is via a statement of the form: asm (character-string-literal); There is no discussion of what inline assembly can and cannot do, how it interacts with the rest of the code in term of semantics, how to pass arguments to and form inline assembly, etc. You might get some of this information from the manuals of compiler implementations, but even that can be surprisingly free of necessary information. Compare this to Rust's inline assembly documentation: https://rust-lang.github.io/rfcs/2873-inline-asm.html https://rust-lang.github.io/rfcs/2873-inline-asm.html (which is more detailed than even gcc's or LLVM's inline assembly documentation).
- deleted 6y ago[deleted]
- deleted 6y ago[deleted]
- tyingq 6y agoMost other languages are implemented in C, so there's an obvious reason that it has some advantages over everything else. That the OS typically depends on the C ABI makes it mandatory. You can find warts, but as of now, there isn't a broadly proven alternative for some use cases.
- jcranmer 6y ago> Most other languages are implemented in C I'm not entirely sure what you mean here, but this is true for neither of the interpretations I have. Most compilers are not written in the C language (indeed, even the major C compilers are all written in C++ now!). Alternatively, most languages compile down into a bytecode language, or into native assembly, without converting into anything that is or looks like C in the process. Indeed, for languages targeting native assembly, C is an unwelcome intermediate step precisely because its semantics can be too constraining.
- tyingq 6y ago"I'm not entirely sure what you mean here" I mean that most of the popular languages I'm familiar with are written in C.
- Thiez 6y agoGCC is written in C++ these days, and LLVM has always been C++. Perhaps some languages are written in C, but C itself hasn't been for some time, if you look at the most popular compilers.
- jcranmer 6y agoThat doesn't clear up any of my confusion, although that may well be because your experience is mostly with managed languages where the distinction between compiler and runtime is less obvious. Even looking at major languages [1], what do we have: * Self-hosting languages: C#, Go, Haskell, Java, most LISP implementations, OCaml, Pascal, Rust, Swift * C++ implementation: C/C++/Objective-C compilers, Fortran compilers (using the same toolchains as the former), JavaScript, probably Visual BASIC (although that may be C# instead) * C implementation: Perl, PHP, Prolog, Python, R, Ruby, Shell (although note that many of these languages have their libraries largely written in their own language). * Not sure: ALGOL, APL, Cobol, Erlang, Forth, Kotlin, SQL, Simula, Smalltalk. Although I suspect that many of these are self-hosted. 7 of 32 is a far cry from "most languages". [1] Using the list of programming languages in Wikipedia's category box at the bottom here.
- coliveira 6y agoI have been programming in C for the last 25 years, and every year or so someone comes with a new shinny thing that will "replace" C. First it was C++ (we know how well it did...), then Objective-C, then Java, then C#, then Go, and now rust. Everyone of these language brought new ideas, but they don't stand a chance because their designers don't understand the point of C. The C language didn't win because it was the "best" language or had the best set of features. Far from it. Even in the mid 70s it was a backward language compared to other cool languages of the day like Algol and Lisp. C won the competition because it just gives programmers the bare minimum functionality to put an operating system and a compiler in place! It is flexible, you can provide your own library if you want, and therefore gives your easy portability. OS writers will chose C any hour of the day or night because it makes their job easier. By comparison, other languages will require a huge library to be available, and sometimes a complex runtime system, just for you to write a simple "hello world"! Imagine if you need to write a new OS, a compiler, a linker, or a shell interpreter... you get the idea. My conclusion is that language designers still didn't get what made C so successful and therefore keep coming up with shiny complex things that don't stand a chance to become the next C.
- coldpie 6y agoI'm in a similar position (only 10 years professionally though). I agree with everything you said, except I think Rust genuinely has a shot here. It doesn't have a runtime, and you can choose to use just the core libraries or even no standard libraries. It exports to the C ABI, so it's compatible with existing software and libraries. It isn't a garbage pile like C++. And it solves a real problem C has, code safety. It's silly to say any language will completely replace any other language, but I think Rust fits into the slot pretty well for the vast majority of cases where C is currently the best choice.
- coliveira 6y ago> I think Rust genuinely has a shot here. People proposing every other language that tried to replace C thought the same. Only time will tell, of course, but I wouldn't bet on it. Nowadays C++ is seen by many as a "garbage pile", the same can happen to rust.
- asveikau 6y ago> Provide only one way to do an operation. I thought this was funny because these two are the same: a[i] *(a + i) Likewise all of these are the same: (*s).x s[0].x s->x As are these: if (x) if (x != 0) But I guess the point can be applied elsewhere.
- jcranmer 6y agoDon't forget that these two are the same: 0[x] x[0]
- mlyle 6y agoSo are a+b and b+a. ;)
- huhtenberg 6y agoUnless one of them is a sneaky macro. Ha!
- Inityx 6y agoSure, but addition is commutative everywhere...
- mlyle 6y agoSure... I'm just pointing out that reordering operands on a commutative operation doesn't really count as "multiple ways to do things."
- nevinera 6y agoIt's fast enough to portably write a better language in!
- secondcoming 6y agoYou can look at piece of C code and immediately see how it'll be laid out by the compiler.
- pjmlp 6y agoIndeed, and most likely you will be wrong with anything beyond -O0.
- MisterTea 6y agoIf you want a great example of a clean well thought out C library from scratch then look no further than plan 9 C. http://man.postnix.pw/9front/2/intro http://man.postnix.pw/9front/2/intro Also might help if I add a paper on porting plan 9 which illustrates some of the niceties of the C library and portability ease. http://doc.cat-v.org/plan_9/4th_edition/papers/libmach http://doc.cat-v.org/plan_9/4th_edition/papers/libmach
- arcticbull 6y ago> Trust the programmer. Programmers have been show, time after time, not to be particularly trustworthy. Have we not learned the lesson that it's really easy to make mistakes, and we should trust tools instead of people to check our work? > Don’t prevent the programmer from doing what needs to be done. Ditto the above. > Keep the language small and simple. It is in some ways, but it's "smallness" leads to a serious lack of simplicity as seemingly simple things are incredibly hard to do right consistently. For instance, avoiding indexing past the end of an array or rolling over an integer. > Provide only one way to do an operation. That's nice, I'll grant you, although there are of course exceptions that prove the rule, like: a[b] is synonymous with *(a + b) *((uint8_t *)a + (b * sizeof(*a)) > Make it fast, even if it is not guaranteed to be portable. Well... I mean...
- GeorgeTirebiter 6y agoThe funny thing about rolling over an integer not being easy to catch is hard to ignore. The PDP-11 'add' instruction sets the 'C' bit if there is a carry from the MSB; the Z bit is set if the result == 0; the N bit is set if the result < 0; and the V bit is set if there is arithmetic overflow (both operands were of the same sign, but the result is of opposite sign). By simply making these bit available as, say, special names that could be tested (e.g. C N Z V) after an operation, you could determine what happened (if appropriate) and take action. HP's SPL had such a feature (used on the HP3000 series). C is not a well-designed language, but an improved 'lifeform' along the way. Today, I would like to see a c-like language developed specifically for RISC-V; especially one that had many many fewer edge cases.
- arcticbull 6y agoWhat's so hard about: if (((a > 0) && (b > 0) && (a + b) < 0) || ((a < 0) && (b < 0) && (a + b) > 0)) { / * Overflow */ } Now you may be saying, well, isn't there a branch-if-arithmetic-overflow instruction in practically every single architecture ever? To which I would say simplicity matters. [edit] </sarcasm>
- 6y ago
- dpc_pw 6y agoC developers are denying sad reality of obsolete archaic ideas, poor ergonomics, crappy tooling, security nightmare, unreliability, and language that is impossible to scale in a bigger team settings (don't bring up Linux; I am a kernel dev; Linux kernel is relative small, carefully engineered core, and majority of the weight are a leaf nodes of device drivers, implementing small and carefully engineered interfaces).
- anta40 6y agoI guess we should ditch C from "Programming 101" classes, yes? Better started with friendlier languages like Java/C#/Python etc where beginners can focus on implementing their algorithms without messing with lower level details like pointers, manual memory management, etc. Of course I'm not saying C is not useful anymore (it still is, for example if you are doing system/kernel programming, very likely you'll deal with C). But this is not the typical case for beginners.
- cptroot 6y agoI think most "Programming 101" classes _have_ ditched C. I haven't heard of any current intro classes being taught in C.
- jbirer 6y agoI was trolled into learning C by 4chan when I was 15. I would say I am grateful to them. I feel like I owe my programming career and very deep understanding of computers and computer science to that. Learning pthreads, file descriptors, client/server architecture made me a really good back-end developer.