4 ms·
Is it a small language? The standard takes a dense 179 pages to describe the language. I guess it's debatable if that is small or not. I certainly don't agree t
by anon1385 13y ago
Is it a small language? The standard takes a dense 179 pages to describe the language. I guess it's debatable if that is small or not. I certainly don't agree that it's a simple language though. There are myriad rules and exceptions to rules to remember. When and where one integer type will be converted to another. When overflow is defined and when it's undefined. Just those things alone mean that much real world C code is full of undefined behaviour[1] just related to arithmetic on numeric types, because the rules are hard to remember and reason about.
John Regehr asked people to submit str2long() implementations that didn't execute undefined behaviour. Despite being well warned about avoiding overflows and other undefined behaviour only 35 of the 78 submissions passed the test suite[2].
Even a trivially small 2 line C function can result in different results on the common compilers depending on which compiler and which optimisation level you use[3]. Compiler engineers struggle to agree on and correctly implement the "simple" rules of C. To me that's an indication that they aren't so simple.
I would say that C doesn't do anything behind your back as long as you don't ever use an optimising compiler or you pay very very close attention to the standard. If you forget then suddenly your check for pointer arithmetic overflow has been "helpfully optimised away" behind your back for reasons that are not immediately apparent at all[4].
I do agree that apparently simple rules can lead to complexity in use, and C is full of this too. For example you would think in a lower level language like C viewing a chunk of memory as a different type would be trivial, but the strict aliasing rule means you have to be a language lawyer to understand what is allowed and what isn't[5] as it makes the intuitive solution into undefined behaviour (which is extra pernicious since most of the time it will work as intended, until somebody compiles the code with a smarter compiler at a high optimisation setting).
[1] http://blog.regehr.org/archives/963 http://blog.regehr.org/archives/963
[2] http://blog.regehr.org/archives/914 http://blog.regehr.org/archives/914
[3] http://blog.regehr.org/archives/482 http://blog.regehr.org/archives/482
[4] http://pdos.csail.mit.edu/~xi/papers/stack-sosp13.pdf http://pdos.csail.mit.edu/~xi/papers/stack-sosp13.pdf example 1
[5] http://blog.regehr.org/archives/959 http://blog.regehr.org/archives/959
- TheCoelacanth 13y agoCompared to other languages, 179 pages for the full specification is pretty small. Java and C sharp have that more than three that size. C++ is roughly five times as large. If you're a fan of functional languages, Haskell is almost twice as large. Even Scheme, an extremely simple language, is only about 30 pages shorter.
- gnuvince 13y agoThe book "The Definition of Standard ML" is ~150 pages for a language that is simple (IMO) and rich. Bonus, the definition is not written in prose, but with typing rules and operational semantics.
- jlgreco 13y ago> Bonus, the definition is not written in prose, but with typing rules and operational semantics. While that is certainly nice, I suspect it makes it hard to really compare based on just pagecount. I doubt prose and such a formal definition like that are equally dense.
- gnuvince 13y agoAgreed. From what I've read [1], the syntax rules and semantic rules take up about 40 pages with the rest of the book being "introduction, exposition, core material, appendices and index". [1] http://mythryl.org/my-Mythryl_is_not_just_a_bag_of_features_like_most_programming_languages_.html#section:notes:engineered http://mythryl.org/my-Mythryl_is_not_just_a_bag_of_features_... (go to the middle of the page or search for "pages")
- Aqwis 13y agoThe Python Language Reference is only 100 or so pages. I'm not sure how different the PLR is from a "real" spec, however.
- derefr 13y agoI wouldn't say that any of that means that C itself is complex. It means your computer's native instruction-set architecture is complex (and full of undefined behavior), and C, being simple, just gives you relatively transparent access to it rather than trying to abstract it and standardize it and generally clean it up.
- aidenn0 13y agoWrong; one example: your CPU's instruction set has well defined behavior for signed integer overflow. C doesn't.
- btilly 13y agoThat does not contradict what was said. C says "undefined" so that compilers are free to simply let each CPU do whatever is easiest for that CPU. On any particular CPU with a particular compiler you get consistent behavior. But when you switch CPUs, watch out. Thus YOUR SPECIFIC CPU's instruction set may be well-defined. But if you say "your CPU" to a group of people with different CPUs, there may be no simple statement that is well-defined and generalizable across all of them.
- pascal_cuoq 13y ago> C says "undefined" so that compilers are free to simply let each CPU do whatever is easiest for that CPU. On any particular CPU with a particular compiler you get consistent behavior. But when you switch CPUs, watch out. That was how it worked in the 1990s. Nowadays, a C programmer needs to figure out which undefined behaviors are justifiable and which should be avoided at all costs because they will be used by the compiler to justify optimizations. And Signed arithmetic overflow used to be in the first category, now it is in the second one. So is the use of uninitialized variables. Not to appeal to authority, but I worry about these things for a living: http://blog.frama-c.com/index.php?post/2013/07/11/Arithmetic-overflows-in-Fluorine http://blog.frama-c.com/index.php?post/2013/07/11/Arithmetic... http://blog.frama-c.com/index.php?post/2013/05/20/Attack-by-Compiler http://blog.frama-c.com/index.php?post/2013/05/20/Attack-by-... If you cannot be bothered to read that much, then please simply compile int f(int x) { return x + 1 > x; } at different optimization levels with the compiler you already have, and observe the values for f(INT_MAX) in each case.
- ctdonath 13y agoThe size of the spec in part reflects the intensity and vastness of real world usage, having to get into painstaking detail regarding otherwise ignorable issues. It agonizes over minutiae because enough people are going into those dark corners that rules and expectations must be laid out for a profusion of obscurities. If you have a relatively small number of users who understand the gist of the language, it can be expressed in a few pages. If you want the language to be useful beyond a trivial description, you'll have to add some complexity, which leads to weaknesses. If your language becomes world-scale popular, you're going to have to spend specification space dealing with things like explaining that under certain conditions yes in fact a Boolean variable can have a value other than "true" or "false".
- joshuaellinger 13y agoBy small, I meant that you can hold every little part of the syntax and normal behavior in your head pretty easily. Yeah, there are edge cases (important ones even) and you can build god-awful complicated expressions if you want. But look a the K&R books -- they are slim. It just not that big. By not-doing-stuff-behind-your-back, I mean that you can map the code you write into machine instructions in many cases. An optimizer will move stuff around on you but it is typically local(-ish) manipulations. That's much less black magic that garbage collection. You have to manage memory yourself. By being-ballet, I mean that it is hard. I am a swing dancer. The best dancers I know all took ballet classes as kids. None of them dance ballet today. It is probably coincidence/age but the best programmers I know all spent a decade or more working in C. I think working in C gives you a level of understanding about what a computer does that a higher level language doesn't. That said -- if you want to get stuff done, use a higher level language. I'm really good at C coding but I'm x2-10 faster when in C#.
- anon1385 13y ago>By small, I meant that you can hold every little part of the syntax and normal behavior in your head pretty easily Well I think that is where we disagree. I don't find it easy to hold all the rules of C in my head. It's plenty large enough that there are things I hardly ever use. Even the common parts of the language like arithmetic on integer types can get complicated very quickly if you want to be sure your code contains no undefined behaviour or works correctly for INT_MAX etc. Often you have to understand not just what the standard says but also what your compiler/target architecture does for the many implementation defined things. >Yeah, there are edge cases (important ones even) and you can build god-awful complicated expressions if you want. You don't have to write long or complicated expressions for things to get tricky. That was the point of this example: http://blog.regehr.org/archives/482 http://blog.regehr.org/archives/482 It's a simple function yet mainstream compilers got it wrong for years. I don't consider these things as edge cases because they come up all the time and have caused countless serious bugs in real world C code. >By not-doing-stuff-behind-your-back, I mean that you can map the code you write into machine instructions in many cases. That is becoming less and less true with modern compilers. Vectorizers will kick in at different optimisation levels and depending on various heuristics that I'm not sure even the compiler authors would be confident in predicting for more complex code. They can perform a lot of complicated transforms. Undefined behaviour means lots of code can be modified in fairly unintuitive ways. >An optimizer will move stuff around on you but it is typically local(-ish) manipulations clang includes a link time optimizer: http://www.llvm.org/docs/LinkTimeOptimization.html#example-of-link-time-optimization http://www.llvm.org/docs/LinkTimeOptimization.html#example-o...
- joshuaellinger 13y ago1. Your first example ends with "C may be a small language, but it’s not a simple one." By your argument, it feels like you'd say the language is large because you have to understand that rule if you really cared about consistent results everywhere. I agree with the author -- small but not simple. Platforms differ and it leaks into the language precisely because it is such a simple language. It is simple like HTML 1.0 is simple. It is simple like the Bill of Rights is simple. There are a limited set of rules but there a lot of undefined behavior as a result. I guess you believe HTML is complex because you have to understand CSS these days to do anything. 2. I find it interesting that you use what is clearly an edge case and then argue that because they are common it is not an edge case. The example of "int foo(char x) { char y = x; return ++x > y; }" is almost the textbook example of an edge case. Seriously, don't trust me. Ask around and see if you can find 10 people who know C well that would consider this mainstream (excluding embedded developers). There are countless serious bugs in real world C code because (a) there is so much damn real world C code and (b) it doesn't exactly protect from shooting yourself in the foot. My experience working with a big program that ran on Windows, 2-5 flavors of UNIX, and VMS (both DEC and Alpha) is that the bulk of the real world errors in C code do not have anything to do with undefined behavior across platforms. They have to do with memory management (null pointers, buffer overruns, etc) and poorly written macros. 3. I agree with you that adding optimizers introduce a whole set of things you have to hold in your head that push you into 'large' territory. Just like programming on a GPU makes you rethink everything about how you organize code and writing for embedded code has its own set of rules. But how does that make the language large? My point is that a language that says "we manage memory on your behalf inside of a VM" is doing a lot more for you. Boy, are we beating this thing to death or what... ... I like C quite a bit but I wouldn't want to make a living programming in it today. I drop back into C when I have a compute kernel that needs it but 99% of the code remains in C#. C is small but too simple for the problems that I'm solving today.