Lab 3 · Explore: a string, byte by byte¶

Introduction to Computer Science (YMT113) · Fall 2026

Nothing here is graded. The cells write small C programs, compile them with gcc and run them, so you can poke at what a string really is. Runs in Google Colab (gcc is preinstalled) or in your codespace.

1 · HI! is four bytes¶

A string ends with a zero byte, \0 (NUL). printf("%s") prints until it meets it. Let's print the bytes, including the one you never typed.

%%writefile hi.c
#include <stdio.h>

int main(void)
{
    char s[] = "HI!";
    for (int i = 0; i < 4; i++)
    {
        printf("s[%i] = %3i  '%c'\n", i, s[i], s[i] == 0 ? '0' : s[i]);
    }
    printf("sizeof(s) = %zu bytes\n", sizeof s);
}
Writing hi.c
!gcc -o hi hi.c && ./hi
s[0] =  72  'H'
s[1] =  73  'I'
s[2] =  33  '!'
s[3] =   0  '0'
sizeof(s) = 4 bytes

2 · Letters are numbers¶

Because characters are numbers, you can do arithmetic on them. 'a' + 1 is 'b', and c - 'a' is a letter's position in the alphabet. That is all a Caesar cipher needs.

%%writefile rot.c
#include <stdio.h>

int main(void)
{
    char msg[] = "HELLO, world";
    int key = 13;
    for (int i = 0; msg[i] != '\0'; i++)
    {
        char c = msg[i];
        if (c >= 'A' && c <= 'Z')
            c = 'A' + (c - 'A' + key) % 26;
        else if (c >= 'a' && c <= 'z')
            c = 'a' + (c - 'a' + key) % 26;
        printf("%c", c);
    }
    printf("\n");
}
Writing rot.c
!gcc -o rot rot.c && ./rot
URYYB, jbeyq

Run it twice: encrypting with 13 and then encrypting the result with 13 again gives the original. That is why ROT13 is its own inverse.

3 · What happens without the zero¶

Overwrite the terminator and printf keeps reading memory until it happens to find a zero. The output differs from machine to machine, which is exactly the point: it is undefined behavior.

%%writefile nozero.c
#include <stdio.h>

int main(void)
{
    char a[] = "HI!";
    char b[] = "bye";
    a[3] = '!';          // the NUL is gone
    printf("%s\n", a);  // prints HI!! and then whatever follows in memory
}
Writing nozero.c
!gcc -Wno-array-bounds -o nozero nozero.c && ./nozero
HI!!bye

4 · The same thing in Python, for comparison¶

Python hides all of this: a string knows its own length and has no terminator. ord and chr are C's implicit conversions, made explicit.

msg = "HELLO, world"
print([ord(c) for c in "HI!"])
print("".join(chr((ord(c) - 65 + 13) % 26 + 65) if c.isupper() else chr((ord(c) - 97 + 13) % 26 + 97) if c.islower() else c for c in msg))
[72, 73, 33]
URYYB, jbeyq

5 · Your turn¶

Change the key, the message, or print each character's number next to it. Try a key of 26, then 27.