9 ms·
> 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
by 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.
- ringshall 6y agoIt's been a long time since I've used C, but I recall strncpy being safe. I assume from context that I'm wrong, though.
- WalterBright 6y agoEvery time I review code and see strncpy, I look closer because it's always used incorrectly. It's always about the terminating 0. Is the 0 there or not? Is the 0 part of n or not? Does the destination have to be n+1 in size for the 0? I quit using it myself because I could never remember just what the exact protocol was for 0.
- Gibbon1 6y agoYeah used naively strncpy leaves you an unterminated string. Also like all of them it's up to the caller to predetermine if they will fail if called. So instead of having the checks in one place inside the string function. You have them scattered all over the code if at all.
- WalterBright 6y agoThe irony of strncpy is it's supposed to eliminate memory corruption errors, but instead is a prolific source of them.
- Gibbon1 6y agoI think I read somewhere the provenience of strncpy was to copy strings into a fixed length field which is why it has the deranged behavior of not terminating the string. Think file systems where the max file name is 8 characters. Or compilers that truncated variable names at 31.
- johntb86 6y agoStrncpy doesn't add a \0 if it truncates the string, so the user has to remember to do that.
- 6y ago
- 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.