TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

Proposal for a Friendly Dialect of C

218 点作者 0x09超过 10 年前

17 条评论

Verdex超过 10 年前
I think it&#x27;s interesting that the famous tech companies are all developing programming languages that are less managed than java&#x2F;c#, but more predictable (read: less undefined behavior) than c&#x2F;c++. Facebook seems interested in D, Apple has Swift, Microsoft has some sort of secret project they are working on, Google has Go, and Mozilla has Rust. Even c++ seems to be attempting to modernize with the new additions to it&#x27;s spec. And now we see a desire for c itself to change. I wonder if our industry is at a turning point where managed languages aren&#x27;t quite cutting it, but no one is comfortable going back to the &#x27;good old days&#x27;.<p>On a personal note, I like the idea of friendly C so much that I finally made an HN account. One of my favorite things to do is to take things apart and understand them. I was mortified when I learned the real meaning of undefined behavior in c&#x2F;c++. It seems like the only way to be sure you understand a C program is to check the generated machine code. Even worse is that when I try to talk to other developers about undefined behavior, I tend to get the impression that they don&#x27;t actually understand what undefined behavior means. I can&#x27;t think of a way to verify what they think it means without insulting their intelligence, but hopefully the existence of something like friendly C will make it an easier discussion to have.
评论 #8236410 未加载
评论 #8236069 未加载
评论 #8241827 未加载
评论 #8246764 未加载
userbinator超过 10 年前
I really like these suggestions since they can be summed up in one sentence: they are what C programmers who write code with UB would already expect any reasonably sane platform would do. I think it&#x27;s definitely a very positive change in attitude from the &quot;undefined behaviour, therefore <i>anything</i> can happen&quot; that resulted in compilers&#x27; optimisations becoming very surprising and unpredictable.<p><i>Rather, we are trying rescue the predictable little language that we all know is hiding within the C standard.</i><p>Well said. I think the practice of UB-exploiting optimisation was completely against the spirit of the language, and that the majority of optimisation benefits happen in the compiler backend (instruction selection, register allocation, etc.) At least as an Asm programmer, I can attest that IS&#x2F;RA can make a <i>huge</i> difference in speed&#x2F;size.<p>The other nice point about this friendly C dialect is that it still allows for much optimisation, but with a significant difference: instead of basing it on assumptions of UB defined by the standard, it can still be done based on proof; e.g. code that can be proved to be unneeded can be eliminated, instead of code that may invoke UB. I think this sort of optimisation is what most C programmers intuitively agree with.
评论 #8236994 未加载
simias超过 10 年前
I can fit the proposed changes in two categories:<p>* Changes that replace undefined behaviours with undefined values. This makes it easier to catch certain types of coding errors at the cost of certain kinds of optimizations.<p>* Changes that remove undefined behaviours (wrapping arithmetic, memcpy&#x2F;memmove, aliasing rules).<p>I&#x27;m comfortable with the first kind, although you can already achieve something very similar to that with most compiler (as far as I know) by building with optimizations disabled. Also stuff like missing return values generates a warning in any compiler worth using, if you ignore that kind of warnings you can only blame yourself.<p>The 2nd kind bothers me more, because it makes otherwise invalid C code valid in this dialect. I&#x27;m worried this makes things even more difficult to explain to beginners (and not so beginners, I still have to check the aliasing rules from time to time to make sure the code I&#x27;m writing is valid).<p>Even if you&#x27;re very optimistic this friendly C is not going to replace daddy anytime soon. There&#x27;ll be plenty of C code out there, plenty of C toolchains, plenty of C environment where the definition of friendliness is having a dump of the registers and stack on the UART in case of an error. Plenty of environments where memcpy is actually memcpy, not memmove.<p>For that reason I&#x27;d be much more in favour of advocating the use of more modern alternatives to C (and there are a bunch of those) rather than risking blurring the lines some more about what is and isn&#x27;t undefined behaviour in C.
评论 #8234375 未加载
twoodfin超过 10 年前
Can someone give a rationalization of why a &quot;friendly&quot; dialect of C should return unspecified values from reading uninitialized storage? Is the idea that all implementations will choose &quot;0&quot; for that unspecified value and allow programmers to be lazy?<p>I&#x27;d much rather my &quot;friendly&quot; implementation immediately trap. Code built in Visual C++&#x27;s debug mode is pretty reliable (and useful) in this regard.<p>EDIT: It occurs to me that this is probably a performance issue. Without pretty amazing (non-computable in the general case?) static analysis, it would be impossible to tell whether code initializes storage in all possible executions, and using VM tricks to detect all uninitialized access at runtime is likely prohibitively expensive for non-debug code.
评论 #8234192 未加载
评论 #8234187 未加载
评论 #8234180 未加载
评论 #8234200 未加载
cousin_it超过 10 年前
Sometime ago I came up with a simpler proposal: emit a warning if UB exploitation makes a line of code unreachable. That refers to actual lines in the source, not lines after macroexpansion and inlining. Most &quot;gotcha&quot; examples with UB that I&#x27;ve seen so far contain unreachable lines in the source, while most legitimate examples of UB-based optimization contain unreachable lines only after macroexpansion and inlining.<p>Such a warning would be useful in any case, because in legitimate cases it would tell the programmer that some lines can be safely deleted, which is always good to know.
评论 #8234350 未加载
DSMan195276超过 10 年前
The kernel uses -fno-strict-aliasing because they can&#x27;t do everything they need to do by adhering strictly to the standard, it has nothing to do with it being to hard (The biggest probably being treating pointers as integers and masking them, which is basically illegal to do with C).<p>IMO, this idea would make sense if it was targeted at regular software development in C (And making it easier to not shoot yourself in the foot). It&#x27;s not as useful to the OS&#x2F;hardware people though because they&#x27;re already not writing standards-compliant code nor using the standard library. There&#x27;s only so much it can really do in that case without making writing OS or hardware code more annoying to do then it already is.
otikik超过 10 年前
I haven&#x27;t done C in at least a decade, so bare with me.<p>&gt; 1. The value of a pointer to an object whose lifetime has ended remains the same as it was when the object was alive<p>&gt; 8. A read from uninitialized storage returns an unspecified value.<p>Isn&#x27;t that already the case in C?<p>&gt; 3. Shift by negative or shift-past-bitwidth produces an unspecified result.<p>What is the meaning of &quot;an unspecified result&quot; there?<p>&gt; 4. Reading from an invalid pointer either traps or produces an unspecified value.<p>What is the meaning of &quot;traps&quot; here? Is it the same as later on (&quot;math- and memory-related traps&quot;)?
评论 #8234104 未加载
评论 #8233928 未加载
评论 #8233976 未加载
评论 #8233951 未加载
评论 #8233938 未加载
revelation超过 10 年前
Plenty of architectures do not trap at null-pointer dereferencing (they don&#x27;t have traps). Some (like AVR) are not <i>arcane</i>, they are one of the best excuses for still writing C nowadays.
评论 #8234458 未加载
评论 #8234599 未加载
jhallenworld超过 10 年前
I have strong feelings that the C standard (and, by extension, C compilers) should directly support non-portable code. It means many behaviors are not &quot;undefined&quot;- instead they are &quot;machine dependent&quot;. Thus overflow is not undefined- it is _defined_ to depend on the underlying architecture in a specific way.<p>C is a more useful language if you can make machine specific code this way.<p>I&#x27;m surprised that some of the pointer math issues come up. Why would the compiler assume that a pointer&#x27;s value is invalid just because the referenced object is out of scope? That&#x27;s crazy..<p>Weird results from uninitialized variables can sometimes be OK. I would kind of accept strange things to happen when an uninitialized bool (which is really an int) is not exactly 1 or 0.<p>Perhaps a better way to deal with the memcpy issue is this: make memcpy() safe (handles 0 size, allows overlapping regions), but create a fast_memcpy() for speed.
评论 #8235363 未加载
Roboprog超过 10 年前
Back in 1990, it didn&#x27;t take long to figure out that (Borland) Turbo Pascal was much less insane than C. Unfortunately, it only ran on MS-DOS &amp; Windows, whereas C was everywhere.<p>Employers demanded C programmers, so I became a C programmer. (now I&#x27;m a Java programmer, for the same reason, and think it&#x27;s also a compromised language in many ways)<p>For anybody who is willing to run a few percent slower so that array bounds get checked, there is now an open source FreePascal environment available, so as not to be dependent on the scraps of Borland that Embarcadero is providing at some cost. Of course, nobody is going to hire you to use Pascal. (or any other freaky language that gets the job at hand done better than the current mainstream Java and C# languages)
评论 #8237468 未加载
sjolsen超过 10 年前
I don&#x27;t really see what&#x27;s to be accomplished by most of the points of this. A program that invokes undefined behaviour isn&#x27;t just invalid; it&#x27;s almost certainly _wrong_. Shifting common mistakes from undefined behaviour to unspecified behaviour just makes such programs less likely to blow up spectacularly. That doesn&#x27;t make them correct; it makes it harder to notice that they&#x27;re incorrect.<p>Granted, not everything listed stops at unspecified behaviour. I&#x27;m not convinced that that&#x27;s a good thing, though. Even something like giving signed integer overflow unsigned semantics is pretty effectively useless. Sure, you can reasonably rely on twos-complement representation, but that doesn&#x27;t change the fact that you can&#x27;t represent the number six billion in a thirty-two bit integer, and it doesn&#x27;t make 32-bit code that happens to depend on the arithmetic properties of the number six billion correct just because the result of multiplying two billion by three is well-defined.<p>Then there&#x27;s portability. Strict aliasing is a good example of this. Sure, you can access an &quot;int&quot; aligned however you like on x86. It&#x27;ll be slow, but it&#x27;ll work. On MIPS, though? Well, the compiler could generate code to automate the scatter-gather process of accessing unaligned memory locations. This is C, though. It&#x27;s supposed to be close to the metal; it&#x27;s supposed to force-- I mean, let you do everything yourself, without hiding complexity from the programmer. How far should the language semantics be stretched to compensate for programmers&#x27; implicit, flawed mental model of the machine, and at what point do we realize that we already have much better tools for that level of abstraction?
评论 #8234961 未加载
评论 #8235147 未加载
评论 #8236109 未加载
评论 #8235495 未加载
Someone超过 10 年前
<i>&quot;Reading from an invalid pointer either traps or produces an unspecified value.&quot;</i><p>That still leaves room for obscure behavior:<p><pre><code> if( p[i] == 0) { foo();} if( p[i] != 0) { bar();} </code></pre> Calling foo might change the memory p points at (p might point into the stack or it might point to memory in which foo() temporarily allocates stuff, or the runtime might choose to run parts of free() asynchronously in a separate thread), so one might see cases where both foo and bar get called. And yes, optimization passes in the compiler might or might not remove this problem.<p>Apart from truly performance-killing runtime checks i do not see a way to fix this issue. That probably is the reason it isn&#x27;t in the list.<p>(Feel free to replace p[i] by a pointer dereference. I did not do that above because I fear HN might set stuff in <i>italics</i> instead of showing asterisks)
评论 #8234806 未加载
rwmj超过 10 年前
I would think also something like &quot;if I write a piece of code, the compiler should compile it&quot;, perhaps &quot;or else tell me with a warning that it isn&#x27;t going to compile it&quot;.
Ono-Sendai超过 10 年前
Not sure this is a good idea. Since a lot of behaviour becomes implementation-defined, code written in this dialect will not be portable.
评论 #8235027 未加载
kazinator超过 10 年前
&gt; 1. The value of a pointer to an object whose lifetime has ended remains the same as it was when the object was alive.<p>This does not help anyone; making this behavior defined is stupid, because it prevents debugging tools from identifying uses of these pointers as early as possible. In practice, existing C compilers do behave like this anyway: though any use of the pointer (not merely dereferencing use) is undefined behavior, in practice, copying the value around does work.<p>&gt; 2. Signed integer overflow results in two’s complement wrapping behavior at the bitwidth of the promoted type.<p>This seems like a reasonable request since only museum machines do not use two&#x27;s complement. However, by making this programming error defined, you interfere with the abilty to diagnose it. C becomes friendly in the sense that assembly language is friendly: things that are not necessarily correct have a defined behavior. The problem is that then people write code which depends on this. Then when they do want overflow trapping, they will have to deal with reams of false positives.<p>The solution is to have a declarative mechanism in the language whereby you can say &quot;in this block of code, please trap overflows at run time (or even compile time if possible); in this other block, give me two&#x27;s comp wraparound semantics&quot;.<p>&gt; 3. Shift by negative or shift-past-bitwidth produces an unspecified result.<p>This is just word semantics. Undefined behavior, unspecified: it spells nonportable. Unspecified behavior may seem better because it must not fail. But, by the same token, it won&#x27;t be diagnosed either.<p>A friendly C should remove all <i>gratuitous</i> undefined behaviors, like ambiguous evaluation orders. And diagnose as many of the remaining ones which are possible: especially those which are errors.<p>Not all undefined behaviors are errors. Undefined behavior is required so that implementations can extend the language locally (in a conforming way).<p>One interpretation of ISO C is that calling a nonstandard function is undefined behavior. The standard doesn&#x27;t describe what happens, no diagnostic is required, and the range of possibilities is very broad. If you put &quot;extern int foo()&quot; into a program and call it, you may get a diagnostic like &quot;unresolved symbol foo&quot;. Or a run-time crash (because there is an external foo in the platform, but it&#x27;s actually a character string!) Or you may get the expected behavior.<p>&gt; 4. Reading from an invalid pointer either traps or produces an unspecified value. In particular, all but the most arcane hardware platforms can produce a trap when dereferencing a null pointer, and the compiler should preserve this behavior.<p>The claim here is false. Firstly, even common platforms like Linux do not actually trap null pointers. They trap accesses to an unmapped page at address zero. That page is often as small as 4096 bytes. So a null dereference like ptr[i] or ptr-&gt;memb where the displacement goes beyond the page may not actually be trapped.<p>Reading from invalid pointers already has the de facto behavior of reading an unspecified value or else trapping. The standard makes it formally undefined, though, and this only helps: it allows advanced debugging tools to diagnose invalid pointers. We can run our program under Valgrind, for instance, while the execution model of that program remains conforming to C. We cannot valgrind the program if invalid pointers dereference to an unspecified value, and programs depend on that; we then have reams of false positives and have to deal with generating tedious suppressions.<p>&gt; 5. Division-related overflows either produce an unspecified result or else a machine-specific trap occurs.<p>Same problem again, and this is already the actual behavior: possibilities like &quot;demons fly out of your nose&quot; does not happen in practice.<p>The friendly thing is to diagnose this, always.<p>Carrying on with a garbage result is anything but friendly.<p>&gt; It is permissible to compute out-of-bounds pointer values including performing pointer arithmetic on the null pointer.<p>Arithmetic on null works on numerous compilers already, which use it to implement the offsetof macro.<p>&gt; memcpy() is implemented by memmove().<p>This is reasonable. The danger in memcpy not supporting overlapped copies is not worth the microoptimization. Any program whose performance is tied to that of memcpy is badly designed anyway. For instance if a TCP stack were to double in performance due to using a faster memcpy, we would strongly suspect that it does too much copying.<p>&gt; The compiler is granted no additional optimization power when it is able to infer that a pointer is invalid.<p>That&#x27;s not really how it works. The compiler assumes that your pointers are valid and proceeds accordingly. For instance, aliasing rules tell it that an &quot;int *&quot; pointer cannot be aimed at an object of type &quot;double&quot;, so when that pointer is used to write a value, objects of type double can be assumed to be unaffected.<p>C compilers do not look for rule violations as an excuse to optimize more deeply, they generally look for opportunities based on the rules having been followed.<p>&gt; When a non-void function returns without returning a value, an unspecified result is returned to the caller.<p>This just brings us back to K&amp;R C before there was an ANSI standard. If functions can fall off the end without returning a value, and this is not undefined, then again, the language implementation is robbed of the power to diagnose it (while remaining conforming). Come on, C++ has fixed this problem, just look at how it&#x27;s done! For this kind of undefined behavior which is erroneous, it is better to require diagnosis, rather than to sweep it under the carpet by turning it into unspecified behavior. Again, silently carrying on with an unspecified value is not friendly. Even if the behavior is not classified as &quot;undefined&quot;, the value is nonportable garbage.<p>It would be better to specify it a zero than leave it unspecified: falling out of a function that returns a pointer causes it to return null, out of a function that returns a number causes it to return 0 or 0.0 in that type, out of a function that returns a struct, a zero-initialized struct, and so on.<p>Predictable and portable results are more friendly than nonportable, unspecified, garbage results.
评论 #8235494 未加载
dschiptsov超过 10 年前
Show us, please, how the dialect of C which is used for development of the Plan9 is unfriendly and not good-enough.
评论 #8235933 未加载
allegory超过 10 年前
It is friendly, if you&#x27;re not an idiot. Not becoming an idiot is the best solution (practice, lots).<p>Edit: perhaps I made my point badly but don&#x27;t assume that you&#x27;ll ever be good enough to not be an idiot. Try to converge on it if possible through. I&#x27;m still an idiot with C and I&#x27;ve been doing it since 1997.
评论 #8234138 未加载
评论 #8233710 未加载
评论 #8233741 未加载
评论 #8234124 未加载
评论 #8234150 未加载
评论 #8233706 未加载
评论 #8233822 未加载