When iterating through character codes in C and printing them with printf, you might encounter a bizarre issue where a large chunk of your output simply disappears. A classic example is looping from 0 to 300 and noticing that your terminal goes completely silent between values 157 and 280:

#include <stdio.h>

int main(void) {
    for (int c = 0; c < 300; c++) {
        printf(">%c< (%d)\n", c, c);
    }
    return 0;
}

Even though you explicitly ask printf to print the integer value (%d) and a newline on every iteration, nothing appears between 157 and 280. Did the program crash? Did printf fail? The answer lies not in your C compiler, but in how modern terminal emulators interpret raw byte streams.

The Culprit: C1 Control Codes

In the ASCII standard, values from 0 to 31 (and 127) represent C0 control codes (such as \n for newline, \t for tab, or \b for backspace). However, standards like ECMA-48 and ISO/IEC 8859 define an extended set called C1 control codes occupying byte values 128 to 159 (0x80 to 0x9F).

When your loop reaches c = 157 (hexadecimal 0x9D), it sends the 8-bit representation of OSC (Operating System Command) to the terminal:

  • 155 (0x9B): CSI (Control Sequence Introducer — equivalent to ESC [)
  • 157 (0x9D): OSC (Operating System Command — equivalent to ESC ])
  • 158 (0x9E): PM (Privacy Message)
  • 159 (0x9F): APC (Application Program Command)

Terminal emulators (like WezTerm, xterm, and others) use OSC sequences to perform terminal-level tasks, such as setting the window title or changing the color palette. Crucially, an OSC sequence treats all following characters as command arguments until it receives a termination signal. As a result, the terminal stopped printing your output to the screen and instead treated the subsequent numbers, brackets, and newlines as an incoming control command.

Why Does It Magically Resume at 280?

The %c format specifier expects an unsigned char. When integer values exceed 255, they wrap around modulo 256:

280 % 256 = 24 (Hexadecimal 0x18)

In standard ASCII/ANSI terminal specifications, 0x18 is the control code for CAN (Cancel). When a terminal emulator encounters the CAN character while parsing an escape sequence, it immediately aborts the current sequence and returns to normal text rendering. That is why standard output suddenly re-emerges right at 280.

How to Safely Print Characters in C

Raw bytes should never be written directly to a terminal unless you are certain they are valid, printable characters or correctly formatted UTF-8 sequences.

1. Filter with isprint()

If you want to inspect ASCII characters safely, use isprint() from <ctype.h> to only output printable characters:

#include <stdio.h>
#include <ctype.h>

int main(void) {
    for (int c = 0; c < 300; c++) {
        unsigned char ch = (unsigned char)c;
        if (isprint(ch)) {
            printf(">%c< (%d)\n", ch, c);
        } else {
            printf(">[non-printable]< (%d)\n", c);
        }
    }
    return 0;
}

2. Output as Hexadecimal or Escaped Formats

When debugging raw binary data or byte streams, avoid printing raw bytes with %c. Instead, display their hexadecimal values or visualize control codes explicitly:

#include <stdio.h>

int main(void) {
    for (int c = 0; c < 300; c++) {
        printf(">0x%02X< (%d)\n", (unsigned char)c, c);
    }
    return 0;
}

Summary

Your program was functioning properly all along. The missing output occurred because the byte 157 (0x9D) initiated an unclosed terminal escape sequence (OSC), effectively hiding your output until byte 280 (0x18, CAN) canceled the sequence and restored the display.