Introduction to Computer Science YMT113 · Fall 2026 Assoc. Prof. Ferhat Uçar
04 C Monday, October 12, 2026

Arrays, Strings and the Command Line

What the compiler really does in four steps, how to debug instead of guessing, and the first data structure: arrays, including strings, which are arrays of characters. Plus command-line arguments and a first taste of cryptography.

CS50Week 2 · Arrays ReadK&R 1.6–1.9 (arrays, characters); CS50 Week 2 notes >_ Lab 3: Strings and Ciphers

Before class · do this at home

Watch, read, then come

CS50x 2026 · Week 2: Arrays by David J. Malan. Watch the lecture on YouTube (you are also enrolled on edX), read the notes, skim the slides. Our lecture builds on it instead of repeating it.

  • Watch the lecture with cs50.dev open in another tab and type scores.c and string.c yourself as they appear.
  • Lab 2 closed on Friday; your score is under Releases in your repository. If anything is unclear about it, bring the question to the lab.
  • Read the semester project page: how it works, milestones and how it is graded. Projects are assigned next week.

Learning objectives

By the end of this week you can:

  • 1 Name the four stages of compilation (preprocessing, compiling, assembling, linking) and what make hides.
  • 2 Debug with printf, the VS Code debugger (debug50) and rubber-duck debugging.
  • 3 Declare, fill and loop over arrays, and pass arrays to functions.
  • 4 Explain that a string is a char array ending with '\0', and use strlen, string indexing and ctype functions.
  • 5 Read command-line arguments through argc and argv, and return exit codes from main.
  • 6 Implement a Caesar cipher on strings.

★ the wow moment

A string is a lie: there is no such thing as text in C, only bytes and a zero at the end.

"HI!" is four bytes in memory: 72, 73, 33 and a 0 that marks the end. printf keeps printing until it meets that zero. Forget the zero and it prints whatever garbage comes next in memory, which is exactly how many real programs leaked passwords. The CS50 library's string type is just a nickname for 'the address of the first char', and by week 6 you will not need the nickname.

Taking the hood off

Last week you learned to write a program. This week you learn what you were actually doing when you typed make, how to find out why a program misbehaves, and how C stores more than one value under one name. Those three things, compilation, debugging and arrays, are the tools; the payoff at the end of the page is that you understand what a string really is, and you can encrypt one.

All programs are from CS50’s Week 2 lecture. Each orange box is a program we open and run in VS Code; the file name is a download link, and the full set is in src2.zip.

What make really does

Until now, make hello turned hello.c into a program as if by magic. It is not one step but four, and the names come back for the rest of your degree.

  1. Preprocessing. Every line that starts with # is handled first. #include <stdio.h> is replaced by the contents of that header file, which is where the compiler learns that a function called printf exists and what arguments it takes. The prototypes you wrote last week do for your own functions what header files do for the library’s.
  2. Compiling. The C is translated into assembly, a human-readable form of the processor’s instructions: movl, call, ret. If you are curious what your hello.c looks like at this level, paste it into Compiler Explorer.
  3. Assembling. The assembly becomes machine code, the actual zeros and ones for your CPU.
  4. Linking. Your machine code is glued together with the machine code of the libraries you used, stdio and, in our labs, cs50.c. That is why the lab compiles with gcc -o hello hello.c cs50.c: two files in, one program out.

The word compiling is used for the whole pipeline, and that is fine, as long as you know there is a pipeline.

Debugging

Every program you write from now on will have bugs. The question is not whether, but how fast you find them. Three techniques, from the simplest to the most powerful.

download
// Buggy example for printf

#include <stdio.h>

int main(void)
{
    for (int i = 0; i <= 3; i++)
    {
        printf("#\n");
    }
}
download
// Buggy example for printf

#include <stdio.h>

int main(void)
{
    for (int i = 0; i <= 3; i++)
    {
        printf("i is %i\n", i);
        printf("#\n");
    }
}
download
// Buggy example for debug50

#include <cs50.h>
#include <stdio.h>

void print_column(int height);

int main(void)
{
    int h = get_int("Height: ");
    print_column(h);
}

void print_column(int height)
{
    for (int i = 0; i <= height; i++)
    {
        printf("#\n");
    }
}

The third technique needs no software. Explain your code, line by line, out loud, to someone who does not understand it: a friend, the CS50 duck at cs50.ai, or an actual rubber duck on your desk. Halfway through the explanation you will usually hear the bug yourself. Programmers call this rubber-duck debugging, and it is not a joke.

Arrays

Suppose you have three exam scores and want their average.

download
// Averages three (hardcoded) numbers

#include <stdio.h>

int main(void)
{
    // Scores
    int score1 = 72;
    int score2 = 73;
    int score3 = 33;

    // Print average
    printf("Average: %f\n", (score1 + score2 + score3) / 3.0);
}

Notice the 3.0 rather than 3: dividing by an int would truncate, as you saw last week.

download
// Averages three numbers using an array

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    // Get scores
    int scores[3];
    scores[0] = get_int("Score: ");
    scores[1] = get_int("Score: ");
    scores[2] = get_int("Score: ");

    // Print average
    printf("Average: %f\n", (scores[0] + scores[1] + scores[2]) / 3.0);
}
download
// Adds a constant

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    // Number of scores 
    const int N = 3;

    // Get scores
    int scores[N];
    for (int i = 0; i < N; i++)
    {
        scores[i] = get_int("Score: ");
    }

    // Print average
    printf("Average: %f\n", (scores[0] + scores[1] + scores[2]) / (float) N);
}

What if you read scores[3] in an array of three? C does not stop you. You get whatever bytes happen to sit after the array, or you crash, or you overwrite something that matters. This silence is what makes C fast and what makes it dangerous; a whole category of security bugs, buffer overflows, is nothing but writing past the end of an array.

Strings are arrays

download
// Prints chars

#include <stdio.h>

int main(void)
{
    char c1 = 'H';
    char c2 = 'I';
    char c3 = '!';

    printf("%c%c%c\n", c1, c2, c3);
}
download
// Prints string

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    string s = "HI!";
    printf("%s\n", s);
}

This is the wow moment of the week: there is no such thing as text in C. There are bytes, and an agreement that a zero means stop. printf("%s") prints byte after byte until it meets that zero. Erase the zero and it keeps going into whatever memory comes next, which is exactly how a famous bug in 2014, Heartbleed, leaked passwords from web servers. The notebook at the bottom of the page lets you break a string on purpose and watch what happens.

download
// Multiple strings

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    string s = "HI!";
    string t = "BYE!";

    printf("%s\n", s);
    printf("%s\n", t);
}

Walking a string

download
// Determines the length of a string

#include "cs50.h"
#include <stdio.h>

int main(void)
{
    // Prompt for user's name
    string name = get_string("Name: ");

    // Count number of characters up until '\0' (aka NUL)
    int n = 0;
    while (name[n] != '\0')
    {
        n++;
    }
    printf("%i\n", n);
}
download
// Prints string char by char, using strlen

#include <cs50.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
    string s = get_string("Input:  ");
    printf("Output: ");
    for (int i = 0; i < strlen(s); i++)
    {
        printf("%c", s[i]);
    }
    printf("\n");
}
download
// Uppercases a string

#include <cs50.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
    string s = get_string("Before: ");
    printf("After:  ");
    for (int i = 0, n = strlen(s); i < n; i++)
    {
        if (s[i] >= 'a' && s[i] <= 'z')
        {
            printf("%c", s[i] - 32);
        }
        else
        {
            printf("%c", s[i]);
        }
    }
    printf("\n");
}

The <ctype.h> family is small and worth knowing: isalpha, isdigit, isupper, islower, isspace, toupper, tolower. The lab uses most of them.

Command-line arguments

So far every input came from get_string while the program was running. Programs can also take input before they run, on the command line: make hello, gcc -o hello hello.c, ./caesar 13. Those words after the command are arguments, and C hands them to main.

download
// Uses get_string

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    string answer = get_string("What's your name? ");
    printf("hello, %s\n", answer);
}
download
// Returns explicit value from main

#include <cs50.h>
#include <stdio.h>

int main(int argc, string argv[])
{
    if (argc != 2)
    {
        printf("Missing command-line argument\n");
        return 1;
    }
    printf("hello, %s\n", argv[1]);
    return 0;
}

Cryptography, finally

Everything above comes together in one classic program. Cryptography is the art of scrambling information so that only the intended reader can unscramble it. The text you start with is the plaintext, the scrambled result is the ciphertext, and the secret that controls the scrambling is the key.

The oldest cipher we know is attributed to Julius Caesar: shift every letter by a fixed number of places. With a key of 1, HI becomes IJ; with 13, HELLO becomes URYYB. Once you know that a letter is a number, the whole algorithm is one line of arithmetic: a letter’s position in the alphabet is c - 'A'; add the key; wrap around with % 26 so that Z becomes A again; add 'A' back to turn the position into a letter.

char rotate(char c, int key)
{
    if (isupper(c))
    {
        return 'A' + (c - 'A' + key) % 26;
    }
    if (islower(c))
    {
        return 'a' + (c - 'a' + key) % 26;
    }
    return c;   // spaces, digits and punctuation pass through
}

Caesar is a toy today; a computer tries all 25 keys in a microsecond. But the pattern, walk the text, transform each character with a key, is the ancestor of the ciphers protecting your Wi-Fi and your bank. In the lab you build it, with the key coming from the command line, and with the usage check and exit code that make it a proper tool.

Summary

  • make runs four steps: preprocessing, compiling, assembling, linking. Header files tell the compiler what the library offers.
  • Debug with a printf flashlight, with breakpoints in the debugger, and by explaining the code out loud. Off-by-one errors live in loop conditions.
  • An array stores many values of one type under one name, indexed from 0. It does not know its own length, so functions receive the length as a parameter. Reading past the end is silent and dangerous.
  • A string is an array of char ending in '\0'. strlen counts to that zero; <ctype.h> classifies and converts characters; letters are numbers, so arithmetic on them is allowed.
  • int main(int argc, string argv[]) receives command-line arguments; check argc before using argv[1]; return 0 for success and non-zero for errors.
  • Caesar: (c - 'A' + key) % 26 + 'A'. The lab, Lab 3: Strings and Ciphers, is min/max/average of an array, a readability grade for any text, and the cipher.

In industry

  • Buffer overflows

    Writing past the end of an array is the single most exploited bug class in history (Morris worm 1988, Heartbleed 2014). C lets you do it silently; that is why memory-safe languages like Rust exist, and why C programmers test bounds religiously.

  • argv is every CLI tool

    git commit -m "msg", gcc -o hello hello.c, ls -l: every command-line program parses argv exactly as you do this week.

  • Cryptography is arithmetic on characters

    Caesar is a toy, but the pattern, shift each byte by a key, is the ancestor of the stream ciphers that encrypt your Wi-Fi.

  • Exit codes drive automation

    return 0 for success, non-zero for failure. Continuous-integration systems, including the autograder, read this number to decide whether your build is green.

Lab 3: Strings and Ciphers

Arrays and strings under the hood: min, max and average of an array through functions; a readability grade for any text; and a Caesar cipher driven from the command line with a proper usage message and exit code. C · due Fri, Oct 16 · 23:59 · 10 points · open now Open lab instructions →

Lab 3 · Explore: a string, byte by byte

Read it here, or open it in Colab: it writes, compiles and runs C right in the browser.

Go deeper

Tags:arraysstringsnull terminatorstrlenctypeargcargvexit statusCaesar ciphercompilationdebugging