[HN Gopher] Essential C [pdf] ___________________________________________________________________ Essential C [pdf] Author : graderjs Score : 87 points Date : 2022-03-05 10:08 UTC (1 days ago) (HTM) web link (cslibrary.stanford.edu) (TXT) w3m dump (cslibrary.stanford.edu) | sylware wrote: | Could you tell the linux kernel devs to stop pouring tons of | extensions and non C89 (with benign bits of c99/c11) code | everywhere? | | For instance in the whole network stack, some "agents of the | planned obsolescence" did not use constant expressions in | switch/case statements or initializers. Also tell those who are | using that toxic c11 "Generic" keyword to use explicit code | instead. I think, the main issue for C now is the standard body | trying to obsolete stable in time C code and forcing upon | devs/integrators the usage of the only few compilers which are | able to follow fast enough their tantrums (latest gcc/latest | clang). Results: all attempts to provide "working" alternative C | compilers is beyond reasonable. This is becoming so accute, it is | more likely to be a worldwide scam than anything else. | | Yep, open source software is not enough anymore, it needs to be | lean, simple but able to do reasonably "the job", and ofc very | stable in time hence C89 only with benign bits of c99/c11, or | "standard assembly". | properparity wrote: | Standard C is not suitable for writing kernels anymore due to | all the ridiculous hostile interpretions of the standard | (mainly typed based aliasing rules). | | I mean you can't even write an allocator in standard C. | shadowofneptune wrote: | Then in this case the portability offered by C is false. | Could be an equally valid approach to place the required | behavior into separate modules written with an assembler. | woodruffw wrote: | > I mean you can't even write an allocator in standard C. | | I might be missing something, but I don't believe this is | true? The type aliasing rules have an explicit carve-out for | `char *` aliases of other types, which is intended to solve | this exact issue. | | Similarly, C99 and C11 both allow aliasing through a union of | pointers without violating the strict aliasing rule. | monocasa wrote: | Both sides are true IMO. The kernel isn't capable of being | written totally in standards compliant C, _and_ it 'd | probably be better off if that fact wasn't used as carte | blanche to write non standards compliant code where it | doesn't provide a tangible benefit. | jstimpfle wrote: | Why can't you write an allocator??? | monocasa wrote: | Casting pointers to different object types and then | dereferencing them is undefined behavior. | rurban wrote: | well, alternative compilers can excel in not providing broken | optimizations based on UB. -Oboring is a thing in the kernel. | chibicc e.g | ChuckMcM wrote: | I have often wondered if a C89 standard with no UB would be | the definitive go to language for systems. I mean you COULD | define things if you chose to, right? | Ar-Curunir wrote: | How do you eliminate ub without memory safety?! | isomel wrote: | For example, as mentioned in another comment, by making | all read and write `volatile`, that way, dangling pointer | and out of bounds are "defined" to be memory corruption | or crash, and not the compiler optimizing the code in a | way that the programmer did not enticipate. | znpy wrote: | Wouldn't that bring a performance penalty though? | MaxBarraclough wrote: | _Some_ kinds of UB can easily be handled that way. GCC | offers a flag to guarantee wrapping on signed arithmetic | overflow, for instance. [0] It wouldn 't be easy to nicely | handle _every_ instance of UB though. The current state-of- | the-art for that kind of thing would be something like | Valgrind, an extremely 'intrusive' runtime system. | | This is because of the particulars of the C language. For | instance, the way it handles arrays and pointer arithmetic | make it difficult to detect every instance of out-of-bounds | access. | | [0] _-fwrapv_ , see | https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html | ChuckMcM wrote: | I think we're talking about different things, but maybe | not. | | Using out of bounds references in arrays and pointer | arithmetic are _unsafe_ but they can be _defined._ The | definition of "array accesses using indexes that are | outside the range of 0 - the declared array size will | access memory in the data segment. The compiler will | treat that access as if the addressed memory had the type | specified for the array." The behavior is defined, it is | unsafe, but it is defined. Not like "the compiler may or | may not choose to optimize out loads and stores, or re- | order them." which especially on embedded systems | _creates_ bugs where the things like "read the status | register THEN read the data register (which clears the | status register when read)" those get re-ordered and | suddenly your loop never exits because your status bit is | never set. That kind of UB needs to die in a fire IMHO. | Thiez wrote: | > which especially on embedded systems creates bugs where | the things like "read the status register THEN read the | data register (which clears the status register when | read)" those get re-ordered and suddenly your loop never | exits because your status bit is never set. | | Why are the embedded systems programmers not using | `volatile`, which exists for this exact reason? | compiler-guy wrote: | "volatile" is not a synchronization primitive and doesn't | forbid reordering amongst access to different memory | locations. | | All it does is forbid reordering or removing accesses to | a particular memory location. | | Historically, many compilers implemented it as a hard | memory barrier, but that isn't how the standard defines | it. | Thiez wrote: | Volatile operations are considered a side effect and as | such may not be reordered with other volatile operations. | How is that not literally sufficient for the operations | that GGP described? | compiler-guy wrote: | I spoke perhaps a little strongly. But that said: | | The compiler is free to reorder accesses to multiple | volatile variables if they happen prior to the same | sequence point. So (roughly) expressions involving two | volatile variables do not have those accesses sequenced. | | But you are right that the usage described earlier is OK, | as long as both variables are marked volatile, and the | accesses straddle a sequence point. | | The more common mistake with volatiles is to use the for | multithreading primitives. | pjmlp wrote: | Out of bounds is only defined for length + 1, for | anything else anything goes. | MaxBarraclough wrote: | That doesn't sound right. C (and C++) permit you to do | pointer arithmetic to derive a pointer value pointing to | one element beyond the final element of an array. This | special treatment doesn't extend to dereferencing though | - if you deference that pointer, that's UB. | | If you do pointer arithmetic to derive a pointer value | pointing 2 or more elements beyond the final element of | an array, that's undefined behaviour, even if you never | dereference that pointer. | pjmlp wrote: | Sure, I just didn't want to spend too much time | explaining all details, my failure. | MaxBarraclough wrote: | > "[...] The compiler will treat that access as if the | addressed memory had the type specified for the array." | The behavior is defined, it is unsafe, but it is defined. | | I believe performance (compiler optimisation) is the | reason the language isn't defined that way. Permitting | the compiler to assume that the runtime error will never | arise, opens the door to all sorts of optimisations. (At | least, that's the idea.) | | C permits the 'union trick' to (roughly speaking) access | the bit-pattern of a value as another type, which is to | say an escape-hatch is offered in the language. | | Similarly the strict aliasing rule is surprising to | people who are new to C, but the C standard committee | seem to be committed to keeping it, presumably for | performance reasons. | | > Not like "the compiler may or may not choose to | optimize out loads and stores, or re-order them." which | especially on embedded systems creates bugs | | Right, but it's defined that way to enable compiler | optimizations, not to spite the programmer. As others | have mentioned, C has features like _volatile_ | specifically to address this kind of thing. If the C | standard required memory fences to be inserted | _everywhere_ , performance would be ruined. | | > That kind of UB needs to die in a fire IMHO. | | C cannot easily be made into a safe language, and I think | the committee is doing the right thing in declining to | try to make C into something it isn't. On the plus side, | there are plenty of other languages around, many of them | with compelling advantages over C. Ada, Rust, and Zig, | for instance. | tialaramex wrote: | It's downright _perverse_ to insist that you should be | able to assume all accesses are volatile reads / stores | just because that was momentarily convenient. | | C lacks the intrinsics you'd actually want for this | (explicit load/ store of various machine sizes) but it | does provide the "volatile" storage qualifier which is | what you should be using to do what you apparently wanted | in C today. It makes no sense to demand everybody else | writes some sort of "not-volatile" qualifier in front of | every variable to tell the compiler that actually this is | just a variable and it's OK to optimise. | eqvinox wrote: | > "Could you tell the linux kernel devs to stop [...]" | | As you seem to care about this, have you made this argument on | LKML yourself? What Linux kernel related work are you active on | that drives your position? | froh wrote: | you did follow the decision to explicitly move to C11, did you? | | https://lwn.net/SubscriberLink/885941/01fdc39df2ecc25f/ | https://news.ycombinator.com/item?id=30459634 | unwind wrote: | How is _Generic toxic? | voldacar wrote: | it's ugly and doesn't feel like a first class citizen of the | language | CyberDildonics wrote: | This is a vague and emotional argument to a technical | question. | kevin_thibedeau wrote: | It has some annoying misfeatures. Closely related types | aren't treated as the same and you can't put multiple related | types in a _Generic association list. You're forced to resort | to explicit casts or call the type specific function directly | which subverts the intended magic of using _Generic in the | first place. | eqvinox wrote: | Some of this was fixed by DR 481 [http://www.open- | std.org/jtc1/sc22/wg14/www/docs/n2396.htm#dr...], can you | give some specific cases/examples at issue? | [deleted] | emmelaich wrote: | > _char ASCII character -- at least 8 bits. Pronounced "car"._ | | Never heard it pronounced _car_! | huqedato wrote: | Thank you! Most useful C book I ever got. ___________________________________________________________________ (page generated 2022-03-06 23:00 UTC)