John Regehr, Professor of Computer Science, University of Utah, USA

A Quiz About Integers in C

The C language's rules for integer operations have some quirks that can make even small programs behave in confusing ways. This post is a review of these rules in the form of a quiz containing 20 questions. I recommend going through the questions in order. If you are a beginning C programmer, you should consult a C book as you go through these questions since there are a lot of little things (such as what "1U" means) that I have not bothered to explain. If you are a serious C programmer, I expect you'll do well -- this quiz is not intended to be extremely difficult. You should assume C99. Also assume that x86 or x86-64 is the target. In other words, please answer each question in the context of a C compiler whose implementation-defined characteristics include two's complement signed integers, 8-bit chars, 16-bit shorts, and 32-bit ints. The long type is 32 bits on x86, but 64 bits on x86-64 (this is LP64, for those who care about such things). Summary: Assume implementation-defined behaviors that Clang / GCC / Intel CC would make when targeting LP64. Make no assumptions about undefined behaviors.

Start

You scored %%SCORE%% out of %%TOTAL%%. Note: Sometimes scores are reported incorrectly -- sorry about that. I think it's a bug in the quiz plugin for WP that I'm using.
I hope you found this quiz to be useful and/or entertaining, and please write a comment or mail me if you find a mistake. As I said in the introduction, it was intended to be easy for experienced C programmers. In reality, integer bugs are hard to avoid not so much because the individual issues are extremely complicated, but rather because integer operations are everywhere and their corner case bugs get mixed in with algorithmic difficulties and other programming problems. I have another integer quiz in the works that will be more difficult.

Your answers are highlighted below.

Question 1

What does the expression 1 > 0 evaluate to?

A

0

B

1

C

undefined

Question 1 Explanation:

Ok, great. That one was a freebie. Of course one is greater than zero.

Question 2

What does the expression 1U > -1 evaluate to?

A

0

B

1

C

undefined

Question 2 Explanation:

C has both signed and unsigned types, creating the potential for confusion when these are mixed. The simple version of the rule for resolving this situation (we'll get to the real version shortly) is that unsigned wins. In other words, if a signed value is compared against an unsigned, the signed value is cast to unsigned and then the comparison is performed between two unsigned values. Therefore, this comparison is actually between 1 and UINT_MAX, which evaluates to 0. Good C programmers often avoid mixing signed and unsigned values in the same expression. Good C compilers warn about this, but GCC only does so at fairly high warning levels.

Question 3

What does the expression (unsigned short)1 > -1 evaluate to?

A

0

B

1

C

undefined

Question 3 Explanation:

In this question, two signed values are being compared. In other words, the rule about converting one operand of a comparison to unsigned if one of them is unsigned does not apply. This is because C "promotes" both operands to arithmetic operators to int type before performing the operation. The rule for promotion states that an unsigned value is promoted to a signed int if (as is the case for promoting an unsigned short to an int) this can be done without losing values. On the other hand (as we saw in the previous question) an unsigned int is not promoted to signed int because this would change large values into negatives. The full rules for promotions are a bit more complicated than this, for example to handle the case of types like long that may be wider than int.

Question 4

What does the expression -1L > 1U evaluate to on x86-64? On x86?

A

0 on both platforms

B

1 on both platforms

C

0 on x86-64, 1 on x86

D

1 on x86-64, 0 on x86

Question 4 Explanation:

This one is a little tricky because it asks you to analyze a feature interaction. On x86-64, int is shorter than long. Therefore, an unsigned int can be promoted to long, making the comparison signed. The comparison thus becomes -1L > 1L, which is false. On x86, int and long are the same size. Therefore, int cannot be promoted to long without changing values. On this platform, the comparison becomes UINT_MAX > 1U, which is true.

Question 5

What does the expression SCHAR_MAX == CHAR_MAX evaluate to?

A

0

B

1

C

undefined

Question 5 Explanation:

Sorry about that -- I didn't give you enough information to answer this one. The signedness of the char type is implementation-defined, meaning the each C implementation is permitted to make its own choice, provided that the choice is documented and consistent. ABIs for x86 and x86-64 tend to specify that char is signed, which is why I've said that "1" is the correct answer here.

Question 6

What does the expression UINT_MAX + 1 evaluate to?

A

0

B

1

C

INT_MAX

D

UINT_MAX

E

undefined

Question 6 Explanation:

The C standard guarantees that UINT_MAX+1 is 0.

Question 7

What does the expression INT_MAX + 1 evaluate to?

A

0

B

1

C

INT_MAX

D

UINT_MAX

E

INT_MIN

F

undefined

Question 7 Explanation:

Overflowing a signed integer is an undefined behavior.

Question 8

What does the expression -INT_MIN evaluate to?

A

0

B

1

C

INT_MAX

D

UINT_MAX

E

INT_MIN

F

undefined

Question 8 Explanation:

When using two's complement integers, INT_MIN has no representable inverse. Moreover, trying to compute it is an undefined behavior in C.

Question 9

Assume x has type int. Is the expression x<<0...

A

defined for all values of x

B

defined for some values of x

C

defined for no values of x

Question 9 Explanation:

Whoops... I thought this was always defined but as a couple of commenters point out, a negative value cannot be left-shifted even by zero bit positions.

Question 10

Assume x has type int. Is the expression x<<1...

A

defined for all values of x

B

defined for some values of x

C

defined for no values of x

Question 10 Explanation:

Shifting a 1 into the sign bit is an error in C99. Therefore, shifting a large value such as INT_MAX left by one bit position is an undefined behavior. Other values can be safely left-shifted.

Question 11

Assume x has type int. Is the expression x<<31...

A

defined for all values of x

B

defined for some values of x

C

defined for no values of x

Question 11 Explanation:

This is basically the same situation as the previous question. A 1 cannot be left-shifted into, or past, the signed bit. Therefore, I believe that 0 is the only value of type int that can be left-shifted by 31 bit positions without executing an undefined behavior.

Question 12

Assume x has type int. Is the expression x<<32...

A

defined for all values of x

B

defined for some values of x

C

defined for no values of x

Question 12 Explanation:

Shifting (in either direction) by an amount equalling or exceeding the bitwidth of the promoted operand is an error in C99.

Question 13

Assume x has type short. Is the expression x<<29...

A

defined for all values of x

B

defined for some values of x

C

defined for no values of x

Question 13 Explanation:

Operands to shift operators are promoted before the shift executes. Therefore, the fact that 29 is not less than 16 is irrelevant and a shift-past-bitwidth error does not occur.

Question 14

Assume x has type unsigned. Is the expression x<<31...

A

defined for all values of x

B

defined for some values of x

C

defined for no values of x

Question 14 Explanation:

Any value whose promoted type is "unsigned" can be legally shifted by an amount that is non-negative and also less than the width of the unsigned type.

Question 15

Assume x has type unsigned short. Is the expression x<<31...

A

defined for all values of x

B

defined for some values of x

C

defined for no values of x

Question 15 Explanation:

This one was just to make sure you've been paying attention. Since unsigned short is promoted to int, it is illegal to shift a 1 bit into or past the sign bit. If we shifted the value by 15 or fewer positions, the result would be defined for all values that can be stored in an unsigned short.

Question 16

Assume x has type int. Is the expression x + 1...

A

defined for all values of x

B

defined for some values of x

C

defined for no values of x

Question 16 Explanation:

Evaluating this expression is an undefined behavior iff x is INT_MAX.

Question 17

Assume x has type int. Is the expression x - 1 + 1...

A

defined for all values of x

B

defined for some values of x

C

defined for no values of x

Question 17 Explanation:

Since C's additive operators are left-associative, this expression is undefined iff x is INT_MIN. If these operators were right-associative, the expression would be defined for all values of x.

Question 18

Assume x has type int. Is the expression (short)x + 1...

A

defined for all values of x

B

defined for some values of x

C

defined for no values of x

Question 18 Explanation:

Any value of type int, after being truncated to short and then promoted back to int, can be safely incremented as long as the int type is wider than the short type.

Question 19

Assume x has type int. Is the expression (short)(x + 1)...

A

defined for all values of x

B

defined for some values of x

C

defined for no values of x

Question 19 Explanation:

Ok, that was an easy one. Of course the cast to short occurs too late to prevent the undefined behavior.

Question 20

Does evaluating the expression INT_MIN % -1 invoke undefined behavior?

A

who knows

Question 20 Explanation:

A bug in the quiz software prevented me from creating two correct answers for this question. But here's the explanation: This construct is widely treated as undefined by real C compilers because making it well-defined would reduce performance of generated code. My reading of the C99 standard is that it is not undefined. YMMV.

Once you are finished, click the button below. Any items you have not completed will be marked incorrect.
Get Results

There are 20 questions to complete.

You have completed

questions

question

Your score is

Correct

Wrong

Partial-Credit

You have not finished your quiz. If you leave this page, your progress will be lost.

Correct Answer

You Selected

Not Attempted

Final Score on Quiz

Attempted Questions Correct

Attempted Questions Wrong

Questions Not Attempted

Total Questions on Quiz

Question Details

Results

Date

Score

Hint

Time allowed

minutes

seconds

Time used

Answer Choice(s) Selected

Question Text

All done

probably spend too much time writing Java

Keep trying!

Not bad!

Good work!

Perfect!

83 thoughts on “A Quiz About Integers in C”

Question 5 contains two identifiers whose values are implementation defined (i.e., SCHAR_MAX and CHAR_MAX) and an equality comparison. The result is either 0 or 1, i.e., the result is unspecified.

Question 17: the status of x – 1 + 1 depends on the order of evaluation and can be undefined is 1 is subtracted from x rather than 1 being added to 1. It is common practice to consider the undefined to dominate and call the result undefined.

Question 18: if the quiz is about what the C Standard says then (short)x+1 is only defined for some values of x if it has type int. Re: comment #33 if we need to take the authors experience into account the answer can be almost anything and spoils the point of the quiz.

Question 20: Something more sensible would be:
What is the value of: sizeof (unsigned short)-1

with possible answers:

a: 1
b: 2
c: 4

Question 18 is wrong: if x > SHORT_MAX or x < SHORT_MIN, then (short) x is undefined and so is (short) x + 1. So (short) x + 1 is defined for some values of x, not all, when x is int.

Question 5 is implementation-defined, and saying it's x86 of x86-64 is not sufficient. I got it wrong since I answered "undefined".

Re #18, conversion of an out-of-range value from int to short is *not* undefined behavior. It yields an implementation-defined result or raises an implementation-defined signal. (The
latter permission was added in C99; I know of no compiler that takes advantage of it.) But the front page mentions several implementation-specific assumptions; the result of this conversion is not among them.

Question 4 not accurate. For example in Win64 sizeof(long) == sizeof(unsigned).

In Q.17, won’t the compiler after the abstract syntax tree creation, during optimization, evaluate +1-1=0 and replace it with 0 before evaluating the entire expression?

I want to call foul on a couple of these!

In #5, the explanation basically says that “undefined” is the right answer but you’re assuming a standard x86 PC.

In #11-15, we don’t know the size of any of these types. For #12, for example, sizeof(int) could well be 64. UNICOS comes to mind.

(If we’re to assume these questions only apply to a particular platform, then it should say that; if we’re to assume only the C language, then these answers aren’t right.)

All of this leads me to believe that the few simple rules I have, when I need to write in C, have treated me very well over the years: (1) never assume booleans are anything other than zero and nonzero, (2) never use unsigned types, and (3) if you need integers greater than 10,000, get a bignum library. ðŸ™‚

Hi Jesse (and others), I’ll just repeat the that platform information is specified at the top of the quiz…

“Iâ€™ll just repeat the that platform information is specified at the top of the quizâ€¦”

I’ll repeat: the information is NOT at the top of the quiz. The information is on the page when you first land, but DISAPPEARS when you open the quiz.

Obviously you approved the previous comment, but maybe you didn’t actually read it since:

(a) you didn’t fix it

and (b) you’re still using false language (AFAICT it’s impossible to have it on the page at the same time as the quiz, so it is NOT “at the top of the quiz”).

Hi anonymous, you’re right, it’s at the top of the post containing the quiz.

As my colleague Matt Might likes to say: There’s always someone in the world who will take exception to anything you do. The Internet helps you find that person.

I’m pointing this out because the comments here are fillled with people who don’t seem to have noticed it, and I’m pointing out a particular, FIXABLE reason they might be doing so.

Your job as a writer is to communicate with your readers. If you fail to communicate to a large number of your readers, you need to stop blaming them and consider that maybe your UX is actually flawed.

I’m a bit surprised about Question 18. Question 5 mentions that overflowing a signed integer is undefined behaviour, and others have pointed out that conversion to a signed integer type that cannot hold the value gives implementation-defined behaviour. We’re given some assumptions about architecture but no assumptions about any particularly implementation (of which there are many for the given architecture).

Hi Dave G, every modern C implementation that I am aware of chooses to define the integer down-casting behavior to be truncation, so I figured this could safely be considered to be a side effect of “assume LP64”. But yes, it would have been better to state this explicitly.

You should not equate this implementation defined behavior with an undefined behavior such as signed overflow. That is a totally different beast.

I’m slightly amused at the people who are angry that THEY didn’t read the instructions ðŸ™‚

I got 78% but 20/20 yay! The quiz gives the correct result if you don’t pick the right answer after having picked the wrong one.

I understood immediately the questions I got wrong. This makes me want even more that C/C++ compilers catch undefined behaviour whenever they can when compiled with debug settings.

Congratulations on killing platforms where sizeof(int) < 4.
The signed char question was likewise mean.

Everyone using ARM (esp. Thumb) now must hate you. ðŸ˜‰

This is very interesting. I’ve learned a lot about C integers. This will be very helpful in programming either in C or in any other language.

I didn’t get all the answers right, because my last C program is some 20 years ago and probably C99 is the 1999 implementation of ANSI C, which is far away from my conception of C. My knowledge is basically derived from K&R C with some book knowledge of ANSI C.

An excellent quiz. Made me realize why I tended toward the use of unsigned 64-bit integers and avoided the corner cases as needed. Math and C are a dangerous combination.

About question 9 (x << 0) I bet common compilers would simplify it into a no-op even with the lowest optimisation level… But if the standard says it is undefined behviour, people say something really odd is going to happen likely. (If the "likely" means on 1% of compilers on 1% of hardware, no matter: it is still undef behaviour, so compilers should make it behaves badly, just to be aligned – ok undef behaviour states a "contract" the programmer can trust, and in this case he can't… 1% of the time, at least). This also suggests that we are dealing with logical shift rather than arithmetic shift (which should be always well defined). So, always do your bit games with unsigned…

I graduated from the University of Utah with a computer science degree back in the70’s and I do not recall any detail instructions on integers. But then, the C programming language did not exist or was too new.

A lot of times, when confronted with such situations, I would look at the assembly language the compiler generated to see if it was doing what I wanted.

Hi RD, I do that too.

But you have to be really careful with undefined behavior since the next time you upgrade your compiler, change optimization options, or even recompile after changing some unrelated code, the assembly language corresponding to the undefined code may do something totally different. For an example, see the comments starting at #3 here:

I’ve been programming and using C for 40 years.
None of these questions came up in my professional experience.
Never had a bug in my programs related to these issues.
If one of these questions comes up and disqualifies me in a job interview, I do not want that job ðŸ™‚

Hi paca, actually this quiz is only for programmers who make mistakes. You go!

Q18: (short)x + 1 is implementation-defined (and may raise a signal), because (short)x is implementation-defined and may raise a signal if x is outside the range of short — see 6.3.1.3.3

Q20: Since INT_MIN/-1 (which would be one greater than INT_MAX assuming twos-complement) is not representable as an int, INT_MIN % -1 is undefined (6.5.5.6)

Hi Matthew, I’ve read the “multiplicative operators” part of the C99 standard very carefully and do not believe that your interpretation is the most reasonable one. They fixed the language in C11.

Nice quiz and learning experience. Thanks!

INTERESTING!!!! LEARN FEW THINGS

As stated scoring doesn’t take into account which attemp the correct answer was arrived at.

Great quiz though, learnt a few things.

For #4, actual program output in my quick test was 1 for both x86 and x64 — so I guess either the answer is wrong or gcc is buggy.

Hi Al, the answer to #4 is correct, as are a few versions of GCC that I tried. If you provide more detail I can try to repro your result.

Question 5 contains two identifiers whose values are implementation defined (i.e., SCHAR_MAX and CHAR_MAX) and an equality comparison. The result is either 0 or 1, i.e., the result is unspecified.

Question 17: the status of x – 1 + 1 depends on the order of evaluation and can be undefined is 1 is subtracted from x rather than 1 being added to 1. It is common practice to consider the undefined to dominate and call the result undefined.

Question 18: if the quiz is about what the C Standard says then (short)x+1 is only defined for some values of x if it has type int. Re: comment #33 if we need to take the authors experience into account the answer can be almost anything and spoils the point of the quiz.

Question 20: Something more sensible would be:

What is the value of: sizeof (unsigned short)-1

with possible answers:

a: 1

b: 2

c: 4

Question 18 is wrong: if x > SHORT_MAX or x < SHORT_MIN, then (short) x is undefined and so is (short) x + 1. So (short) x + 1 is defined for some values of x, not all, when x is int.

Question 5 is implementation-defined, and saying it's x86 of x86-64 is not sufficient. I got it wrong since I answered "undefined".

Re #18, conversion of an out-of-range value from int to short is *not* undefined behavior. It yields an implementation-defined result or raises an implementation-defined signal. (The

latter permission was added in C99; I know of no compiler that takes advantage of it.) But the front page mentions several implementation-specific assumptions; the result of this conversion is not among them.

Question 4 not accurate. For example in Win64 sizeof(long) == sizeof(unsigned).

In Q.17, won’t the compiler after the abstract syntax tree creation, during optimization, evaluate +1-1=0 and replace it with 0 before evaluating the entire expression?

I want to call foul on a couple of these!

In #5, the explanation basically says that “undefined” is the right answer but you’re assuming a standard x86 PC.

In #11-15, we don’t know the size of any of these types. For #12, for example, sizeof(int) could well be 64. UNICOS comes to mind.

(If we’re to assume these questions only apply to a particular platform, then it should say that; if we’re to assume only the C language, then these answers aren’t right.)

All of this leads me to believe that the few simple rules I have, when I need to write in C, have treated me very well over the years: (1) never assume booleans are anything other than zero and nonzero, (2) never use unsigned types, and (3) if you need integers greater than 10,000, get a bignum library. ðŸ™‚

Hi Jesse (and others), I’ll just repeat the that platform information is specified at the top of the quiz…

“Iâ€™ll just repeat the that platform information is specified at the top of the quizâ€¦”

I’ll repeat: the information is NOT at the top of the quiz. The information is on the page when you first land, but DISAPPEARS when you open the quiz.

Obviously you approved the previous comment, but maybe you didn’t actually read it since:

(a) you didn’t fix it

and (b) you’re still using false language (AFAICT it’s impossible to have it on the page at the same time as the quiz, so it is NOT “at the top of the quiz”).

Hi anonymous, you’re right, it’s at the top of the post containing the quiz.

As my colleague Matt Might likes to say: There’s always someone in the world who will take exception to anything you do. The Internet helps you find that person.

I’m not pointing this out idly.

I’m pointing this out because

the comments here are fillled with people who don’t seem to have noticed it, and I’m pointing out a particular, FIXABLE reason they might be doing so.Your job as a writer is to communicate with your readers. If you fail to communicate to a large number of your readers, you need to stop blaming them and consider that maybe your UX is actually flawed.

I’m a bit surprised about Question 18. Question 5 mentions that overflowing a signed integer is undefined behaviour, and others have pointed out that conversion to a signed integer type that cannot hold the value gives implementation-defined behaviour. We’re given some assumptions about architecture but no assumptions about any particularly implementation (of which there are many for the given architecture).

Hi Dave G, every modern C implementation that I am aware of chooses to define the integer down-casting behavior to be truncation, so I figured this could safely be considered to be a side effect of “assume LP64”. But yes, it would have been better to state this explicitly.

You should not equate this implementation defined behavior with an undefined behavior such as signed overflow. That is a totally different beast.

I’m slightly amused at the people who are angry that THEY didn’t read the instructions ðŸ™‚

I got 78% but 20/20 yay! The quiz gives the correct result if you don’t pick the right answer after having picked the wrong one.

I understood immediately the questions I got wrong. This makes me want even more that C/C++ compilers catch undefined behaviour whenever they can when compiled with debug settings.

Congratulations on killing platforms where sizeof(int) < 4.

The signed char question was likewise mean.

Everyone using ARM (esp. Thumb) now must hate you. ðŸ˜‰

This is very interesting. I’ve learned a lot about C integers. This will be very helpful in programming either in C or in any other language.

Very instructive.

I didn’t get all the answers right, because my last C program is some 20 years ago and probably C99 is the 1999 implementation of ANSI C, which is far away from my conception of C. My knowledge is basically derived from K&R C with some book knowledge of ANSI C.

An excellent quiz. Made me realize why I tended toward the use of unsigned 64-bit integers and avoided the corner cases as needed. Math and C are a dangerous combination.

About question 9 (x << 0) I bet common compilers would simplify it into a no-op even with the lowest optimisation level… But if the standard says it is undefined behviour, people say something really odd is going to happen likely. (If the "likely" means on 1% of compilers on 1% of hardware, no matter: it is still undef behaviour, so compilers should make it behaves badly, just to be aligned – ok undef behaviour states a "contract" the programmer can trust, and in this case he can't… 1% of the time, at least). This also suggests that we are dealing with logical shift rather than arithmetic shift (which should be always well defined). So, always do your bit games with unsigned…

I graduated from the University of Utah with a computer science degree back in the70’s and I do not recall any detail instructions on integers. But then, the C programming language did not exist or was too new.

A lot of times, when confronted with such situations, I would look at the assembly language the compiler generated to see if it was doing what I wanted.

Hi RD, I do that too.

But you have to be really careful with undefined behavior since the next time you upgrade your compiler, change optimization options, or even recompile after changing some unrelated code, the assembly language corresponding to the undefined code may do something totally different. For an example, see the comments starting at #3 here:

http://blog.regehr.org/archives/722

I’ve been programming and using C for 40 years.

None of these questions came up in my professional experience.

Never had a bug in my programs related to these issues.

If one of these questions comes up and disqualifies me in a job interview, I do not want that job ðŸ™‚

Hi paca, actually this quiz is only for programmers who make mistakes. You go!

Q18: (short)x + 1 is implementation-defined (and may raise a signal), because (short)x is implementation-defined and may raise a signal if x is outside the range of short — see 6.3.1.3.3

Q20: Since INT_MIN/-1 (which would be one greater than INT_MAX assuming twos-complement) is not representable as an int, INT_MIN % -1 is undefined (6.5.5.6)

Hi Matthew, I’ve read the “multiplicative operators” part of the C99 standard very carefully and do not believe that your interpretation is the most reasonable one. They fixed the language in C11.

Nice quiz and learning experience. Thanks!

INTERESTING!!!! LEARN FEW THINGS

As stated scoring doesn’t take into account which attemp the correct answer was arrived at.

Great quiz though, learnt a few things.

For #4, actual program output in my quick test was 1 for both x86 and x64 — so I guess either the answer is wrong or gcc is buggy.

Hi Al, the answer to #4 is correct, as are a few versions of GCC that I tried. If you provide more detail I can try to repro your result.