The char data type is the smallest integer type in C. This note covers what it stores, how to use it, and how its range comes from having just 8 bits. It continues from Data Types And Variables, which explains what data types are and how their sizes vary across compilers.

Char Data Type

The char data type is used to store a single character, such as a letter ('A'), a digit ('5'), or a symbol ('@').

  • It occupies 1 byte (8 bits) of memory.
  • It stores characters using their numeric codes, commonly ASCII codes.
  • It is one of the smallest integer data types in C.
  • A char can be signed or unsigned, depending on the implementation and declaration.

Example:

char letter = 'A';

Here, the variable letter stores the character 'A', which has the ASCII code 65.

In simple words: char is a data type that uses 1 byte of memory to represent a single character or a small integer value.

Exercise

Write a program to sum and print distances from point A to point C:

  • Point A to B= 160
  • Point B to C= 40

Choose appropriate data types and print the results to the terminal. Use the below format specifier table to print the output:

Format SpecifierDescription
%dInteger Format Specifier
%fFloat Format Specifier
%cCharacter Format Specifier
%sString Format Specifier
%uUnsigned Integer Format Specifier
%ldLong Int Format Specifier

Solution:

#include <stdio.h>
 
int main() {
  unsigned char pointA_B = 160;
  unsigned char pointA_C = 40;
  unsigned char result = pointA_B + pointA_C;
  printf("Result: %u\n", result);
  return 0;
}

We use the %u format specifier to print the result as it is an unsigned char integer.

Range of the char Data Type

We have been quoting ranges like -128 to 127 for char and 0 to 255 for unsigned char without saying where they come from. Let’s actually derive them, because once you can derive the range of a 1-byte type, you can derive the range of any integer type just by changing one number: the number of bits.

Prerequisites

This section uses binary place values, hexadecimal, and two’s complement. If any of that is unfamiliar, read Binary Number System and One’s and Two’s Complement first. They build all of it from scratch. Then come back here.

1 Byte Signed Data Representation

A char occupies 1 byte = 8 bits, and it holds signed data. In signed representation, one dedicated bit is reserved to encode the sign:

+--------+-------------------------------------------+
|  Bit 7 |          Bits 6 ... 0 (7 bits)            |
|  (MSB) |                Magnitude                  |
+--------+-------------------------------------------+
  sign bit
   0 → data is positive
   1 → data is negative
  • If the sign bit is 0, the data is positive, and the remaining 7 bits store the magnitude as plain binary.
  • If the sign bit is 1, the data is negative, and the remaining 7 bits store the magnitude in 2’s complement form.

Since every bit doubles the number of patterns, a byte holds exactly distinct bit patterns. The whole question of the range comes down to this: which of the 256 patterns is the smallest, and which is the largest?

Storing -25 in One Byte

Let’s store step by step, exactly as a compiler would.

Step 1: split the number. Sign = negative, magnitude = .

Step 2: write the magnitude in binary. Using the place values :

Weights:  128  64  32  16  8  4  2  1
Bits:       0   0   0   1  1  0  0  1   →  +25 (only 5 bits needed,
                                              remaining 3 bits = 0)

Step 3: the data is negative, so the magnitude must go in 2’s complement form. First take the one’s complement (flip every bit), then add 1:

  +25:   0 0 0 1 1 0 0 1
  flip:  1 1 1 0 0 1 1 0        ← 1's complement
        +               1
        ----------------
  2's:   1 1 1 0 0 1 1 1        ← magnitude of 25 in 2's complement
         ↑
         sign bit = 1 → negative ✓

Step 4: convert to hex by splitting into nibbles (4-bit groups):

  1110 0111
    ↓    ↓
    E    7          →  0xE7

Reading it back

The MSB is 1, so the value is . ✓

Storing +25 in One Byte

For a positive number there is nothing clever to do: the sign bit is 0, and the magnitude is stored as plain binary. No 2’s complement is taken.

  sign bit
      ↓
      0   0 0 1 1 0 0 1     →  0001 1001  =  0x19
      ↑
      positive

Deriving the Signed Range

Now sweep through all possible sign-bit values and see which numbers come out.

Positive side, sign bit = 0. The smallest magnitude is all zeros, the largest is all ones:

  0 0000000  →   0     (smallest)
  0 0000001  →  +1
      ⋮
  0 1111111  → +127   (largest positive)

Negative side, sign bit = 1. The magnitude is read in 2’s complement:

  1 0000000  → -128   (largest negative, all magnitude bits zero)
      ⋮
  1 1111111  →   -1   (smallest negative, all magnitude bits one)

Putting both sides together:

Why is the range asymmetric (−128 but only +127)?

There are patterns. They must cover through (128 patterns) on the positive side, which leaves exactly 128 patterns for the negatives: through . Because zero is already counted on the positive side and 2’s complement has no separate , the negative side gets one extra slot. That’s where the comes from.

Deriving the Unsigned Range

An unsigned char stores unsigned data. No bit is reserved for a sign, so all 8 bits store the magnitude as plain binary:

  00000000  →    0   (smallest, all zeros)
      ⋮
  11111111  →  255   (largest, all ones)

Same 256 patterns as before, but now every one of them is used for a magnitude. That is why the unsigned range reaches exactly twice as high (255 vs 127).

The General Formula

Notice the pattern and you can write down the range of any integer type without a table. For a type that uses bits:

RepresentationRangeNumber of values
Signed to
Unsigned to

Plugging in :

And the same formulas with give the short int ranges, with the int ranges, and so on.

Plain char is not guaranteed to be signed

Whether a plain char is signed or unsigned is implementation-defined. Some compilers treat it as signed, others as unsigned. If the sign matters, always spell it out as signed char or unsigned char.

Previous: Data Types And Variables