UP | HOME

For Loops, Arrays, and Strings
COP-3223H

Table of Contents

New construct: increment (++)

i++;

means

i = i + 1;

Syntactic sugar for reassigning a variable to the value plus one.

Similarly, i-- means decrement, i = i - 1

The i++ operator is postfix, i.e., the operator appears after the variable.

Prefix variations

  • ++i
  • --i

Prefix vs. postfix affects when the increment/decrement happens:

  • Prefix, e.g., ++i, increment the value before using the variable's value.
  • Postfix, e.g., i++, increment the value after using the variable's value.
int i = 1;
printf("%d\n", i++);  // prints 1 (increment after using)
printf("%d\n", i);    // prints 2

int j = 1;
printf("%d\n", ++j);  // prints 2 (increment before using)
printf("%d\n", j);    // prints 2

For loops

for (INITIALIZE; CONDITION; ITERATOR) {
  instructions;
 }
  1. Run INITIALIZE
  2. Check CONDITION
  3. If it's true, run instructions
  4. Then run ITERATOR
  5. Then repeat from #2
  6. If it's not true, done, continue after the for loop

Example: printing digits

What is the output of this program?

for (int i = 0; i < n; i++) {
  printf("%d\n", i);
 }

Notice that i++ makes this slightly more concise to write, instead of

for (int i = 0; i < n; i = i + 1) {
  printf("%d\n", i);
 }

Demonstrating for-loop semantics, boundary conditions, etc.

#include <stdio.h>

int main() {
  int n = 10;
  for (int i = 0; i < n; i++) {
    printf("%d\n", i);
  }
  printf("done\n");

  for (int i = 0; i < n; i = i + 1) {
    printf("%d\n", i);
  }
  printf("done\n");  

  for (int i = 0; i < n; i = i + 2) {
    printf("%d\n", i);
    // once i is eight
  }
  printf("done\n");  

  for (int i = 0; i < n; i = i + 2) {
    printf("%d\n", i);
    // once i is eight
  }
  printf("done\n");  


  for (int i = 9; i < n; i++) {
    printf("%d\n", i);
  }

  // i = 9
  // i < n => 0 < 10 true
  // print 9
  // i++ => 9++ => 10
  // i < n => 10 < 10
  // for loop is done
  printf("done\n");  

  int i;
  for (i = 10; i < n; i++) {
    printf("%d\n", i);
  }
  // i = 10
  // i < n => 10 < 10 false
  // continue after the for loop
  printf("done\n");
  printf("%d\n", i);
}

Example: equivalent while loop

for (int i = 0; i < n; i++) {
  printf("%d\n", i);
 }
int i = 0;
while (i < n) {
  instructions;
  i++;
 }

Aside on C scoping rules

#include <stdio.h>

int main() {  // new "scope" for main
  int i = 1;

  printf("%d\n", i);
  if (i > 0) { // new "scope" for a set of variables for if statement
    i = 2;

    printf("%d\n", i);
    if (i > 0) {
      i = 3;
    }
    printf("%d\n", i);

  } // scope is thrown away along with any variable declared here

  // still inside main's scope
  printf("%d\n", i);

}
#include <stdio.h>

int global;

int main(int argc, char **argv) {
  int n;

  n = 100;

  int x;

  x = 11;  // main.x


  int mainsvar = 8;  // main.mainsvar


  // cs.ucf.edu.
  // cs.nyu.edu.
  // cs.ucf.com.

  int i = 10;  // main.i
  for (int i = 0; i < n; i++) {  // // main.forloop.i
    int x;  // main.forloop.x
    x = 10;
    mainsvar = 7;
    printf("%d\n", i);
    printf("inside loop mainsvar: %d\n", mainsvar);
    printf("inside loop x: %d\n", x);
  }
  printf("%d\n", i);

  {
    int i = 2;
  }
  printf("%d\n", i);

  printf("mainsvar: %d\n", mainsvar);
  printf("x: %d\n", x);
}

General while loop implementation

for (INITIALIZE; CONDITION; ITERATOR) {
  instructions;
 }
INITIALIZE;
while (CONDITION) {
  instructions;
  ITERATOR;
 }

Technically, the equivalence would be

{
  INITIALIZE;
  while (CONDITION) {
    instructions;
    ITERATOR;
  }
}

This ensures that any declarations in INITIALIZE are only scoped to the while loop, and do not interfere with declarations above the while loop.

Example: sum of 1..N

#include <stdio.h>

int main() {
  int n;
  int sum;
  scanf("%d", &n);

  // sum the numbers from 1 to n
  // assume 1 to N is inclusive
  sum = 0;
  for (int i = 1; i < n + 1; i++) {
    sum = sum + i;
  }

  //  what values of i will we have (inside the loop body)?
  // n large enough i = { 1, 2, 3, ..., N }
  // n = 0? i = { }
  // n = 1? i = { 1 }
  // sum = {  1, 3, 6, ... }


  printf("%d\n", sum);
}

Example: exponent

#include <stdio.h>

int main() {
  int x;
  int e;
  int result;
  scanf("%d", &x);
  scanf("%d", &e);

  //  x^e = x * x * x * ... e times
  result = 1;
  for (int i = 1; i <= e; i++) {
    result = result * x;
  }

  printf("%d\n", result);
}

Example: factorial

Example: counting down

for (int i = 9; i >= 0; i--) {
}

Example: sum of powers of two

int sum = 0;
for (int i = 0; i < N; i++) {
  int pow = 1;
  for (int j = 0; j < i; j++) {
    pow = 2 * pow;
  }
  sum = sum + pow;
}

More efficient way to do this?

Arrays

  • Variables store one value
  • Arrays store one or more values
  • Uses a single variable name

Defining a fixed-length array

int a[] = { 1, 2, 3, 4, 5 };

All values have the same datatype, e.g., int in this case.

Operations

Looks like variables, but with a "subscript" in brackets

a[0];

Base-0

  • Arrays indices start at zero
int a[] = { 1, 2, 3, 4, 5 };
Index Value
0 1
1 2
2 3
3 4
4 5

Note that there is no index 5. The array has five elements, but starting the count as 0 means there are only indices 0 (inclusive) through 4 (inclusive). If you take the length of the array, that is the upper bound, exclusive, i.e,.

[0, 5)

0 <= index < 5

Assigning an array at an index

a[0] = 2;

Reading an array at an index

printf("%d\n", a[0]);

Memory diagram

Diagram

  • Variable names one memory box
  • Array names several memory boxes
  • Index starts at 0
  • We can think of this as an offset from 0

Example: reading and writing a fixed array

#include <stdio.h>

int main(int argc, char **argv) {
  int a[] = { 1, 2, 3, 4, 5 };
}

Uninitialized arrays

int a[5];

You don't need to initialize an array on declaration. Instead you can declare the variable, and all memory slots will be uninitialized as with scalar variables. It may be difficult to reason about which indicies have been initialized, so it is good practice to carefully initialize arrays before usage, e.g., with a for loop. Be careful about crafting the loop, as mistakes like off-by-one errors can lead to uninitialized indicies or out-of-bounds array accesses.

Initializer loop

int a[5];

for (int i = 0; i < 5; i++) {
  a[i] = i + 1;
}

Make the for loop match the array, i.e., start at 0 and end at the array length (exclusive).

C gotcha: out-of-bounds array accesses

Compilers can catch simple-to-reason-about instances. In general, though, it is difficult or impossible to do so. For simplicity of implementation, most C compilers will produce machine code that performs the out of bounds memory access.

Example: what does this array contain?

int a[10];

for (int i = 0; i < 10; i++) {
  a[i] = i * i;
}

Example: what does this array contain?

int a[10];

for (int i = 0; i < 10; i++) {
  a[i] = 1;
}

for (int i = 1; i < 10; i++) {
  a[i] = a[i - 1] * 2;
}

Example: what does this array contain?

int a[10];

for (int i = 0; i < 1; i++) {
  a[i] = 0;
}

for (int i = 1; i < 3; i++) {
  a[i] = 1;
}

for (int i = 3; i < 10; i++) {
  a[i] = a[i - 1] + a[i - 2];
}

Notice how the for-loop boundaries "link" together when using the inclusive/exclusive pattern, i.e., the first assigment is [0, 1), and the first loop is from [1, 3), while the second loop is from [3, 10). It's easy to see that this array is completely initialized by joining [0, 1), [1, 3) and [3, 10) to be [0, 10), the complete set of array indices.

Example: dot product

Tic-tac-toe

  • 3x3 board
  • 2 players take turns placing X and O

bool type

#include <stdbool.h>

int main(int argc, char **argv) {
  bool b;

  b = true;
  if (b) {
  } else {
  }

  b = false;
  if (b) {
  } else {
  }

  int x = 1;
  b = x > 0;
  if (b) {
  } else {
  }
}

Game code

#include <stdio.h>
#include <assert.h>
#include <stdbool.h>

#include "term.c"

int main(int argc, char **argv) {
  set_input_mode ();
  char board[] = { '1', '2', '3', '4', '5', '6', '7', '8', '9' };
  int boardlen = 9;

  enum { xturn, oturn, xwin, owin, tie };
  int state = xturn;

  // turn 0 is x, turn 1 is o.
  while (1) {
    printf("\x1b[2J");
    printf("\x1b[H");

    // draw board
    for (int i = 0; i < boardlen; i++) {
      switch (board[i]) {
      case 'O':
        printf("\033[31m");
        break;
      case 'X':
        printf("\033[32m");
        break;
      default:
        printf("\033[30m");
        break;
      }
      putchar(board[i]);
      putchar(' ');
      if ((i % 3) == 2) {
        putchar('\n');
      }
    }

    // draw state and quit if done
    switch (state) {
    case xturn:
      printf("\033[32m");
      printf("X's turn\n");
      break;
    case oturn:
      printf("\033[31m");
      printf("O's turn\n");
      break;
    case xwin:
      printf("\033[32m");
      printf("X wins\n");
      exit(0);
      break;
    case owin:
      printf("\033[31m");
      printf("O wins\n");
      exit(0);
      break;
    case tie:
      printf("\033[30m");
      printf("tie\n");
      exit(0);
      break;
    default:
      // should never reach here
      assert(0);
      break;
    }

    assert(xturn == state || oturn == state);

    // user input
    char c;
    scanf("%c", &c);

    // search for board index
    int index = -1;
    for (int i = 0; i < boardlen; i++) {
      if (c == board[i]) {
        index = i;
      }
    }

    // check for valid move
    if (   0 <= index && index < 9
        && !('X' == board[index] || 'O' == board[index])) {
      // place the X or O
      switch (state) {
      case xturn:
        board[index] = 'X';
        state = oturn;
        break;
      case oturn:
        board[index] = 'O';
        state = xturn;
        break;
      default:
        assert(0);
        break;
      }
    }

    // check for win
    bool is_xwin = false;
    bool is_owin = false;
    // horizontal and vertical
    for (int i = 0; i < 3; i++) {
      is_xwin = is_xwin || (   'X' == board[i * 3 + 0]
                            && 'X' == board[i * 3 + 1]
                            && 'X' == board[i * 3 + 2])
                        || (   'X' == board[0 + i]
                            && 'X' == board[3 + i]
                            && 'X' == board[6 + i]);
      is_owin = is_owin || (   'O' == board[i * 3 + 0]
                            && 'O' == board[i * 3 + 1]
                            && 'O' == board[i * 3 + 2])
                        || (   'O' == board[0 + i]
                            && 'O' == board[3 + i]
                            && 'O' == board[6 + i]);
    }
    //diagonal
    is_xwin = is_xwin
      || ( 'X' == board[0] && 'X' == board[4] && 'X' == board[8] )
      || ( 'X' == board[2] && 'X' == board[4] && 'X' == board[6] );
    is_owin = is_owin
      || ( 'O' == board[0] && 'O' == board[4] && 'O' == board[8] )
      || ( 'O' == board[2] && 'O' == board[4] && 'O' == board[6] );

    assert(! (is_xwin && is_owin));

    if (is_xwin) {
      state = xwin;
    } else if (is_owin) {
      state = owin;
    } else { // no one has won (yet)
      // check for filled board
      int turns = 0;
      for (int i = 0; i < boardlen; i++) {
        if ('O' == board[i] || 'X' == board[i]) {
          turns++;
        }
      }
      if (boardlen == turns) {  // check for tie
        state = tie;
      } else {
        // no win or tie, keep playing
      }
    }
  }
}
wget https://www.cs.ucf.edu/~gazzillo/teaching/cop3223hspring26/files/term.c

State machine

State
x's turn
o's turn
x wins
o wins
tie

Transitions

  • x's turn
    • valid move -> o's turn
    • winning move -> x wins
    • tying move -> tie
    • invalid move -> x's turn
  • o's turn
    • valid move -> x's turn
    • winning move -> o wins
    • tying move -> tie
    • invalid move -> o's turn

enum

  • Give names to numbers.
enum { xturn, oturn, xwin, owin, tie };
int game_state = xturn;

Compiler assigns integers under the hood.

enum values in switch statements

  • Makes it more intuitive to track state.
switch(game_state) {
  case xturn:
    // ...
    break;
  case oturn:
    // ...
    break;
  case xwin:
    // ...
    break;
  case owin:
    // ...
    break;
  case tie:
    // ...
    break;
  default:
    // ...
    break;
}

Multidimensional arrays

#include <stdio.h>

int main(int argc, char **argv) {
  int a[3][3];

  a[0][1] = 10;
}

Example: tic-tac-toe

Example: matrix multiplication

Strings

Recall the "hello, world!\n" program:

printf("hello, world!\n");

Strings are character arrays

char str[] = "hello, world!\n";
printf("%s", str);  // %s is for strings (character arrays)
#include <stdio.h>

int main(int argc, char **argv) {
  /* printf("hello, world!\n"); */

  char str[] = "hello, world!\n";

  char nullchar = '\0'; 

  /* printf("%s", str); */

  int len = 0;

  printf("print using a for loop\n");
  /* for (int i = 0; i < 14; i++) { */
  /*   putchar(str[i]); */
  /* } */
  char c;
  int i = 0;
  while ( (c = str[i++]) != '\0') {
    putchar(c);
  }
}
#include <stdio.h>

int main(int argc, char **argv) {
  printf("hello, world!\n");

  char str[] = "hello, world!\n";

  printf("%s", str);

  str[7] = 'p';
  str[8] = 'a';
  str[9] = 'u';
  str[10] = 'l';
  str[11] = '!';

  printf("%s", str);
}

Exploiting Format String Vulnerabilities

Recall: the character type

  • Characters represent single digits, letters, punctuation, etc.
  • Wrapped with single quotes: '

Recall that there are also non-printable characters like '\n';

Strings are null-delimited

Diagram

  • Memory slots of string character array
  • Null character ends the string '\0'.

By convention, strings do not have fixed sizes, but end with a special character, null '\0'

Why Pascal Is Not My Favorite Programming Language : Brian W. Kernighan : Fre…

Character arrays

char chararray[] = { 'h', 'e', 'l', 'l', 'o', '!', '\n', '\0' };

printf("%s", chararray);

Just like integer arrays, we can make a character array.

char chararraydelim[] = { 'h', 'e', 'l', 'l', 'o', '!', '\n', '\0', 'X', '\n' };

printf("%s", chararraydelim);

char chararraynodelim[] = { 'h', 'e', 'l', 'l', 'o', '!', '\n', 'X', '\n' };

printf("%s", chararraynodelim);
#include <stdio.h>

int main(int argc, char **argv) {
  /* char str[] = "hello, world!\n"; */

  /* printf("%s\n", str); */

  /* str[7] = 'p'; */
  /* str[8] = 'a'; */
  /* str[9] = 'u'; */
  /* str[10] = 'l'; */
  /* str[11] = '!'; */

  /* printf("%s", str); */

  char chararray[] = { 'h', 'e', 'l', 'l', 'o', '!', '\n', '\0' };

  printf("FIRST: %s", chararray);

  char chararraydelim[] = { 'e', 'e', 'l', 'l', 'o', '!', '\n', '\0', 'X', '\n' };

  printf("SECOND: %s", chararraydelim);

  char chararraynodelim[] = { 'h', 'e', 'l', 'l', 'o', '!', '\n',     'X', '\n' };

  printf("THIRD: %s", chararraynodelim);
}

String literals

  • Like integer (1, 43, etc.), float, or characters
  • Wrap with double quotes: "
char chararray = "hello!\n";

This array has the same contents as the previous array

Example: counting characters in a string

Write a program that, given a string str, counts how often a character c appears in string.

String operations

Getting length

With a loop

int i = 0;
while ('\0' != s[i]) {
  i++;
}
int i = 0;
while ('\0' != s[i++]);

Function

strlen

Compare strings

#include <stdio.h>

int main(int argc, char **argv) {
  char str1[] = "hello!\n";
  char str2[8] = { 'h', 'e', 'l', 'l', 'o', '!', '\n', '\0' };

  printf("%s", str1);
  printf("%s", str2);

  printf("str1 == str2?\n");
  if (str1 == str2) {
    printf("yes!\n");
  } else {
    printf("no!\n");
  }

  printf("str1[0] == str2[0]?\n");
  if (str1[0] == str2[0]) {
    printf("yes!\n");
  } else {
    printf("no!\n");
  }
}

str1 and str2 are the addresses of the beginning of the string, not the actual characters.

str1 and str2 are pointing to different memory slots, so these are not the same.

Example: compare strings by hand

Safe(r) usage

Operation Function
strncmp Compare two strings
fgets Read a string
strlcpy Copy a string

Comparing strings

strncmp

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

int main(int argc, char **argv) {
  char str1[] = "hello!\n";
  char str2[8] = { 'h', 'e', 'l', 'l', 'o', '!', '\n', '\0' };

  printf("%s", str1);
  printf("%s", str2);

  printf("str1 == str2?\n");
  if (str1 == str2) {
    printf("yes!\n");
  } else {
    printf("no!\n");
  }





  printf("str1[0] == str2[0]?\n");
  if (str1[0] == str2[0]) {
    printf("yes!\n");
  } else {
    printf("no!\n");
  }



  printf("%d\n", strncmp(str1, str2, 8));

  if (strncmp(str1, str2, 8) == 0) {
    printf("yes!\n");
  } else {
    printf("no!\n");
  }
}
The strcmp() function compares the two strings s1 and s2.  The locale is not taken into account (for a locale-aware
comparison, see strcoll(3)).  The comparison is done using unsigned characters.

strcmp() returns an integer indicating the result of the comparison, as follows:

•  0, if the s1 and s2 are equal;

•  a negative value if s1 is less than s2;

•  a positive value if s1 is greater than s2.

The strncmp() function is similar, except it compares only the first (at most) n bytes of s1 and s2.

Reading strings

fgets with size less than some given value

#include <stdio.h>

int main(int argc, char **argv) {
  int destlen = 20;
  char dest[destlen];

  // try less than, equal to, and greater than destlen (null terminator?)
  fgets(dest, destlen, stdin);

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

gets, never use this function

gets(3)                                          Library Functions Manual                                          gets(3)

NAME
       gets - get a string from standard input (DEPRECATED)

LIBRARY
       Standard C library (libc, -lc)

SYNOPSIS
       #include <stdio.h>

       [[deprecated]] char *gets(char *s);

DESCRIPTION
       Never use this function.
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s.
Reading stops after an EOF or a newline.  If a newline is read, it is stored into the buffer.  A  terminating  null
byte ('\0') is stored after the last character in the buffer.

Copying strings

strlcpy

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

int main(int argc, char **argv) {
  char str[] = "hello!";
  int destlen = 3;
  /* int destlen = 20; */
  char dest[destlen];

  strlcpy(dest, str, destlen);

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

If we just try to copy using assignment, we will overwrite the original string.

strlcpy and strlcat - consistent, safe, string copy and concatenation.

Author: Paul Gazzillo

Created: 2026-10-14 Wed 10:50

Validate