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.