tl;dr - C has some undefined behavior, if you plan on things working based on your experience with one compiler and one computer, you will be surprised.
I wonder how many C programmers have experience with writing multi platform/compiler code. I have done a lot of C and C++ but never had to port it so I would not be surprised if I had made a ton of mistakes.
> And at this point, I only have to apologize. The test is clearly provocative and may even be a little offensive. I’m sorry if it causes any aggravation.
Failed all of them. I totally agree with what the author is writing. Don’t presume anything but measure. I also tend to code in a way that it’s not necessary to know all the intricacies. Its not as clever as many like and often makes for longer code but is usually easier to read.
I think that's exactly the wrong takeaway here. Most of these have a well-defined result on a given platform (host + abi). You can measure that result. But it'll be different on a different platform. And the others don't have a well-defined result — a real-world compiler will produce a result, and you can measure that, but it might be different tomorrow. Unless you know the difference between platform-specific behavior and undefined behavior, you don't know which ones to avoid.
Sad to say I scored perfectly, due to a similar early disillusionment on embedded platforms, and years of pain porting code between 16- and 32-bit architectures when the author thought they knew the size of “int”.
One immediate redflag I have noticed is using "int", "char", "short" as if they have a definite size. They don't. C standard only guarantees a minimum size. For example, many PDPs are 36-bit. Assuming the size of a variable is a common practice nowadays, but at least one should use uint8_t, int32_t, etc. from stdint.h.
But I was still tricked, it should be obvious in hindsight, 12 years of schooling led me to think: If the author was asking these questions, at least one or two questions must be answerable (even if it's technically incorrect, but you'd better to guess the original intention of a question). So I still tried to guess and got two wrong answers... Get to be careful next time...
4/5 here. In fact after the third question provided "I don't know" as an answer I started to suspect something was up — especially since the author said only one answer was the right one ... why even provide "I don't know" then, I wondered?
I knew "int" was sort of platform-dependent (was 16-bits generally when I was learning to code, later 32-bit became more typical) — so combined with that niggle and all the "I don't knows", I (correctly) reevaluated by first couple of answers.
Still, didn't realize the last one was compiler-dependent.
I scored perfectly. I've been programming in C since 1989, on various platforms (started with the Amiga, then VAX/VMS, Linux x86, and various embedded systems.)
At the end of the test, the author talks about automation programming for a nuclear power plant. I don’t think I could ever sleep the same at night after writing something like that.
Particularly writing it in C... It isn't a language well suited to be fully defined (see this very article for why), and no, Rust/Go aren't either. But Ada derivative or Haskell perhaps, there's some amazing tooling for safety critical systems and the languages themselves lend themselves to exposing side-effects.
Ada, maybe? I don't know enough about it to comment. You definitely don't want to use Haskell for that sort of work load, though, at least not directly. Laziness-by-default is precisely the sort of hard-to-reason-about logic you don't want in that sort of application.
That said, if I I had no alternative but to try and tackle this problem, I would seriously consider a strategy where I would write a Haskell program that would generate the actual program (potentially in ASM directly) for me.
> I don’t think I could ever sleep the same at night after writing something like that
In these situations, you likely know your hardware and know your compiler, so you can actually provide an answer for 4 of the questions. The last one is a situation where someone should tell you not to get cute in the code review.
I wrote C in telecom and finance and in both places we enforced a rule: when you define a structure, put a comment after each element that says what you think the structure offset should be, and at the end of the structure #define a constant that says what you think the size of the structure should be. In a code review, if anyone noticed something that didn't look right, you could talk about it. In testing, you could also check that sizeof(foo_s) == FOO_S_SIZE and fail if it wasn't.
In some of our code, we would test the size of various types and structures on startup and immediately exit if they weren't what we expected. We'd print type sizes to logs to help debugging if there was ever a problem. We were supporting a single code base that ran on big endian, little endian, X86, Itanium, SPARC, ARM. Compilers change, but automated tests of type and structure sizes catch things immediately.
It may sound like a lot of work, but it actually isn't at all. It also helps a lot with long-term maintainability.
> In some of our code, we would test the size of various types and structures on startup
This is one of the things that C++ has actually improved a lot recently: doing this with static_assert is much nicer in terms of catching problems early... And yes, it's great for long-term maintainability.
I answered Idk to all. After the first two, the pattern became clear and I felt like if I wrote a compiler myself, the answers could be very different.
I've worked on 16 bit C code, 32 and now 64 bit code. So I knew that the behavior was implementation and optimization dependent. :)
I failed on one, number 4. I bravely assumed 16 bit integers cannot exist. Can anyone name a concrete platform/compiler where int is/was 16 bit. Or is this just a theoretical option left open by the spec?
Many C compilers that target 8-bit (sometimes 16-bit) machines. MOS 6502, Zilog Z80, and Motorola 6809. Modern examples include Intel 8051, AVR and PIC.
Turbo C on MS-DOS for one. In fact 16-bit int was the norm on that platform, because the architecture didn't have 32-bit general purpose registers.
In the C89 days, you'd use 'short' in aggregates (structs and array) for values you knew wouldn't exceed 16 bits so didn't want to potentially waste space; 'long' in situations where you knew 16 bits wouldn't be enough; and 'int' the rest of the time (where 16 bits was enough, and there weren't any storage benefits to outweigh the performance benefit of using the native word size).
Why couldn't they? C had already existed for a couple of decades when 32-bit machines started getting popular. `int`, as the default integer type, usually is the size of the machine word for best performance. It would make no sense to have slow, emulated 32-bit ´int`s on a 16-bit system, never mind 8-bit ones.
My amiga c compiler (aztex manx) allowed either 16 or 32 bit ints. All/most systems libraries used 32 bit parameters, despite this I insisted on the 16 bit version "for performance". In hindsight this was sort of insane: one missing L (say in "1L" for casting to long) meant a not so quick floppy disk reboot). :-)
Anyhow, for a computer with 16 bit wide data bus, having 16 bit ints might be justified by performance (and/or reducing memory usage.)
By that logic, someone who says "the origin of handedness is unknown" contradicts himself, since he does know the origin of handedness to be "unknown".
There are a few problems with the questionnaire. "I don't know" is pretty generic choice to be given/choosen.
Say for example, in question 5, the statement "return i++ + ++i;" is undefined, because the value of i is read and modified twice in a single sequence point (and of course, the order of addition is unspecified), which is not allowed in C. So the answer is "Undefined." (The explanation given in the page not accurate enough in terms of C)
And for question 1, the code is valid, but the result is not strictly defined. It depends on the implementation. So the answer is "Implementation defined."
The usage of "main()" hurts me, the strictly conforming way is to write it as "int main(void)" (or similar)
I feel like the questionnaire piss off people who really knows C.
Somewhat related, my introductory classes involved a lot of games around pre- and post- increments and short circuiting. While I get that understanding these operations is fundamentally important, is understanding ridiculous combinations of them important? I mean, these were the basis of large portions of some quizzes and midterms. I get playing with them from a theoretical perspective, as this can literally be done in many languages, but why force freshmen to play this deep mental gymnastics? Maybe a play at making the classes weeder classes and no other reason.
I don't know how convoluted the questions on your midterms were but one good reason that kind of irritating thing pops up in tests is that it's quite common in real world C code. Think of the old K&R string copy example.
The questions are testing whether you really understand the basic rules of the language. Often times, the best way to test whether you really get the rules is to raise them in some odd context, so that you can’t just pattern match to figure out the result.
1) This made me curious. Are any of the compilers in real use nondeterministic?
2) Probably that's not needed? A normal optimizing compiler just inlines the function somewhere new — and boom? Then again, can that really happen with practical contemporary compilers and this exact statement?
Most compilers are nondeterministic in small ways. For example, it's common to use hash tables that are keyed by pointer address and then iterate over the entries in storage order, so the order in which certain things are emitted will change from run to run. This is why "deterministic builds" are such a big deal, and not just an obvious thing that you get for free.
I don't know what the chances are that such a thing could ever translate into good assembly being emitted in one run and bad assembly being emitted in the next.
Register allocation can be quite tricky, and sometimes it can only explore a small part of the problem space, so if you don't start the algorithm with exactly the same seed you might end up with significantly different code in certain functions.
Whoa there! You mean “unspecified behavior”. int i = [unspecified] means that i has some value, but the spec doesn’t determine the value. Undefined behavior means that all your secrets might be sold to the highest bidder, your centrifuges might explode, and your computer is now full of ransomware.
> Whoa there! You mean “unspecified behavior”. int i = [unspecified] means that i has some value, but the spec doesn’t determine the value. Undefined behavior means that all your secrets might be sold to the highest bidder, your centrifuges might explode, and your computer is now full of ransomware.
That's only when using Boehm GC[0] in kernel device drivers that self-modify. Or any MSVC binary.
Whilst my other comment was intended to be jovial, it is hard to say if that was accurately conveyed. So this one will be serious.
The original problem definition, as specified by @pksadiq, read thusly:
> Say for example, in question 5, the statement "return i++ + ++i;" is undefined ...
This inspired a response by @Filligree of:
> Compile it, look at the assembly. You can know. The answer will vary from place to place, but it isn't non-existent.
Given the original constraint of an undefined statement result, and the suggested activity to address same, I posited that the recommended action is an exemplar of observing the product of undefined behaviour.
You then contributed:
> You mean “unspecified behavior”.
As per c-faq.com[0], there are three categories identified relating to this topic:
1 - implementation-defined: The implementation must pick some behavior; it may not fail to compile the program.
2 - unspecified: Like implementation-defined, except that the choice need not be documented.
3 - undefined: Anything at all can happen; the Standard imposes no requirements.
Whereas you imply a standards-conformant implementation of "return i++ + ++i;" is unspecified (category #2), it is, in fact, undefined (category #3). The support for this assertion is as follows.
As per the same site, Question 3.8[1] includes:
> Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored.
And further states:
> ... if an object is written to within a full expression, any and all accesses to it within the same expression must be directly involved in the computation of the value to be written. This rule effectively constrains legal expressions to those in which the accesses demonstrably precede the modification.
And concludes with an example stating:
> ... the Standard declares that it is undefined, and that portable programs simply must not use such constructs.
Therefore, the original expression presented by @pksadiq is in fact an exemplar of an undefined expression as defined by category #3 shown above. Since both it and the message to which I originally responded satisfy same, I stand by my response given to @Filligree as having had informally defined the standard C concept of "undefined behaviour."
> Whereas you imply a standards-conformant implementation of "return i++ + ++i;" is unspecified (category #2), it is, in fact, undefined (category #3).
You're misreading things. amluto's assertion is that "The answer will vary from place to place, but it isn't non-existent." is a description of category #2. That assertion is basically correct, depending on how exactly you define "place to place".
An informal definition of category #3 is "The answer can vary from place to place, or not exist at all." ideally followed by "It might crash or run unrelated code or even prevent the preceding code from running." It's flat-out wrong to say a value "isn't non-existent" when it comes to source code exhibiting undefined behavior.
Except from the perspective of a pragmatics linguistic analysis, "I don't know" has a social context of "There's an answer, and I don't know it."
In this case, a non-C programmer should answer "I don't know" to all of them. A person with a passing familiarity should answer similarly. A seasoned pro would be forced to answer the same. Making it a rather useless tool for distinguishing people who think they know C but are honest when faced with their limitations or those who truly know it and know the answer is undetermined, which is supposed to be the point of the exercise.
You don't think the claim is justified because some might say otherwise. Some is irrelevant. Some might say lots of mutually exclusive interpretations. It's the author's intent that matters, and the context of the Author's post indicates the some interpretation isn't the author's intent. His post begins with the question "So you think you know C?". He then goes on to present a test that is, by his own words, intended identify to test takers whether or not they really understand the intricacies of C, and to think critically about that source of their knowledge "I had to learn to rely on the standard instead of folklore; to trust measurements and not presumptions; to take “things that simply work” skeptically"
Never once does the author mention that C is confusing, use the word confusing, or otherwise indicate that general idea. If you're getting that impression, it's your own reading into it. I'm not even saying you'd be incorrect, but that's not the author's intent, which was the basis of my comment.
And yet its not confusing. Given the confines of any particular implementation and compiler the behavior can be known without confusion. The author never directly mentions or implies that their intent is to convey that C in confusing. Quite the contrary, they indicate their intent is to demonstrate that certain segments of people who believe they know C don't in fact understand its intricacies.
> Given the confines of any particular implementation and compiler the behavior can be known without confusion.
Only through extreme levels of compiler code inspection, as it can vary based on optimization heuristics.
> Quite the contrary, they indicate their intent is to demonstrate that certain segments of people who believe they know C don't in fact understand its intricacies.
Demonstrating that people don't know C is subtly different from an intent of testing whether people know C. The point being made is about C itself.
If what "some" might say about what the author intends is irrelevant, then what you say about what the author intends is also irrelevant, because you are just some person (unless you're the author). My point was why should I trust your interpretation of what the author intends more than anyone else's.
>"So you think you know C?"
That goes along with the interpretation that the point is to illustrate C is confusing. It would go along with something like "You think you know it, you think it's simple, well actually you don't know it, it's confusing."
>intended identify to test takers whether or not they really understand the intricacies of C, and to think critically about that source of their knowledge
Yes, its intent is to indicate to test takers that a lot of them don't really understand the intricacies of C, which demonstrates that C is more confusing than they originally thought.
>Never once does the author mention that C is confusing, use the word confusing, or otherwise indicate that general idea.
Here are some quotes that indicate the idea that C is confusing:
>C is not that simple.
>It’s only reasonable that the type of short int and an expression with the largest integer being short int would be the same. But the reasonable doesn’t mean right for C.
>Actually, it’s much more complicated than that. Take a peek at the standard, you’ll enjoy it.
>The third one is all about dark corners.
>The test is clearly provocative and may even be a little offensive.
Then the author says that he did C for 15 years and thought he knew it, but then realized he didn't. That indicates to me either that the author is saying that he's not smart, or that C is confusing. The second appears to be the point the author is actually making.
My interpretation is based directly on what the author states. Your "some" is based on a vague aggregate group whose interpretations, in aggregate, would be diverse and often contradictory and mutually exclusive. Personally, I trust the explicit an implied interpretation of the author's direct statements than you mere speculation as to what others might interpret.
If you don't like "some" then replace it with me. I interpret it as the author saying C is confusing.
My interpretation is also based on what the author states, fairly explicitly. And I don't think there's anything that explicitly contradicts my interpretation.
You say it's confusing because the author says it's not simple. The same might be said of any language. Or of any learning specialty at all. It's not synonymous with confusing. You're severely stretching the meaning of the author's words when you say the author's point was to say that C is confusing. It's what you infer because you were confused, which points to this being personal to you, not the general intent of the author.
There is a world of difference between "don't know" and "can't know", as the first implies a shortcoming on the side of the developer while second one states that the question is patently meaningless to someone who does master the language.
No, it isn't. The correct answer to #2 is "According to the standard, the result is implementation defined, but on my target platform, 0". "I don't know" is the wrong answer.
The C specification does not say that undefined behaviour must give a deterministic result on a given platform. All you can say is "this one time I compiled and then ran this code, it gave 0". There is no requirement that the code compiles at all, nor that the same compiler on the same platform produces the same binary on every run, nor that the resulting binary produces the same result on every run, nor that the binary produces any result, nor that it doesn't sometimes produce a result and sometimes not, nor that the compiler doesn't sometimes produces a binary and sometimes not ... undefined behaviour is exactly that: undefined behaviour.
I'm well aware of what undefined behavior is. I still know it's undefined behavior and can read my compiler manual to answer the question of how the code behaves. "I don't know" is simply wrong.
> and can read my compiler manual to answer the question of how the code behaves.
Which is both not true (because the compiler manual usually won't define undefined behaviour) and irrelevant (because the questions were about C, not about a compiler).
In the example I choose (#2) most compilers totally specify the behavior. And the question was (right from the article) "what the return value would be?" In order for a function to return, it must be run. In order for a function to be run, it must be compiled. In order for a function to be compiled, there must be a compiler (or interpreter, I suppose).
You're being pedantic about something silly, but you're also wrong in your pedantry.
It’s not just that stuff. What pissed me off was asking about the return code of a comparator. That’s just bad form. You’re only supposed to check for zero or nonzero. I have never used the value beyond that, and if you are, that’s a problem.
You’re incorrect. The result of a comparison is guaranteed to be zero or one in C. Similarly for the exclamation-point “not” operator, and || and &&.
This isn’t a recent standardization; it’s been an explicitly specified feature of C pretty much since the very beginning of the language. See page 7 of the prehistoric https://www.bell-labs.com/usr/dmr/www/cman.pdf
Have you ever worked with a pre-ANSI (K&R) C compiler? Omitting the return type for main is legal in those old compilers. Newer ones give you a warning.
To me "I don't know" is a very apt choice. It makes the point clear that indeed reading the code does not allow you to know the result, which is quite a pitfall.
In your comment you are jumping from "I don't know", which is the first step, to wanting to explain why.
But the point is that I do know what that will print on my computer(s), with my compiler(s), on my architecture(s). "I don't know" is too generic a statement.
I understand your claim. But I've gotta say this claim is maybe a little too aggressive. I know for a fact that on a Sun-3 (68020 SunOS Desktop Pizza Box) using either gcc or the bundled cc, all of them would have been the same answer, and the answer would have been known to the coder, before running that code (unless you unleashed one of the gcc command line dogs). Except maybe #5, because who does this?
There were multiple questions where I would have answered "It's undefined" or "It's implementation defined", but those weren't options. It's not that I don't know the answer; I know the answer ("it's implementation defined according to the spec, but on essentially every relevant platform, the result will be X"), but the "it's implementation defined" part of my answer isn't an option, so the only possible answer becomes "on essentially every relevant platform, the answer is X".
Using "I don't know" as a substitute for "I know that the standard clearly covers this, and it says that the result depends on the implementation" does seem to be designed to piss off people who know C. If they really wanted to get the point across that you don't know what is and isn't implementation defined or undefined, they shouldn't be using vague questions to mislead people; they should just plainly ask questions which people don't know the answer to.
I hate this kind of questioning where you 100% know the subject matter the quiz is asking about, but the question and possible choices is so vague you have to try to interpret what you suspect the person who wrote the quiz wants the answer to be. I once had an exam which was full of that kind of multiple choice question, and guessed the exam author's intentions wrong on most of them.
the quiz asks what each one would evaluate to. “Undefined” or “implementation dependent” are not answers to that question. “I don’t know [because it is undefined]” or “I don’t know [based on the given information]” are logically consistent answers to the question that was asked
Ok, say I made a quiz where I ask you about what `((((16 <= 16) << 16) >> 16) <= 16)` evaluates to. I could give you the options 0, 1, 16, or "I don't know". If I as a quiz author wanted to test your knowledge about shift operators and esoteric uses of equality operators and your ability to reason through an expression, I would mark "1" as correct, and "0", "16", and "I don't know" as incorrect (I would include the "I don't know" option just because that's a common thing to do in quizzes, to not force people to guess one of the options if they don't actually know the answer).
My point isn't that there's no logically consistent answer. My point is that there are _two_ logically consistent answers, and which one is correct depends on the unknowable state of mind of the quiz author. On the other hand, if the options included "It's implementation defined" or "it's undefined", the author would have made their expectations clear, and the quiz would actually test people's knowledge of C rather than people's ability to try to reason about what sort of answer the author expects.
> I feel like the questionnaire piss off people who really knows C.
I hated this test. I’ve spent 12 years working on C targeting various flavors of arm and x86.
Just because the behavior is undefined when compiled without warnings and run on a Soviet water integrator doesn’t mean the language is undefined for the 99.995% of the industry uses.
Behavior of c89 or later with -Wall -Werror on modern clang, gcc, icc, visual studio, is well understood on arm, x86, mips, risc, ppc, Cortex-m and just about every other hardware architecture.
But, C is a pia, and I’ve been using rust instead :)
There is a minor error in the explanation for the third one: the minimum allowed value for CHAR_BIT in C is 8 (it does not affect the result, principally because the value of ' ' could be anything in the range of char).
That's the one where I wanted most to quibble with the "explanation" of why there is no set answer: To me, the top of the line answer is that the value of the string constant ' ' as an integer is implementation-defined (or maybe it depends on the execution environment? See, I'll get the precise wording wong too); anyway, space is 32 in ASCII but 64 in EBCDIC. The most you could say is that it's not zero (and maybe that it's not -1? I'd have to check how EOF is defined)..
5/5, but I don't think this test is very good at capturing the more obscure features of C, they all just deal with the fact that platforms have different datatypes/alignment requirements, except for the last one. I think a better example would be the following:
int a=1, b=2, i, j;
i = a += 2, a + b;
j = (a += 2, a + b);
Whats the value of a, b, i, j? Hint: i and j are different.
Which begs the question, why does C have those features in the first place? The only C code where it's reasonably common seems to be in crypto algorithms.
This is a perfect example of what I hate about some tests.
Didn't quite know the purpose of the test, there's a difference between code for any machine and any compiler and gcc running on some vanilla x86, which is pretty common, and could have been the content (ex: you say it's undefined, that's obvious, everyone knows that already, but it's still deterministic... Here's a breakdown of what happens in practice, blah blah blah). There's a real difference between the kind of "knowing" here and say the kind of "knowing" with unallocated pointers.
If it said "esoteric implementation on exotic hardware" then it's easy, you know what they are trying to do. If it said C89 you also know what's up. But how it's presented, it's a guess.
This was endemic throughout schooling. Instructors would say "just do your best" and I'd be like "wtf? There's like 2,3, maybe 4 perspectives on this with different answers depending on how clever you're trying to be or what you're trying to get at... Might as well put "I'm thinking of a number 1 through 5" on the exam".
You can easily get bitten by at least one of those examples just by compiling your application written on x86 to ARM to get it running on Android, so not sure if it's as esoteric as you think.
Funny. My first thought of question one is we'd need to make assumptions about which architecture we're working on to know this answer. By the time I got to question 3, I realized the author's trend. This is both the curse and blessing of C, a language that gives you just barely a high level translation layer over the raw silicon.
343 comments
[ 3.0 ms ] story [ 261 ms ] threadI think that's exactly the wrong takeaway here. Most of these have a well-defined result on a given platform (host + abi). You can measure that result. But it'll be different on a different platform. And the others don't have a well-defined result — a real-world compiler will produce a result, and you can measure that, but it might be different tomorrow. Unless you know the difference between platform-specific behavior and undefined behavior, you don't know which ones to avoid.
"Measuring" is exactly the wrong thing to do because often it is indicative of only your specific architecture/compiler.
One immediate redflag I have noticed is using "int", "char", "short" as if they have a definite size. They don't. C standard only guarantees a minimum size. For example, many PDPs are 36-bit. Assuming the size of a variable is a common practice nowadays, but at least one should use uint8_t, int32_t, etc. from stdint.h.
But I was still tricked, it should be obvious in hindsight, 12 years of schooling led me to think: If the author was asking these questions, at least one or two questions must be answerable (even if it's technically incorrect, but you'd better to guess the original intention of a question). So I still tried to guess and got two wrong answers... Get to be careful next time...
This test.. it reverses that entirely.
I knew "int" was sort of platform-dependent (was 16-bits generally when I was learning to code, later 32-bit became more typical) — so combined with that niggle and all the "I don't knows", I (correctly) reevaluated by first couple of answers.
Still, didn't realize the last one was compiler-dependent.
Ada, maybe? I don't know enough about it to comment. You definitely don't want to use Haskell for that sort of work load, though, at least not directly. Laziness-by-default is precisely the sort of hard-to-reason-about logic you don't want in that sort of application.
That said, if I I had no alternative but to try and tackle this problem, I would seriously consider a strategy where I would write a Haskell program that would generate the actual program (potentially in ASM directly) for me.
In these situations, you likely know your hardware and know your compiler, so you can actually provide an answer for 4 of the questions. The last one is a situation where someone should tell you not to get cute in the code review.
I wrote C in telecom and finance and in both places we enforced a rule: when you define a structure, put a comment after each element that says what you think the structure offset should be, and at the end of the structure #define a constant that says what you think the size of the structure should be. In a code review, if anyone noticed something that didn't look right, you could talk about it. In testing, you could also check that sizeof(foo_s) == FOO_S_SIZE and fail if it wasn't.
In some of our code, we would test the size of various types and structures on startup and immediately exit if they weren't what we expected. We'd print type sizes to logs to help debugging if there was ever a problem. We were supporting a single code base that ran on big endian, little endian, X86, Itanium, SPARC, ARM. Compilers change, but automated tests of type and structure sizes catch things immediately.
It may sound like a lot of work, but it actually isn't at all. It also helps a lot with long-term maintainability.
This is one of the things that C++ has actually improved a lot recently: doing this with static_assert is much nicer in terms of catching problems early... And yes, it's great for long-term maintainability.
[1]: https://stackoverflow.com/a/7287341
Answered the rest, IDK.
Laughed! Great exercize.
I've worked on 16 bit C code, 32 and now 64 bit code. So I knew that the behavior was implementation and optimization dependent. :)
Ignorance is bliss in C.
In the C89 days, you'd use 'short' in aggregates (structs and array) for values you knew wouldn't exceed 16 bits so didn't want to potentially waste space; 'long' in situations where you knew 16 bits wouldn't be enough; and 'int' the rest of the time (where 16 bits was enough, and there weren't any storage benefits to outweigh the performance benefit of using the native word size).
Anyhow, for a computer with 16 bit wide data bus, having 16 bit ints might be justified by performance (and/or reducing memory usage.)
Say for example, in question 5, the statement "return i++ + ++i;" is undefined, because the value of i is read and modified twice in a single sequence point (and of course, the order of addition is unspecified), which is not allowed in C. So the answer is "Undefined." (The explanation given in the page not accurate enough in terms of C)
And for question 1, the code is valid, but the result is not strictly defined. It depends on the implementation. So the answer is "Implementation defined."
The usage of "main()" hurts me, the strictly conforming way is to write it as "int main(void)" (or similar)
I feel like the questionnaire piss off people who really knows C.
The code is undefined, but "I don't know." is still the correct answer for what happens to the variable.
Well, then a better choice would be "I can't know."
Ans: A compiler.
2) Probably that's not needed? A normal optimizing compiler just inlines the function somewhere new — and boom? Then again, can that really happen with practical contemporary compilers and this exact statement?
I don't know what the chances are that such a thing could ever translate into good assembly being emitted in one run and bad assembly being emitted in the next.
This pretty much is the definition of "undefined behaviour" in the context of a standardized language specification.
That's only when using Boehm GC[0] in kernel device drivers that self-modify. Or any MSVC binary.
:-D
0 - https://www.hboehm.info/gc/
The original problem definition, as specified by @pksadiq, read thusly:
> Say for example, in question 5, the statement "return i++ + ++i;" is undefined ...
This inspired a response by @Filligree of:
> Compile it, look at the assembly. You can know. The answer will vary from place to place, but it isn't non-existent.
Given the original constraint of an undefined statement result, and the suggested activity to address same, I posited that the recommended action is an exemplar of observing the product of undefined behaviour.
You then contributed:
> You mean “unspecified behavior”.
As per c-faq.com[0], there are three categories identified relating to this topic:
1 - implementation-defined: The implementation must pick some behavior; it may not fail to compile the program.
2 - unspecified: Like implementation-defined, except that the choice need not be documented.
3 - undefined: Anything at all can happen; the Standard imposes no requirements.
Whereas you imply a standards-conformant implementation of "return i++ + ++i;" is unspecified (category #2), it is, in fact, undefined (category #3). The support for this assertion is as follows.
As per the same site, Question 3.8[1] includes:
> Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored.
And further states:
> ... if an object is written to within a full expression, any and all accesses to it within the same expression must be directly involved in the computation of the value to be written. This rule effectively constrains legal expressions to those in which the accesses demonstrably precede the modification.
And concludes with an example stating:
> ... the Standard declares that it is undefined, and that portable programs simply must not use such constructs.
Therefore, the original expression presented by @pksadiq is in fact an exemplar of an undefined expression as defined by category #3 shown above. Since both it and the message to which I originally responded satisfy same, I stand by my response given to @Filligree as having had informally defined the standard C concept of "undefined behaviour."
0 - http://c-faq.com/ansi/undef.html
1 - http://c-faq.com/expr/seqpoints.html
You're misreading things. amluto's assertion is that "The answer will vary from place to place, but it isn't non-existent." is a description of category #2. That assertion is basically correct, depending on how exactly you define "place to place".
An informal definition of category #3 is "The answer can vary from place to place, or not exist at all." ideally followed by "It might crash or run unrelated code or even prevent the preceding code from running." It's flat-out wrong to say a value "isn't non-existent" when it comes to source code exhibiting undefined behavior.
"I dont know" was absolutely the correct answer.
In this case, a non-C programmer should answer "I don't know" to all of them. A person with a passing familiarity should answer similarly. A seasoned pro would be forced to answer the same. Making it a rather useless tool for distinguishing people who think they know C but are honest when faced with their limitations or those who truly know it and know the answer is undetermined, which is supposed to be the point of the exercise.
I don't think that assumption is justified. Someone could say the point of the exercise is to illustrate that C is confusing.
Never once does the author mention that C is confusing, use the word confusing, or otherwise indicate that general idea. If you're getting that impression, it's your own reading into it. I'm not even saying you'd be incorrect, but that's not the author's intent, which was the basis of my comment.
Only through extreme levels of compiler code inspection, as it can vary based on optimization heuristics.
> Quite the contrary, they indicate their intent is to demonstrate that certain segments of people who believe they know C don't in fact understand its intricacies.
Demonstrating that people don't know C is subtly different from an intent of testing whether people know C. The point being made is about C itself.
>"So you think you know C?"
That goes along with the interpretation that the point is to illustrate C is confusing. It would go along with something like "You think you know it, you think it's simple, well actually you don't know it, it's confusing."
>intended identify to test takers whether or not they really understand the intricacies of C, and to think critically about that source of their knowledge
Yes, its intent is to indicate to test takers that a lot of them don't really understand the intricacies of C, which demonstrates that C is more confusing than they originally thought.
>Never once does the author mention that C is confusing, use the word confusing, or otherwise indicate that general idea.
Here are some quotes that indicate the idea that C is confusing:
>C is not that simple.
>It’s only reasonable that the type of short int and an expression with the largest integer being short int would be the same. But the reasonable doesn’t mean right for C.
>Actually, it’s much more complicated than that. Take a peek at the standard, you’ll enjoy it.
>The third one is all about dark corners.
>The test is clearly provocative and may even be a little offensive.
Then the author says that he did C for 15 years and thought he knew it, but then realized he didn't. That indicates to me either that the author is saying that he's not smart, or that C is confusing. The second appears to be the point the author is actually making.
My interpretation is also based on what the author states, fairly explicitly. And I don't think there's anything that explicitly contradicts my interpretation.
There is a world of difference between "don't know" and "can't know", as the first implies a shortcoming on the side of the developer while second one states that the question is patently meaningless to someone who does master the language.
Which is both not true (because the compiler manual usually won't define undefined behaviour) and irrelevant (because the questions were about C, not about a compiler).
You're being pedantic about something silly, but you're also wrong in your pedantry.
Actually, as undefined behavior should not be used at all then the correct answer should be "nevermimd these examples, they are all bug-ridden".
This isn’t a recent standardization; it’s been an explicitly specified feature of C pretty much since the very beginning of the language. See page 7 of the prehistoric https://www.bell-labs.com/usr/dmr/www/cman.pdf
In your comment you are jumping from "I don't know", which is the first step, to wanting to explain why.
This is an article about the C language and the starting point with all the examples is that you do not know for a fact what the result will be.
Using "I don't know" as a substitute for "I know that the standard clearly covers this, and it says that the result depends on the implementation" does seem to be designed to piss off people who know C. If they really wanted to get the point across that you don't know what is and isn't implementation defined or undefined, they shouldn't be using vague questions to mislead people; they should just plainly ask questions which people don't know the answer to.
I hate this kind of questioning where you 100% know the subject matter the quiz is asking about, but the question and possible choices is so vague you have to try to interpret what you suspect the person who wrote the quiz wants the answer to be. I once had an exam which was full of that kind of multiple choice question, and guessed the exam author's intentions wrong on most of them.
My point isn't that there's no logically consistent answer. My point is that there are _two_ logically consistent answers, and which one is correct depends on the unknowable state of mind of the quiz author. On the other hand, if the options included "It's implementation defined" or "it's undefined", the author would have made their expectations clear, and the quiz would actually test people's knowledge of C rather than people's ability to try to reason about what sort of answer the author expects.
I hated this test. I’ve spent 12 years working on C targeting various flavors of arm and x86.
Just because the behavior is undefined when compiled without warnings and run on a Soviet water integrator doesn’t mean the language is undefined for the 99.995% of the industry uses.
Behavior of c89 or later with -Wall -Werror on modern clang, gcc, icc, visual studio, is well understood on arm, x86, mips, risc, ppc, Cortex-m and just about every other hardware architecture.
But, C is a pia, and I’ve been using rust instead :)
Which begs the question, why does C have those features in the first place? The only C code where it's reasonably common seems to be in crypto algorithms.
Anyway, I am inclined to agree that these are misfeatures. I’d almost certainly ask for it to be changed in code review.
Didn't quite know the purpose of the test, there's a difference between code for any machine and any compiler and gcc running on some vanilla x86, which is pretty common, and could have been the content (ex: you say it's undefined, that's obvious, everyone knows that already, but it's still deterministic... Here's a breakdown of what happens in practice, blah blah blah). There's a real difference between the kind of "knowing" here and say the kind of "knowing" with unallocated pointers.
If it said "esoteric implementation on exotic hardware" then it's easy, you know what they are trying to do. If it said C89 you also know what's up. But how it's presented, it's a guess.
This was endemic throughout schooling. Instructors would say "just do your best" and I'd be like "wtf? There's like 2,3, maybe 4 perspectives on this with different answers depending on how clever you're trying to be or what you're trying to get at... Might as well put "I'm thinking of a number 1 through 5" on the exam".
(You also seem to be assuming GCC is more predictable about undefined behaviour than it actually is.)
That's an assumption though. If it's undefined, the result may even depend on the order of some internal hash table which is affected by other code.
Undefined behavior means the standard says demons can fly out of your nose. Those demons might behave nondeterministically.