9 ms·
C is simple in the sense that it is very possible to have the whole language "in your head" and at one point in my career I did. Something I would never claim a
by random_upvoter 5y ago
C is simple in the sense that it is very possible to have the whole language "in your head" and at one point in my career I did. Something I would never claim about C++. Yes, you can write atrociously bad C code that is hard to parse and hard to predict, as the webpage demonstrates. All this can be trivially avoided by having the most basic coding standards. And yes I'm sure that some of the modern alternatives to C are better. But all in all, C is simple.
- dottedmag 5y agoCan you really keep all instances of undefined behaviour in C in your head?
- mburee 5y agoWhy would he need to? Just know what is definitely defined and stick with that
- Certhas 5y agoBecause he claimed to keep the entire language in his head, not just a sane subset. You can also keep a sane subset of C++ in your head.
- jb1991 5y agoUndefined behavior means that which is not defined in the standard. The C standard is quite small, and it is very easy to keep all that in your head. There’s no need to have any subset of the language in memory, this is the whole standard.
- adwn 5y ago> Undefined behavior means that which is not defined in the standard. No, you're wrong. "Undefined Behavior" (UB) in the context of C has a very different meaning from the colloquial meaning "not specified". The meaning of UB is explicitly defined in the C standard, and the C standard explicitly lists certain program execution patterns as UB.
- gpderetta 5y agoFrom the sacred texts (emphasis mine): > If a ‘‘shall’’ or ‘‘shall not’’ requirement that appears outside of a constraint is violated, the behavior is undefined. Undefined behavior is otherwise indicated in this International Standard by the words ‘‘undefined behavior’’ or by the omission of any explicit definition of behavior. There is no difference in emphasis among these three; they all describe ‘‘behavior that is undefined’’
- jb1991 5y agoBut do you consider that a "subset" of the language, as parent says?
- deleted 5y ago[deleted]
- pjc50 5y agoThen you can't do arithmetic.
- random_upvoter 5y agoSure. How long do you imagine that list to be?
- qiqitori 5y agoDoes anyone have any examples of undefined behavior in C where it isn't pretty obvious that you'll get undefined behavior?
- erik_seaberg 5y agoa[i] = i++ is undefined, even though a[i] = i and a[j] = i++ each do what they obviously should.
- random_upvoter 5y ago> a[i] = i++ And I would never excrete such a line of code without automatically thinking "hmmm.. does this really do what I think it does?"
- gjulianm 5y agoIf I see a[i] = i++ in a codebase I am immediately removing it as it looks ambiguous, possibly invalid and probably unpredictable.
- adrian_b 5y agoThis is just a very particular case of calling a function with such arguments that the result will depend on the order of evaluation for the arguments. This is an undefined operation in almost all programming languages.
- gnubison 5y agoThis feels like one of those “you’re a fan of X? Name every Y” memes. If someone has some examples that a reasonable person would write, specifically examples that look like the simplest way to do something, then I’d love to hear them. Negative shifts and infinite loops with counters are just not things you normally encounter.
- SubjectToChange 5y ago> This feels like one of those “you’re a fan of X? Name every Y” memes. How can you identify legal C without understanding undefined behavior? Or rather, what is the list of UB that nay professional C developer should be expected to know? > If someone has some examples that a reasonable person would write, specifically examples that look like the simplest way to do something, then I’d love to hear them. Basically all OOP-style C. For instance, the Windows macro #define CONTAINING_RECORD(address, type, field) ((type *)( \ (PCHAR)(address) - \ (ULONG_PTR)(&((type *)0)->field))) The Linux kernel is also an excellent source of these. Also, serialization/deserialization libraries are an excellent source for UB. And huge amounts of code depends on UB by using unsigned char arrays for storage of other types. Furthermore, in practice, C code assumes most of the following: - null pointers are represented as 0, e.g. if(ptr). - pointer provenance does not exist - char and unsigned char are 8-bit bytes - two's-complement arithmetic - page-based memory protection - no types have trap representations - the values of base types, besides floating point, do not have multiple representations. - etc
- gnubison 5y ago> what is the list of UB that nay professional C developer should be expected to know? Off the top of my head, as a non professional C programmer: - can’t read from uninitialized memory - one malloc = one free - signed overflow is undefined - pointers to stack variables can’t outlive the stack variable itself - can’t dereference NULL pointers (Obviously take this with a massive grain of salt, there are almost certainly important ones that I’m forgetting.) > null pointers are represented as 0, e.g. if(ptr). If(ptr) is guaranteed by the standard to work. Memset(ptr, 0, len) is not (and is very broken in other ways as well). > char and unsigned char are 8-bit bytes They’re guaranteed to be one byte, where a byte is at least 8 bits. So unless you’re trying to simulate %256 with an implicit conversion, or relying on unsigned overflow to turn 0xFF into 0x00, then you’ll be fine.
- chousuke 5y agoIf the entirety of C is in your head, would you take a challenge to list every operation that has undefined or implementation-defined behaviour?
- masklinn 5y agoIt seems like it would be easier to lists those which don’t, and that would be a pretty small list.
- serial_dev 5y agoIf I were to write C, I'd probably come up with my list of "The bad parts" (like the js book), and not use those (and agree with my team not to use those). Like in JavaScript, you can understand and remember how double equals work, if you try hard enough... Or you can just decide to always use triple equals and voila, your problem is solved once and forever. But I don't write C, so take this with a grain of salt.
- adrianN 5y agoIf it were easy to avoid "the bad parts" of C, there would be a lot fewer exploits based on memory corruption. Many of the bad parts are unfortunately integral to the language. Only the strictest coding standards have a chance to avoid all the bad parts, but it's not generally possible to automatically check for compliance with all rules of, e.g. MISRA, so you still have fallible humans in the loop.
- jerf 5y agoI would assume you would claim you have English "in your head"... can you name every word in the top 5,000 words that has a "z" in it? If English is not your first language, than convert to an appropriate challenge in your native language. We aren't computers, and that's not what we mean by having things in our head.
- mannykannot 5y agoA typo involving 'z' within an English word very rarely has any significant consequences.
- TheOtherHobbes 5y agoYou can keep C syntax in your head. But that's the problem with C. It looks like a simple language with a small number of straightforward constructs, when in fact it has very poor predictability. Real-world results are implementation and platform-dependent, which is a polite way of saying they're unhelpfully unpredictable. Coding standards will help, but they only get you so far because some ambiguities will always be there. Basically C is a half-assed wrapper around assembler - useful in its day, but machine-dependent and outdated now.
- jstanley 5y ago> You can keep C syntax in your head. You can keep the syntax and all the useful parts of the standard library in your head.
- wheelerof4te 5y agoThat is indeed helpful. A lot of modern languages sacrificed simplicity for features no one (with enough skill) asked. Even with Python, I can only keep some part of built-in functions and methods together with syntax in my head. For a large part of standard library, I absolutely need IDE features. Especially since Python is an object oriented language where every data instance has a bunch of methods attached.
- gjulianm 5y agoI have been coding some time in C (and some of that doing kernel drivers), and I don't think I've ever found "unhelpfully unpredictable" behavior in regular code. The "coding standard" that gets rid of those is "if it looks weird, it goes away". I've seen quite a lot of talk about the problems of undefined behavior but I haven't actually faced those problems in the wild.
- pjmlp 5y agoI suggest doing code review of offshored code delivered by sweep shop consultancies, you will find plenty of it.
- scarface74 5y agoHow useful is C without platform or environment specific frameworks or libraries in most cases?
- tialaramex 5y agoIt's almost completely useless without libraries. It's probably a tough call whether to just write assembly language instead at that point, if you have a nice assembler and know how to use it that might be more productive than C with no libraries. Much of the stuff programmers are just used to being "part of C" is actually from a library. For example NULL is just a macro that some library provided, C's "null pointers" are just the literal zero for a pointer, even if your hardware does not represent "null pointers" by an all-zeroes value.
- pjmlp 5y agoCase in point, malloc() is impossible to implement in ISO C, it is either done in Assembly, or requires language extensions. Unless we are speaking about the very niche use case of allocating a big blob of static memory on the global segment. Most of the stuff done with ISO C + language extensions/external Assembly, can be as easily done in safer languages. Language extensions and external Assembly aren't a C priviledge.
- tialaramex 5y agoWell, an allocator is something your system might just not need. Rust doesn't come with an allocator either, if I only have say 16384 bytes of RAM maybe I don't want an allocator, I can write down what I'm using each byte for. But C doesn't come with functions like memcmp() and memcpy() or memmove() either and those are pretty fundamental even if you're on a tiny constrained system that can't aspire to stuff like "strings" and "allocation" you've probably heard of data structures and copying.
- BenjiWiebe 5y agoWhy can't you implement malloc() in ISO C? Or do you mean you can't write your own malloc that complies with the OS's memory management in C? Because I'm pretty sure you can write a malloc in ISO C when you aren't dealing with an OS (which is usually when you need to write a malloc).
- chunkyguy 5y agoI feel that same way about Objective-C.
- deleted 5y ago[deleted]
- billfruit 5y agoBut the thing is so many things are mighty inconvenient to do in C. Every large program in C has to build up lots of fundamental infrastructure like a library of data structures, and mechanisms for error handling. Why should one use C and spend a lot time on building a basic platform, rather than spending time, effort and creativity on the core concerns. Not to mention the chances of so many things to go wrong, in writing C code. Its like climbing a mountain without a safety gear.
- dzaima 5y agoIf those utilities make up an excessively large portion of your program, C probably isn't the best choice. As long as you don't care about performance lost from the non-zero costs of "zero cost abstractions". If you're implementing custom data structures, you'll likely be able to make them optimal for your use-case, both from ease-of-use and performance. (except hashmaps. those do suck to write)
- wheelerof4te 5y agoHashmaps are often overrated. I can count on my fingers how many times I used dictionaries in Python. Pretty much anything you can do with dictionaries can be much more cleanly done with classes and other built-in features. Membership testing? Use sets. Data storage? Use classes with slots or tuples/namedtuples. Custom switch statement? Use the new match/case keywords or just fall back to plain if/elif towers. It's so much simpler.
- dzaima 5y agoIt definitely depends on the project. There are plenty of domains where hashmaps are useless, and some where half the things you do need one, and many in between. The in-between is the worst - if you need a hashset/hashmap just once, you'd still have to suffer through implementing one (O(n^2) is just not acceptable). But there is a bright side to this - hashmaps are pretty inefficient if a list could do, so the more you're pushed against using one, the more likely you are to have faster code :) (as long as you don't go the O(n^2) route, that is; also, hashsets are equally hard to implement manually as hashmaps)