Data types, operators and expressions
What a C value really is (a fixed number of bits), how operands are converted before every operation, and the precedence and overflow rules that GATE expression questions are built on.
Why it exists
Mathematics has integers of any size. A C int has exactly 32 bits (on the machines GATE assumes), so there are only values. Unsigned arithmetic quietly wraps around at the ends, and signed arithmetic that leaves the range is undefined. On top of that, every operation in C happens at a particular type, and C silently converts operands to a common type before computing. Most "what does this program print" questions about expressions are really testing these two facts: the width of the values, and the conversions the compiler inserts.
If you can answer three questions about any expression, you can evaluate it: what is the type of each operand after conversion, what does the operator do at that type, and what happens when the result is stored into a variable of another type.
Prerequisites
None. This is a starting point.
You should be comfortable writing numbers in binary and in two's complement, and you should know what an expression and a statement are. Nothing else from C is assumed.
Precise definitions
Integer types. char, short, int, long, long long, each signed or unsigned (plain char is a separate type from both signed char and unsigned char). The standard fixes only minimum ranges and nests them: the range of each type is contained in the range of the next one. In practice that gives:
sizeof(char)is 1 by definition. Ashortand aninthave at least 16 bits, alongat least 32, along longat least 64.- .
- The usual GATE assumption is
char1 byte,short2,int4,float4,double8. A question that depends onlongor pointer size states it (it is 8 bytes on 64-bit Linux and 4 on 32-bit systems). - Plain
charmay be signed or unsigned: this is implementation-defined (signed on x86, unsigned on many ARM compilers). Writesigned charorunsigned charwhen it matters.
Value ranges. An -bit unsigned type holds to . An -bit signed type in two's complement holds to . For 8 bits that is 0 to 255 and -128 to 127. For 32 bits, INT_MAX is .
Integer promotion. An operand whose type ranks no higher than int (char, short, _Bool, bit-fields, enumerations, signed or unsigned) is converted to int if int can represent all its values, and otherwise to unsigned int. With a 32-bit int that means every char and short becomes int. Promotion is applied to the operands of arithmetic, bitwise and shift operators and of unary +, - and ~, before the operator runs. That is why c + 1 for a char c is an int.
Usual arithmetic conversions (C11 6.3.1.8). For a binary arithmetic operator, after promotion the two operands are brought to a common type. The decision uses the conversion rank of the integer types: char < short < int < long < long long, and a signed type and its unsigned partner have the same rank. In order:
- If either operand is
long double, the other becomeslong double. Otherwise if either isdouble, the other becomesdouble. Otherwise if either isfloat, the other becomesfloat. - Otherwise both are integers. If they already have the same type, nothing changes.
- If both are signed, or both are unsigned, the one with the lower rank is converted to the type of the higher rank.
- If the unsigned operand has rank greater than or equal to the signed operand's rank, the signed operand is converted to the unsigned type. This is the common GATE case:
intagainstunsigned intgivesunsigned int, andintagainstunsigned longgivesunsigned long. - Otherwise (the signed type has the higher rank), if the signed type can represent every value of the unsigned type, the unsigned operand is converted to the signed type (for example
longagainstunsigned inton a 64-bit machine). - Otherwise both operands are converted to the unsigned type that corresponds to the signed type.
Overflow and conversion.
- Unsigned arithmetic wraps: the result is taken modulo . This is fully defined.
- Signed arithmetic overflow (for example
INT_MAX + 1) is undefined behaviour. - Converting an out-of-range value to a signed type is implementation-defined (the standard also allows the implementation to raise a signal); gcc keeps the low bits, a two's complement wrap.
- Converting a negative value to an unsigned type is defined: the result is the value plus ("keep the bits").
Division and remainder. Integer division truncates toward zero. The remainder takes the sign of the dividend, and holds whenever and is representable. Division by zero is undefined, and so is INT_MIN / -1 (the quotient does not fit).
Shifts. x << n and x >> n are undefined if is negative or at least the width of the promoted left operand. For a signed x, x << n is defined only if x is non-negative and fits in the result type; otherwise it is undefined (so shifting a negative value left, or shifting into the sign bit, is undefined). For an unsigned x, the result is taken modulo . Right-shifting a negative signed value is implementation-defined (gcc shifts arithmetically, copying the sign bit); right-shifting an unsigned value always fills with zeros.
Logical operators &&, || and ! yield int 0 or 1, and treat any non-zero operand as true. && and || evaluate left to right and short-circuit: the right operand is evaluated only if needed. There is a sequence point after the left operand.
Operator precedence (high to low, all binary levels group left to right):
- postfix
()[].->++-- - prefix
++ -- + - ! ~ * & sizeofand casts (right to left) */%+-<<>><<=>>===!=&^|&&||?:then=+=... (right to left) then,
How it works
Step through the animation. Each step is one real evaluation: the numbers, the C type the compiler gave each sub-expression (read from the compiler with _Generic), and the actual bit pattern at that width. The sign bit is ringed. Bits that differ from the row above are shown in amber.
Values, types and bit patterns: unsigned char wraps around
Real values from gccC code
unsigned char c = 255; c = c + 1;
Value, C type and bits (sign bit ringed, changed bits in amber)
Step 1
c is promoted to int before the addition, so c + 1 is computed in 32 bits and equals 256. Storing it back into an 8-bit unsigned char keeps only the low 8 bits, which are all zero. Unsigned arithmetic always wraps modulo 2^width.
Three habits make these questions routine:
- Write the type next to every operand.
c + 1with achar cisint + int.-1 < 1uisunsigned + unsignedafter conversion. - Compute at that width. For
unsigned intuse arithmetic modulo . Forint, a result outside to is undefined, so a well-posed question will not ask for it. - Convert on assignment. Storing into a narrower unsigned type keeps the value modulo . Storing into a narrower signed type is implementation-defined (gcc keeps the low bits). Storing a
doubleinto aninttruncates toward zero, and is undefined if the truncated value does not fit.
Worked examples
EX 1Ranges and wrap-around
Easy#include <stdio.h>
int main(void) {
signed char s = (signed char)200;
unsigned char u = (unsigned char)-1;
printf("%d %d\n", s, u);
return 0;
}Output:
-56 255Step 1. 200 is 11001000 in 8 bits. As signed char the top bit is the sign bit, so the value is .
Step 2. -1 converted to unsigned char is (all eight bits set).
Check. The unsigned conversion is fully defined: -1 becomes . The signed one is implementation-defined, because a signed 8-bit type cannot hold 200 (its maximum is 127); gcc, like every mainstream compiler, wraps by 256, and exam questions normally assume that.
EX 2Promotion hides the wrap-around
Medium#include <stdio.h>
int main(void) {
unsigned char a = 200, b = 100;
printf("%d\n", a + b);
printf("%d\n", (unsigned char)(a + b));
return 0;
}Output:
300
44Step 1. a and b are promoted to int before +, so a + b is the int 300. Nothing wraps.
Step 2. The cast to unsigned char keeps only the low 8 bits: .
Lesson. A narrow type only wraps when a value is stored back into it, not while the expression is evaluated.
EX 3Mixing int and unsigned int
Medium#include <stdio.h>
int main(void) {
int x = -5;
unsigned int y = 3;
printf("%d\n", x < y);
printf("%u\n", x + y);
printf("%d\n", (int)(x + y));
return 0;
}Output:
0
4294967294
-2Step 1. Same size, mixed signedness: x is converted to unsigned int. .
Step 2. x < y becomes , which is false: 0.
Step 3. x + y is as an unsigned int. Printing with %u shows that number.
Step 4. Casting back to int gives on gcc (converting an out-of-range value to a signed type is implementation-defined, and every mainstream compiler keeps the same bits).
EX 4Precedence of shifts, additions and bitwise operators
GATE level#include <stdio.h>
int main(void) {
int x = 4;
printf("%d %d\n", x & 1 == 0, (x & 1) == 0);
printf("%d %d\n", 1 + 2 << 3, 1 << 2 + 3);
return 0;
}Output:
0 1
24 32Step 1. == is above &, so x & 1 == 0 is x & (1 == 0), that is 4 & 0 = 0. With parentheses, (4 & 1) is 0 and 0 == 0 is 1.
Step 2. + binds tighter than <<. So 1 + 2 << 3 is (1 + 2) << 3 = 3 << 3 = 24, and 1 << 2 + 3 is 1 << 5 = 32.
EX 5Integer versus floating division
GATE level#include <stdio.h>
int main(void) {
printf("%d %d %d\n", 7 / 2, -7 / 2, -7 % 3);
double r1 = 7 / 2;
double r2 = 7 / 2.0;
printf("%.1f %.1f\n", r1, r2);
return 0;
}Output:
3 -3 -1
3.0 3.5Step 1. Integer division truncates toward zero: and (not -4).
Step 2. The remainder takes the sign of the dividend: , since .
Step 3. In double r1 = 7 / 2; the division is done in int first (giving 3) and only then converted to double. The conversion to double does not rescue the fraction. 7 / 2.0 has a double operand, so 7 is converted first and the result is 3.5.
EX 6Short-circuit evaluation
GATE level#include <stdio.h>
int main(void) {
int i = 0, j = 5;
if (i++ && j++) {
}
printf("%d %d\n", i, j);
int k = 1, m = 5;
if (k++ || m++) {
}
printf("%d %d\n", k, m);
return 0;
}Output:
1 5
2 5Step 1. i++ yields the old value 0 (false) and sets i to 1. && already knows the result is false, so j++ is not evaluated: j stays 5.
Step 2. k++ yields 1 (true) and sets k to 2. || already knows the result is true, so m++ is not evaluated.
Rule. For && the right side runs only if the left is non-zero. For || it runs only if the left is zero.
Cheat box
Cheat box
- -bit unsigned: to . -bit signed: to .
- Anything narrower than
intbecomesintbefore the operator. It wraps only when stored back. intwithunsigned int: unsigned wins. So-1 < 1uis false and-1 > 0uis true.- Unsigned overflow wraps (defined). Signed overflow is undefined. Out-of-range conversion to signed is implementation-defined.
- Integer division truncates toward zero.
a % bhas the sign ofa. . double r = 7 / 2;is 3.0, not 3.5.- Precedence, high to low: unary,
* / %,+ -,<< >>,< <= > >=,== !=,&,^,|,&&,||,?:,=,,. x & (x - 1)clears the lowest set bit.(x & (x - 1)) == 0tests for a power of two or zero (for unsignedx).&&and||short-circuit and have a sequence point after the left operand.
Traps and GATE patterns
Other things examiners twist:
char c = 'A' + 2;stores the character code 67, which prints asCwith%cand as 67 with%d.- A negative number printed with
%ushows its unsigned reinterpretation; an unsigned value printed with%dcan appear negative. A mismatched format is itself undefined behaviour in the standard, but GATE usually just wants the reinterpretation. sizeofis an operator, not a function, and is evaluated at compile time (its operand is not executed, unless it is a variable-length array).
Connections
Builds on
No prerequisites.
Unlocks
Across subjects
Within this subject
The bit-level view of numbers is covered in signed number representation and IEEE 754. The bitwise operators here are the software version of the Boolean algebra of logic gates, and the "anything can happen" rules continue on the page about undefined behaviour.
PYQs
8 previous year questions from your book. I re-solved each one myself (8 by running or simulating, 0 by reasoning only); the book's key is shown next to my answer. All PYQs
What is printed by the following ANSI C program?
#include<stdio.h>
int main (int argc, char *argv[])
{
char a= 'P';
char b= ' x ';
char c= (a & b)+' *';
char d= (b) - ' -';
char e= (a∧b) + ' + ';
printf("%c %c %c \n", c, d, e);
return 0;
}
ASCII encoding for relevant characters is given
below.
A
B
C
.....
Z
65
66
67
.....
90
*
+
-
42
43
45- (a)z K S
- (b)122 75 83
- (c)* - +
- (d)P x +
Consider the following C program
#include<stdio.h>
int main ( ) {
int m= 10
int n, nl;
n=++m;
nl=m++;
n--;
--n1;
n-= nl;
printf("%d", n);
return 0;
}
The output of the program is ____.Suppose n and p are unsigned int variables in a C program. We wish to set p to nC3. If n is large, which one of the following statements is most likely to set p correctly?
- (a)p= n*(n-1)*(n-2)/6;
- (b)p= n*(n-1)/2*(n-2)/3;
- (c)p= n*(n-1)/3*(n-2)/2;
- (d)p= n*(n-1)*(n-2)/6.0;
Which combination of the integer variables x, y,and z makes the variable a get the value 4 in the following expression? a= (x> y)? ((x> z)? x: z): ((y> z)? y: z)
- (a)x = 3, y = 4, z = 2
- (b)x = 6, y = 5, z = 3
- (c)x = 6, y = 3, z = 5
- (d)x = 5, y = 4, z = 5 a b c ... ... z 97 98 99 ... ... 122
Consider the following C program:
#include <stdio.h>
int main () {
float sum = 0.0, j = 1.0, i = 2.0;
while (i/j > 0.0625) {
j = j + j;
sum = sum + i/j;
printf ("%f \n", sum);
}
return 0;
}
The number of times the variable sum will be printed,
when the above program is executed, is ______.Consider the following C program.
#include <stdio.h>
#include <string.h>
void printlength (char*s, char*t)
{
unsigned int c = 0;
int len = ((strlen(s) - strlen(t)) > c)?strlen(s):strlen(t);
printf("%d\n", len);
}
void main () {
char*x = "abc";
char*y = "defgh";
printlength(x,y);
}
Recall that strlen is defined in string.h as returning a
value of type size_t, which is an unsigned int. The
output of the program is _________.
int pp(int a, int b) {
int arr[20];
int i, tot = 1, ex, len;
ex= a;
len = tob(b, arr);
for (i=0; i < len; i++ ) {
if (arr[i] == 1
tot = tot * ex;
ex= ex * ex;
}
} return (tot);Consider the following C program.
void f(int, short);
void main ()
{
int i = 100;
short s = 12;
short *p = &s;
__________ ; // call to f()
}
Which one of the following expressions, when placed
in the blank above, will NOT result in a type checking
error?- (a)f(s, *s)
- (b)i = f(i, s)
- (c)f(i, *s)
- (d)f(i, *p)
Consider the function shown below :
int func(int num) {
int count = 0;
while (num) {
count ++;
num>> = 1;
}
return (count);
}
The value returned by func (435) is _____.Self-check
Question 1
What does the following print? printf("%d", (unsigned char)(255 + 1));
Show answerHide
255 + 1 is the int 256. The cast keeps the low 8 bits, which are all zero, so the output is 0.
#include <stdio.h>
int main(void) {
printf("%d\n", (unsigned char)(255 + 1));
return 0;
}0Question 2
With int a = -1; unsigned int b = 0;, what is the value of a > b?
Show answerHide
a is converted to unsigned: 4294967295, which is greater than 0. The value is 1, even though is false in mathematics.
#include <stdio.h>
int main(void) {
int a = -1;
unsigned int b = 0;
printf("%d\n", a > b);
return 0;
}1Question 3
What is the value of 5 & 6 == 6?
Show answerHide
== binds first: 6 == 6 is 1. Then 5 & 1 is 1 (binary 101 and 001 share the lowest bit). If you had wrongly evaluated 5 & 6 first (giving 4) you would have computed 4 == 6, which is 0.
#include <stdio.h>
int main(void) {
printf("%d\n", 5 & 6 == 6);
return 0;
}1Question 4
After char c = 'A' + 2;, what do printf("%d %c", c, c) print?
Show answerHide
The code of 'A' is 65 in ASCII, so c holds 67. The output is 67 C.
#include <stdio.h>
int main(void) {
char c = 'A' + 2;
printf("%d %c\n", c, c);
return 0;
}67 CRevision card
One-screen revision
- Every value has a fixed width. -bit unsigned: to ; signed: to .
- Narrow types are promoted to
intfirst. They wrap only on storage. - Mixed
intandunsigned int: convert to unsigned. Mixed withdouble: convert to double. - Unsigned overflow wraps. Signed overflow is undefined. Division truncates toward zero.
7 / 2is 3 even when assigned to adouble.- Precedence: bitwise operators are below
==. Shifts are below+. &&and||short-circuit.- Think in three steps: type of each operand, operation at that type, conversion on store.
References
- ·Kernighan and Ritchie, The C Programming Language, 2nd ed., Chapter 2 Types, Operators and Expressions
- ·ISO/IEC 9899:2011 (C11), Section 6.3 Conversions (6.3.1.1 integer promotions, 6.3.1.8 usual arithmetic conversions), Section 6.5 Expressions
- ·Bryant and O'Hallaron, Computer Systems: A Programmer's Perspective, 3rd ed., Chapter 2 Representing and Manipulating Information
Verification
All C programs on this page are compiled with -Wall -Wextra -Werror and run by npm run verify:c, and the shown outputs match. The values, types and bit patterns in the animation are printed by a real gcc program (types via _Generic, widths via sizeof). The prose, the precedence and conversion summaries and the GATE-pattern claims have NOT yet had a human textbook check (the independent second pass is described at the end of these notes). SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand (long C programs were not re-read line by line: they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose). No defect found. Status raised to self-checked; verified needs a textbook or human check.
last verified: never
My notes (saved in this browser)