Programming Fundamentals

Objectives

  • introduction to pointers
  • scanning until ctrl-d
  • processing of characters and strings
  • use of functions

Feedback Week!

In this week's lab, your tutors will go around the class and give you one on one feedback on some code you have written in a previous week.

Take this as an opportunity to ask any questions you might have about the course content so far!

So, if you would like, have a think about if there is any particular exercise you would like to receive feedback for, or any particular content you would like to ask about.

Reminder: Help sessions

Help sessions are running this week!

These are one of the best ways for you to get one on one help with a tutor for any course content (including Lab Exercises and Assignments).

For the dates and times of the help sessions, see the Help Session Timetable.

To join a help session, or for more information, see the COMP(1511|1911) Help Session Microsoft Teams.

For face-to-face help sessions, the lab map can be found here. If help sessions are running in other buildings you can use Lost on Campus to help you find them.

Activities To Be Completed

The following is a list of all the activities available to complete this week...

Worth 1 mark(s) in total:

  • print_address
  • debug_increment
  • array_sum_prod
  • swap_pointers
  • my_scanf
  • string_match

Worth 1 mark(s) in total:

  • summation
  • stellar_information
  • substitution

Worth 0.5 mark(s) in total:

  • frequency_analysis

For your interest, but not for marks:

  • word_search

Problem sets are capped at 15 marks (there are 4 possible bonus marks from the three-dot exercises that can bring you up to a total of 15 if you missed out on any other marks in the one- or two-dot exercises).

Completing just the one and two-dot exercises every week can give you the full 15 marks needed in this component.

For more details, see the course outline.

Preparation

Before the lab you should re-read the relevant lecture slides and their accompanying examples.

When attempting the following exercises, make sure to read the whole exercise, including any hints and assumptions that may make the exercise easier.

Videos

The following short videos may be of some help to get started on the lab exercises.

If you have watched the videos and are still unsure how to start the exercises, ask your tutor for help :)

Exercise
(●◌◌)
:

Print Address

Download print_address.c here

Or, copy these file(s) to your CSE account using the following command:

1511 fetch-activity print_address
In this activity, you will complete the C program print_address.c which attempts to print a memory address and value at that address, using pointers.

In main, you are provided with a variable number of type int and a supposed pointer to number called number_pointer.

Your main should do the following;

  1. Correctly initialise the pointer number_pointer to point to the address of number. In the provided code, the pointer is declared incorrectly, you will need to fix this before you can initialise it.
  2. Print the memory address stored in number_pointer.
  3. Call the function print_at_address and pass the memory address stored in number_pointer to it.

The print_at_address will need to be implemented by you and it should;

  1. Print the memory address passed to it.
  2. Print the value at that memory address.

You must not change the function signature of print_at_address.

Examples

dcc print_address.c -o print_address
./print_address
Passing the memory address 0x16b44b028 to the function
The memory address passed to this function is 0x16b44b028
The value at the memory address is 42

Assumptions/Restrictions/Clarifications

  • You must not change the function signature of print_at_address.
  • You must use the function print_at_address to print the second and third lines of the output.
You can run an automated code style checker using the following command:
1511 style print_address.c

When you think your program is working, you can use autotest to run some simple automated tests:

1511 autotest print_address

When you are finished working on this exercise, you and your lab partner must both submit your work by running give:

give cs1511 lab07_print_address print_address.c

Note, even though this is a pair exercise, you both must run give from your own account before Monday 28 October 20:00 to obtain the marks for this lab exercise.

Sample solution for print_address.c
// Print the memory address of a variable
// Created by: Ibrahim Ghoneim z5470570 03/2024
// print_at_address.c

#include <stdio.h>

void print_at_address(int *memory_address);

int main(void) {

    int number = 42;

    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////// ONLY MODIFY CODE BELOW //////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    
    int *number_pointer = &number;
    printf("Passing the memory address %p to the function\n", number_pointer);
    print_at_address(number_pointer);

    return 0;
}

/*
param: memory_address - is an integer pointer that points to a memory address
return: void
Prints the memory address and the value at the memory address
*/
void print_at_address(int *memory_address) {
    printf("The memory address passed to this function is %p\n", memory_address);
    printf("The value at the memory address is %d\n", *memory_address);
    return;
}

Exercise
(●◌◌)
:

Debugging - increment

Download debug_increment.c here

Or, copy these file(s) to your CSE account using the following command:

1511 fetch-activity debug_increment

Debugging Tips!

Some debugging tips for you:

  • dcc output - as you run into issues, dcc will point you to where the errors are. Remember that dcc gives you the line number the issue is on, and will give some sort of explanation. Make sure you read everything dcc gives you. Sometimes we get “errors carried forward”, so find your first error, fix that, then recompile.
  • print statements - sometimes it can be handy to see if the flow of your code puts you in the spot you expect it to be (ie. inside the right if statement, or going through a loop the correct amount of times). A quick way you can check this is by putting print statements in your code for testing purposes, like "the value of x is %d and y is %d". This lets you check that you got against what you expected.
  • COMP1511 debugging guide

The Task

This exercise takes in a number and intends to increment the value of the number using a function increment(), which takes a pointer to an integer as its parameter. Currently it has some issues - it is your job to figure them out and fix the code.

Examples

dcc debug_increment.c -o debug_increment
./debug_increment
Please enter a number: 1
Before increment: 1
After increment: 2
./debug_increment
Please enter a number: -8
Before increment: -8
After increment: -7
./debug_increment
Please enter a number: 100
Before increment: 100
After increment: 101
You can run an automated code style checker using the following command:
1511 style debug_increment.c

When you think your program is working, you can use autotest to run some simple automated tests:

1511 autotest debug_increment

When you are finished working on this exercise, you must submit your work by running give:

give cs1511 lab07_debug_increment debug_increment.c

You must run give before Monday 28 October 20:00 to obtain the marks for this lab exercise. Note that this is an individual exercise, the work you submit with give must be entirely your own.

Sample solution for debug_increment.c
// debug_pointers.c
// This program given a number, incraments it using pointers
// Written by Sofia De Bellis, z5418801 on July 2023

#include <stdio.h>

void increment(int *ptr);

int main() {
    int num;
    printf("Please enter a number: ");
    scanf("%d", &num);
    int *ptr = &num;

    printf("Before increment: %d\n", *ptr);
    increment(ptr);
    printf("After increment: %d\n", *ptr);

    return 0;
}

void increment(int *ptr) {
    (*ptr)++;
}

Exercise
(●◌◌)
:

Calculate both the sum and product of the values in an array

Download array_sum_prod.c here

Or, copy these file(s) to your CSE account using the following command:

1511 fetch-activity array_sum_prod

Your task is to add code to this function in array_sum_prod.c:

// Calculates the sum and product of the array nums.
// Actually modifies the  variables that *sum and *product are pointing to
void array_sum_prod(int length, int nums[length], int *sum, int *product) {
    // TODO: Complete this function
}

The above file array_sum_prod.c contains a function array_sum_prod, which should find the sum and the product of the values stored in the array. It should write these values into the integers referenced by the pointers in the input to the function.

Unfortunately, the provided function doesn't actually work. For this lab exercise, your task is to complete this function.

The file also contains a main function which you can use to help test your array_sum_prod function. It has two simple test cases.

This main function will not be marked -- you must write all of your code in the array_sum_prod function.

You may modify the main function if you wish (e.g. to add further tests), but only the array_sum_prod function will be marked.

Examples

dcc -o array_sum_prod array_sum_prod.c 
./array_sum_prod 
Sum: 20, Product: 360
Sum: 10, Product: 24

Assumptions/Restrictions/Clarifications

  • You will not be given an empty array as input, you can assume that you have at least 1 value.
You can run an automated code style checker using the following command:
1511 style array_sum_prod.c

When you think your program is working, you can use autotest to run some simple automated tests:

1511 autotest array_sum_prod

When you are finished working on this exercise, you and your lab partner must both submit your work by running give:

give cs1511 lab07_array_sum_prod array_sum_prod.c

Note, even though this is a pair exercise, you both must run give from your own account before Monday 28 October 20:00 to obtain the marks for this lab exercise.

Sample solution for array_sum_prod.c
// COMP1511 Array Sum Product
// Calculate the sum and the product of the elements in an array
// and write the results into variables passed into the function
// by reference.

// Modified by Marc Chee, March 2020

#include <stdio.h>

void array_sum_prod(int length, int nums[length], int *sum, int *product);

// This is a simple main function that you can use to test your array_sum_prod
// function.
// It will not be marked - only your array_sum_prod function will be marked.
//
// Note: the autotest does not call this main function!
// It calls your array_sum_prod function directly.
// Any changes that you make to this main function will not affect the autotests.

int main(int argc, char *argv[]){
   int nums[] = {3,4,1,5,6,1};
   int prod;
   int sum;

   //Pass in the address of the sum and product variables
   array_sum_prod(6, nums, &sum, &prod);

   printf("The sum is %d and prod is %d\n",sum,prod);
   return 0;
}


// Calculates the sum and product of the array nums.
// Actually modifies the  variables that *sum and *product are pointing to
void array_sum_prod(int length, int nums[length], int *sum, int *product) {
    int i = 0;
    *sum = 0;
    *product = 1;
    while (i < length) {
        *sum = *sum + nums[i];
        *product = *product * nums[i];
        i++;
    }
}

Exercise
(●◌◌)
:

Using pointers and a function to swap number values

Download swap_pointers.c here

Or, copy these file(s) to your CSE account using the following command:

1511 fetch-activity swap_pointers

Your task is to add code to this function in swap_pointers.c:

// swap the values in two integers, given as pointers
void swap_pointers(int *a, int *b) {
    // PUT YOUR CODE HERE
}

swap_pointers should take two pointers to integers as input and swap the values stored in those two integers.

For example if the integers are:

int first = 1;
int second = 2;

After your function runs, first should be 2 and second should be 1.

Assumptions/Restrictions/Clarifications

  • swap_pointers is a void function. It cannot return any values.
  • swap_pointers should not call scanf (or getchar or fgets).
  • swap_pointers should not print anything. It should not call printf.
  • Your submitted file may contain a main function. It will not be tested or marked.
You can run an automated code style checker using the following command:
1511 style swap_pointers.c

When you think your program is working, you can use autotest to run some simple automated tests:

1511 autotest swap_pointers

When you are finished working on this exercise, you must submit your work by running give:

give cs1511 lab07_swap_pointers swap_pointers.c

You must run give before Monday 28 October 20:00 to obtain the marks for this lab exercise. Note that this is an individual exercise, the work you submit with give must be entirely your own.

Sample solution for swap_pointers.c
#include <stdio.h>

// swap the values in two integers, given as pointers
void swap_pointers(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

// This is a simple main function which could be used
// to test your swap_pointers function.
// It will not be marked.
// Only your swap_pointers function will be marked.

int main(void) {
    int first = 1;
    int second = 2;
    
    swap_pointers(&first, &second);
    
    printf("%d, %d\n", first, second);
    return 0;
}

Exercise
(●◌◌)
:

My Scanf

Download my_scanf.c here

Or, copy these file(s) to your CSE account using the following command:

1511 fetch-activity my_scanf

Ben Briant has decided to learn how scanf() works, and to help with his understanding, he has decided to write his own simple scanf functions!

They are much simpler than a regular scanf, as they only allow for scanning in a single integer or a single double (depending on if my_scanf_int() or my_scanf_double() is being called) at a time.

Both of these functions should:

  • take in a pointer to a variable that we want to scan our value into,
  • read a value from the user (using regular scanf), then
  • set the value of the input to be the value read from the user.

Unfortunately, Ben got overwhelmed with all the pointer syntax (* and &), so decided to just avoid writing any of it! Your job is to put all the * and & in the correct spots in the code.

You will have to edit both the main() function, my_scanf_int() and my_scanf_double(), But you can't change any part of the code aside from adding * or &.

Examples

dcc my_scanf.c -o my_scanf
./my_scanf 
Enter the amount of study you need to do this week (in decimal): 7.5
Enter the number of days you have free: 3
You have on average 2.50 hour(s) each free day to do homework.

Testing

When testing your program, we are checking more than that it simply produces the correct output. We are also testing that the functions work correctly individually (by replacing your main function with one of our own).

You can run an automated code style checker using the following command:
1511 style my_scanf.c

When you think your program is working, you can use autotest to run some simple automated tests:

1511 autotest my_scanf

When you are finished working on this exercise, you must submit your work by running give:

give cs1511 lab07_my_scanf my_scanf.c

You must run give before Monday 28 October 20:00 to obtain the marks for this lab exercise. Note that this is an individual exercise, the work you submit with give must be entirely your own.

Sample solution for my_scanf.c
// A program that creates its own simple scanf functions.
// Activity written by Paula

// This program was written by Ben Briant
// This program was fixed by [student]

#include <stdio.h>

void my_scanf_double(double *d) {
    double input;
    scanf("%lf", &input);
    *d = input;
}

void my_scanf_int(int *i) {
    int input;
    scanf("%d", &input);
    *i = input;
}

int main(void) {

    printf("Enter the amount of study you need to do this week (in decimal): ");
    double total_time;
    my_scanf_double(&total_time);

    printf("Enter the number of days you have free: ");
    int days;
    my_scanf_int(&days);

    double time_per_day = total_time / days;
    printf("You have on average %.2lf hour(s) each free day to do homework.\n", time_per_day);

    return 0;
}

Exercise
(●◌◌)
:

String Match

Your job is to write a program called string_match.c which lets us count the number of times we see our "search term" in a list of strings.

TASK 1 Scan in the "search term"

TASK 2: Scan in strings from standard input until Ctrl-D is pressed.

TASK 3: Count the number of times the "search term" appear in the input.

Examples

dcc string_match.c -o string_match
./string_match
Enter the search term: same
Enter the list of strings:
same
sand
same
send
shade
same
shadow

There was 3 occurrence(s) of the search term in the input.
./string_match
Enter the search term: An ostrich's eye is bigger than its brain.
Enter the list of strings:
That is a cool fact.
I never knew that!
An ostrich's eye is bigger than its brain.
ostrich?
Why the random facts?

There was 1 occurrence(s) of the search term in the input.
./string_match
Enter the search term: 42 is the meaning of life, the universe, and everything
Enter the list of strings:
The ascii for * is 42
42 is the meaning of life, the universe, and everything!!!
42 = everything

There was 0 occurrence(s) of the search term in the input.

Assumptions/Restrictions/Clarifications

  • You can assume that each string will be no longer than 128 characters long
  • You may find the strcmp() function useful
You can run an automated code style checker using the following command:
1511 style string_match.c

When you think your program is working, you can use autotest to run some simple automated tests:

1511 autotest string_match

When you are finished working on this exercise, you must submit your work by running give:

give cs1511 lab07_string_match string_match.c

You must run give before Monday 28 October 20:00 to obtain the marks for this lab exercise. Note that this is an individual exercise, the work you submit with give must be entirely your own.

Sample solution for string_match.c
// string_match.c
// Searches for words in standard input until ctrl + d is pressed 
// that match any of the given search term
// Written by Sofia De Bellis, z5418801, on July 2023

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

#define MAX_WORD_LEN 128

int main(void) {
    int num_search_terms = 0;
    printf("Enter the search term: ");
    char search_term[MAX_WORD_LEN];

    fgets(search_term, MAX_WORD_LEN, stdin);

    printf("Enter the list of strings:\n");

    int total = 0;

    char buf[MAX_WORD_LEN];
    while (fgets(buf, MAX_WORD_LEN, stdin) != NULL) {
        if (strcmp(buf, search_term) == 0) {
            total++;
        }
    }
    printf("There was %d occurrence(s) of the search term in the input.\n",
    total);

    return 0;
}

Exercise
(●●◌)
:

Summation

Write a C program summation.c that takes integers as command line arguments and calculates their sum.

Examples

dcc summation.c -o summation
./summation 1 2 3 4 5 6 7 8 9
Sum: 45
./summation -5 10 -15 20
Sum: 10
./summation 100
Sum: 100
./summation
Sum: 0

Assumptions/Restrictions/Clarifications

  • You may find the atoi() function in the C standard library (stdlib.h) useful.
  • You may assume that argv[0] will always be the program name and all subsequent elements will be integers.
You can run an automated code style checker using the following command:
1511 style summation.c

When you think your program is working, you can use autotest to run some simple automated tests:

1511 autotest summation

When you are finished working on this exercise, you must submit your work by running give:

give cs1511 lab07_summation summation.c

You must run give before Monday 28 October 20:00 to obtain the marks for this lab exercise. Note that this is an individual exercise, the work you submit with give must be entirely your own.

Sample solution for summation.c
// summation.c
//
// This program calculates and outputs the sum of all the integers provided as
// command line arguments.
//
// Written by Sofia De Bellis, z5418801, July 2023

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[]) {
    int sum = 0;

    // Start from index 1 to skip the program name (argv[0])
    for (int i = 1; i < argc; i++) {
        // Convert each command line argument to an integer using atoi() and
        // add the current number to the sum
        sum += atoi(argv[i]);
    }

    printf("Sum: %d\n", sum);

    return 0;
}

Exercise
(●●◌)
:

Stellar Information Capture

Download stellar_information.c here

Or, copy these file(s) to your CSE account using the following command:

1511 fetch-activity stellar_information

Write a C program stellar_information.c that simulates a star system in space. Each star in the system is represented by a structure containing its name, distance from Earth (in light-years), and spectral type. Your task is to initialize a pointer to a star structure, write a function to populate its fields, a function to calculate an estimate for the time it would take to travel from earth to the star and a function to print the star's information and the estimate for the time it would take to travel from earth to the star.

Task 1

Define a structure named star with the following fields:

  1. name: a string of size 50 to store the star's name.
  2. distance: a double to store the star's distance from Earth in light-years.
  3. spectral_type: a character to store the star's spectral type (e.g., O, B, A, F, G, K, M).

Task 2

Declare a pointer star_ptr to a star structure.

Task 3

Write the function called input_star_information() that prompts the user to enter the star's name, distance from Earth, and spectral type. Then store the input values in the respective fields of the structure pointed to by star_ptr.

Task 4

Write the function called time_travel() that, given a pointer to a star, calculates and returns the estimated travel time from Earth to the star based on star's distance. This calculation involves the following steps:

  1. Converting the stars distance from light-years to kilometers by multiplying the distance by the CONVERSION_CONSTANT that is defined at the top of your program.
  2. Dividing that previously calculated value by the LIGHT_SPEED constant which is defined at the top of your program.

Task 5

Write the function called print_star_information() that, given a pointer to a star, prints the star's information (name, distance, spectral type, and time travel).

Examples

dcc stellar_information.c -o stellar_information
./stellar_information
Enter the star's name: Sirius
Enter the star's distance from Earth (in light-years): 8.6
Enter the star's spectral type: A

Star's Information:
Name: Sirius
Distance: 8.600000 light-years
Spectral Type: A
Estimated travel time from Earth: 271403091.80 seconds
./stellar_information
Enter the star's name: Polaris
Enter the star's distance from Earth (in light-years): 323
Enter the star's spectral type: F

Star's Information:
Name: Polaris
Distance: 323.000000 light-years
Spectral Type: F
Estimated travel time from Earth: 10193395192.08 seconds
./stellar_information
Enter the star's name: Rigel
Enter the star's distance from Earth (in light-years): 864.3
Enter the star's spectral type: B

Star's Information:
Name: Rigel
Distance: 864.300000 light-years
Spectral Type: B
Estimated travel time from Earth: 27276010726.06 seconds

Assumptions/Restrictions/Clarifications

  • You may assume that valid input will always be given
  • You may find using %.2lf useful for printing the travel time value to two decimal places
You can run an automated code style checker using the following command:
1511 style stellar_information.c

When you think your program is working, you can use autotest to run some simple automated tests:

1511 autotest stellar_information

When you are finished working on this exercise, you must submit your work by running give:

give cs1511 lab07_stellar_information stellar_information.c

You must run give before Monday 28 October 20:00 to obtain the marks for this lab exercise. Note that this is an individual exercise, the work you submit with give must be entirely your own.

Sample solution for stellar_information.c
// stellar_information.c
// This program simulates a star system in space
// Written by Sofia De Bellis, z5418801, July 2023

#include <stdio.h>
#include <stdlib.h>

#define CONVERSION_CONSTANT 9.461e12
#define LIGHT_SPEED 299792.458

struct star {
    char name[50];
    double distance;
    char spectral_type;
};

void print_star_information(struct star* star);
void input_star_information(struct star* star);
double time_travel(struct star* star);

int main(void) {
    struct star star;
    struct star *star_ptr = &star;
    input_star_information(star_ptr);
    print_star_information(star_ptr);
    return 0;
}

// Takes in the stars information
void input_star_information(struct star* star) {
    printf("Enter the star's name: ");
    fgets(star->name, 50, stdin);

    printf("Enter the star's distance from Earth (in light-years): ");
    scanf("%lf", &star->distance);

    printf("Enter the star's spectral type: ");
    scanf(" %c", &star->spectral_type);
}

// Prints the stars information
void print_star_information(struct star* star) {
    printf("\nStar's Information:\n");
    printf("Name: %s", star->name);
    printf("Distance: %lf light-years\n", star->distance);
    printf("Spectral Type: %c\n", star->spectral_type);
    printf("Estimated travel time from Earth: %.2lf seconds\n", time_travel(star));
}

// Estimate travel time from Earth to the star based on star's distance
double time_travel(struct star* star) {
    double distance_km = star->distance * CONVERSION_CONSTANT; 
    double travel_time = distance_km / LIGHT_SPEED; 
    return travel_time;
}

Exercise
(●●◌)
:

Encrypting Text with a Substitution Cipher

Write a C program substitution.c which reads characters from its input and writes the characters to its output encrypted with a Substitution cipher.

A Substitution cipher maps each letter to another letter.

The mapping will be given to your program via the command line as a command line argument (no spaces between any characters). This is all on one line. This input will contain 26 characters: an ordering of the letters 'a'..'z'.

Characters other than letters should not be encrypted.

Your program should stop only at the end of input.

Your program should contain at least one function other than main.

Examples

dcc substitution.c -o substitution
./substitution qwertyuiopasdfghjklzxcvbnm
Enter text:
I was scared of dentists and the dark
O vql leqktr gy rtfzolzl qfr zit rqka
I was scared of pretty girls and starting conversations
O vql leqktr gy hktzzn uoksl qfr lzqkzofu egfctklqzogfl

./substitution abcdefghijklmnopqrstuvwxyz
Enter text:
The identity cipher!!!
The identity cipher!!!

./substitution bcdefghijklmnopqrstuvwxyza
Enter text:
The Caesar cipher is a subset of the substitution cipher!
Uif Dbftbs djqifs jt b tvctfu pg uif tvctujuvujpo djqifs!

Your program will only be tested with an appropriate and valid mapping input for marking - but a good programmer would check the input is present and appropriate.

You can run an automated code style checker using the following command:
1511 style substitution.c

When you think your program is working, you can use autotest to run some simple automated tests:

1511 autotest substitution

When you are finished working on this exercise, you and your lab partner must both submit your work by running give:

give cs1511 lab07_substitution substitution.c

Note, even though this is a pair exercise, you both must run give from your own account before Monday 28 October 20:00 to obtain the marks for this lab exercise.

Sample solution for substitution.c
// Substitution.c
//  Write stdin to stdout encrypted with a Substitution cipher
//  https://en.wikipedia.org/wiki/Substitution_cipher
//
//  The mapping will be supplied as a command-line argument containing 26 characters:
//  These will be an an ordering of the letters 'a'..'z'.
//
// Written 3/3/2018 by Andrew Taylor (andrewt@unsw.edu.au)
// and adapted for 23T2 by Sofia De Bellis, z5418801 on July 2023

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

#define ALPHABET_SIZE  26

int encrypt(char character, char mapping[ALPHABET_SIZE]);

int main(int argc, char *argv[]) {
    char mapping[ALPHABET_SIZE + 1];
    for (int i = 0; i < ALPHABET_SIZE; i++) {
        mapping[i] = argv[1][i];
    }

    mapping[ALPHABET_SIZE] = '\0';
    

    // Error checking - not necessary in student solution or tested
    if (mapping[0] == '\0') {
        printf("Usage: ./substitution <mapping>\n");
        return 1;
    }

    if (strlen(mapping) != ALPHABET_SIZE) {
        printf("./substitution: mapping must contain %d letters\n", ALPHABET_SIZE);
        return 1;
    }

    printf("Enter text:\n");

    char character;
    while (scanf("%c", &character) == 1) {
        int encrypted_character = encrypt(character, mapping);
        printf("%c", encrypted_character);
    }

    return 0;
}

// encrypt letters with a substitution cipher with the specified mapping
int encrypt(char character, char mapping[ALPHABET_SIZE]) {
    if (character >= 'A' && character <= 'Z') {
        return mapping[character - 'A'] - 'a' + 'A';
    } else if (character >= 'a' && character <= 'z') {
        return mapping[character - 'a'];
    } else {
        return character;
    }
}

Exercise
(●●●)
:

Frequency Analysis

Write a C program frequency_analysis.c which reads characters from its input until end of input.

It should then print the occurrence frequency for each of the 26 letters 'a'..'z'.

The frequency should be printed as a decimal value and an absolute number in exactly the format below.

Note upper and lower case letters are counted together.

Examples

dcc frequency_analysis.c -o frequency_analysis
./frequency_analysis
Hello and goodbye.

'a' 0.066667 1
'b' 0.066667 1
'c' 0.000000 0
'd' 0.133333 2
'e' 0.133333 2
'f' 0.000000 0
'g' 0.066667 1
'h' 0.066667 1
'i' 0.000000 0
'j' 0.000000 0
'k' 0.000000 0
'l' 0.133333 2
'm' 0.000000 0
'n' 0.066667 1
'o' 0.200000 3
'p' 0.000000 0
'q' 0.000000 0
'r' 0.000000 0
's' 0.000000 0
't' 0.000000 0
'u' 0.000000 0
'v' 0.000000 0
'w' 0.000000 0
'x' 0.000000 0
'y' 0.066667 1
'z' 0.000000 0
./frequency_analysis
Hey! Hey! Hey!
I don't like walking around this old and empty house
So hold my hand, I'll walk with you my dear

'a' 0.072289 6
'b' 0.000000 0
'c' 0.000000 0
'd' 0.084337 7
'e' 0.084337 7
'f' 0.000000 0
'g' 0.012048 1
'h' 0.096386 8
'i' 0.072289 6
'j' 0.000000 0
'k' 0.036145 3
'l' 0.084337 7
'm' 0.036145 3
'n' 0.060241 5
'o' 0.084337 7
'p' 0.012048 1
'q' 0.000000 0
'r' 0.024096 2
's' 0.036145 3
't' 0.048193 4
'u' 0.036145 3
'v' 0.000000 0
'w' 0.036145 3
'x' 0.000000 0
'y' 0.084337 7
'z' 0.000000 0

Hint: use an array to store counts of each letter.

Hint: make sure you understand this example program which counts integers from the range 0..99.

Manually Cracking a Substitution Cipher

This English text was encrypted with a substitution cipher.

Di jd, vdl'ht xtqa dh O qn
Vdl rdlwk O'ss wdkith htqromu omkd ok
O fhdwqwsv xdm'k
Styk kd nv dxm rtzoetj
Wlk kiqk'j kit royythtmet om dlh dfomodmj

Vdl'ht q ndlkiyls
Kiqk qndlmkj ydh qmdkith xtta dm nv dxm
Mdx O'n q mdzts nqrt htjdlhetyls
O jkqhk q eiqom xoki nv kidluik

Kqsa oj eitqf, nv rqhsomu
Xitm vdl'ht yttsomu houik qk idnt
O xqmmq nqat vdl ndzt xoki edmyortmet
O xqmmq wt xoki vdl qsdmt

What was the original text?

Mapping: qwertyuiopasnmdfghjklzxcvb

Oh so, you're weak or I am
You doubt I'll bother reading into it
I probably won't
Left to my own devices
But that's the difference in our opinions

You're a mouthful
That amounts for another week on my own
Now I'm a novel made resourceful
I start a chain with my thought

Talk is cheap, my darling
When you're feeling right at home
I wanna make you move with confidence
I wanna be with you alone
You can run an automated code style checker using the following command:
1511 style frequency_analysis.c

When you think your program is working, you can use autotest to run some simple automated tests:

1511 autotest frequency_analysis

When you are finished working on this exercise, you must submit your work by running give:

give cs1511 lab07_frequency_analysis frequency_analysis.c

You must run give before Monday 28 October 20:00 to obtain the marks for this lab exercise. Note that this is an individual exercise, the work you submit with give must be entirely your own.

Sample solution for frequency_analysis.c
//  Written 3/3/2018 by Andrew Taylor (andrewt@unsw.edu.au)
//  Read characters from stdin until of input then print the frequency of letters

#include <stdio.h>

#define NOT_A_LETTER   (-1)
#define ALPHABET_SIZE  26

int get_letter_frequencies(int letter_count[ALPHABET_SIZE]);
int letter_index(int character);
void print_frequencies(int letter_count[ALPHABET_SIZE], int n_letters_read);

int main(int argc, char *argv[]) {
    int letter_count[ALPHABET_SIZE] = {0};  // 1 array element for each English letter

    int n_letters_read = get_letter_frequencies(letter_count);
    print_frequencies(letter_count, n_letters_read);
    return 0;
}

// read  characters from stdin, and for uppercase and lower case letters updating
// accumulating count a letter_count in letter_frequencies
// number of uppercase and lower case letters read is returned

int get_letter_frequencies(int letter_count[ALPHABET_SIZE]) {
    char character;
    int n_letters_read = 0;
    while (scanf(" %c", &character) == 1) {
        int index =  letter_index(character);
        if (index != NOT_A_LETTER) {
            letter_count[index] = letter_count[index] + 1;
            n_letters_read = n_letters_read + 1;
        }
    }
    return n_letters_read;
}

// return position of letter in English  alphabet (0..25)
// for lower case and upper case letter
// return NOT_A_LETTER for other characters

int letter_index(int character) {
    if (character >= 'A' && character <= 'Z') {
        return character - 'A';
    } else if (character >= 'a' && character <= 'z') {
        return character - 'a';
    } else {
        return NOT_A_LETTER;
    }
}

void print_frequencies(int letter_count[ALPHABET_SIZE], int n_letters_read) {
    if (n_letters_read == 0) {
        return;
    }
    int i = 0;
    while (i < ALPHABET_SIZE) {
        printf("'%c' %lf %d\n", 'a'+i, letter_count[i]/(double)n_letters_read,  letter_count[i]);
        i = i + 1;
    }
}

Exercise
(☠)
:

Solving a 3D word search

Write a C program word_search.c which takes in as input from terminal:

  • A number n followed by any number of words
  • An n x n x n cube of characters

Note

This exercise requires the use of 3D arrays.

3D arrays are outside the scope of this course. The use of them in this exercise is simply to allow those that are interested to use/explore them.

Reminder: This exercise is not worth any marks, so do not feel obliged to learn things that aren't in this course!

Before reading any further, it is important to note that you will be expected to use 3D arrays in this exercise (although it is possible without them). A small snippet on how to create a 3d array is shown below.

#include <stdio.h>

#define X_SIZE 5
#define Y_SIZE 3
#define Z_SIZE 7

int main(void) {
    // Declaring a 3D array of integers and initialising all elements to 0
    int cube[X_SIZE][Y_SIZE][Z_SIZE] = {0};

    // Filling element at index (2, 1, 5) with value '6'
    cube[2][1][5] = 6;

    // Printing this value
    printf("%d", cube[2][1][6]);
}

A 3D array can be thought of in a couple of ways:

  • A Rectangular prism made up of small cubes (each array element)
  • An array of 2D arrays
  • An array of arrays of arrays

In this exercise, there is a cube which represents a 3D word-search grid. This cube is filled in by from values scanned in when the program is actually run. Your goal is to determine whether each of the provided words in the given input arguments can be found within this cube.

The rules for finding a word are as follows:

  • Words can only appear in straight lines in the cube
  • Words will only be going through one dimension of the cube, this means if a word is found through the x dimension, all characters in that word will have the same y and z positions
  • Words cannot exist as diagonals
  • Words will not appear backwards. This means that words can only be found when going through each dimension. In terms of how this would work with a 3D array, you will never need to go backwards from an index when searching for a word

Examples

dcc word_search.c -o word_search
./word_search
Please enter cube length: 2
Please enter grid 0:
oa
np
Please enter grid 1:
on
ai

on
on found!
an
an found!
po
po not found!
pi
pi found!
ai
ai found!
pa
pa not found!

./word_search
Please enter cube length: 3
Please enter grid 0:
daa
ood
gwd
Please enter grid 1:
opb
pda
pod
Please enter grid 2:
opo
edm
sup

dog
dog found!
doge
doge not found!
cat
cat not found!
ood
ood found!
add
add found!
friend
friend not found!
app
app found!
op
op found!
man
man not found!
bad
bad found!
long
long not found!
dam
dam found!
supercalifragilisticexpialidocious
supercalifragilisticexpialidocious not found!
low
low not found!
woo
woo not found!
moo
moo not found!
pod
pod found!
lip
lip not found!

Assumptions/Restrictions/Clarifications

  • You will always be given an integer as the first input
  • All inputs will be valid
  • The cube size that is input will be in the range [1, 32] inclusive
You can run an automated code style checker using the following command:
1511 style word_search.c

When you think your program is working, you can use autotest to run some simple automated tests:

1511 autotest word_search
Sample solution for word_search.c
// Program to take in a list of words then scan in a 3D cube of letters then
// print out the words that are found in this cube
// Written by Rory Golledge (z5308772) on October 2021

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

#define WORD_LEN 50
#define MAX_SIZE 32

#define TRUE 1
#define FALSE 0

void fill_cube(int size, int search_cube[size][size][size]);
int word_in_cube(char *word, int size, int search_cube[size][size][size]);
int find_word_from_index(
    char *word,
    int x, int y, int z,
    int size, int search_cube[size][size][size]
);
int word_at_index(
    char *word,
    int *x, int *y, int *z, int *target_dimension,
    int size, int search_cube[size][size][size]
);

int main(void) {
    int search_cube[MAX_SIZE][MAX_SIZE][MAX_SIZE] = {0};

    int size;
    printf("Please enter cube length: ");
    scanf(" %d", &size);

    fill_cube(size, search_cube);
    getchar();
    putchar('\n');

    char word[WORD_LEN];
    while (fgets(word, WORD_LEN, stdin) != NULL) {
        if (word[strlen(word) - 1] == '\n') {
            word[strlen(word) - 1] = '\0';
        }
        
        if (word_in_cube(word, size, search_cube)) {
            printf("%s found!\n", word);
        } else {
            printf("%s not found!\n", word);
        }
    }

    return 0;
}

/*
 * Fills the search cube with given input from the user
 */
void fill_cube(int size, int search_cube[size][size][size]) {
    int x = 0;
    while (x < size) {
        printf("Please enter grid %d:\n", x);
        int y = 0;
        while (y < size) {
            int z = 0;
            while (z < size) {
                search_cube[x][y][z] = getchar();
                // Ignore new lines
                if (search_cube[x][y][z] != '\n') {
                    z++;
                }
            }
            y++;
        }
        x++;
    }
}

/*
 * Determines if a given word is found in the search cube from the provided
 * rules.
 *
 * Returns TRUE if found, otherwise FALSE.
 */
int word_in_cube(char *word, int size, int search_cube[size][size][size]) {
    int x = 0;
    // This first loop can be thought of as holding each yz plane
    while (x < size) {
        int y = 0;
        // This second loop can be though of as holding each z strip
        while (y < size) {
            int z = 0;
            // And finally, this third loop is where all the magic happens as
            // each character can be indexed here.
            while (z < size) {
                if (find_word_from_index(word, x, y, z, size, search_cube)) {
                    return TRUE;
                }
                z++;
            }
            y++;
        }
        x++;
    }

    return FALSE;
}

/*
 * Finds the given word by using the index (x, y, z) as a starting point.
 * Returns TRUE if found, otherwise FALSE
 */
int find_word_from_index(
    char *word,
    int x, int y, int z,
    int size, int search_cube[size][size][size]
) {
    return word_at_index(word, &x, &y, &z, &x, size, search_cube) ||
           word_at_index(word, &x, &y, &z, &y, size, search_cube) ||
           word_at_index(word, &x, &y, &z, &z, size, search_cube);
}

/*
 * Determines if the given word is found from the current index (x, y, z) when
 * looking at the target_dimension.
 *
 * The 'target_dimension' is a pointer with equal value to either x, y or z.
 * This is simply incremented in a loop and by doing it this way, there is no
 * need to check for 3 things to determine what to increment.
 *
 * Returns TRUE if found, otherwise FALSE
 */
int word_at_index(
    char *word,
    int *x, int *y, int *z, int *target_dimension,
    int size, int search_cube[size][size][size]
) {
    int word_length = strlen(word);

    // Cannot physically find the word when it there is not enough space
    if (*target_dimension + word_length > size) {
        return FALSE;
    }

    // Just store what the original target was to maintain state
    int target = *target_dimension;

    int i = 0;
    while (word[i] != '\0' && search_cube[*x][*y][*z] == word[i]) {
        (*target_dimension)++;
        i++;
    }

    *target_dimension = target;

    if (i == word_length) {
        return TRUE;
    }
    return FALSE;
}

Exercise — individual:
(Not For Marks) Debugging - concatenate

Download debug_concatenate.c here

Or, copy these file(s) to your CSE account using the following command:

1511 fetch-activity debug_concatenate

Note that this exercise is not marked or worth marks!

Debugging Tips!

Some debugging tips for you:

  • dcc output - as you run into issues, dcc will point you to where the errors are. Remember that dcc gives you the line number the issue is on, and will give some sort of explanation. Make sure you read everything dcc gives you. Sometimes we get “errors carried forward”, so find your first error, fix that, then recompile.
  • print statements - sometimes it can be handy to see if the flow of your code puts you in the spot you expect it to be (ie. inside the right if statement, or going through a loop the correct amount of times). A quick way you can check this is by putting print statements in your code for testing purposes, like "the value of x is %d and y is %d". This lets you check that you got against what you expected.
  • COMP1511 debugging guide

The Task

This exercise concatenate the strings passed as command-line arguments excluding the filename. Currently it has some issues - it is your job to figure them out and fix the code. Additionally, think of ways you could refactor the starter code to produce a simpler solution.

Examples

dcc debug_concatenate.c -o debug_concatenate
./debug_concatenate A blue whales heartbeat can be heard over 2 miles away
Concatenated string: Abluewhalesheartbeatcanbeheardover2milesaway
./debug_concatenate The moon has moonquakes
Concatenated string: Themoonhasmoonquakes
./debug_concatenate Pigs cannot look up into the sky
Concatenated string: Pigscannotlookupintothesky
./debug_concatenate
Concatenated string: 

Clarifications

  • The maximum length of the resulting string is 1028 characters, including the null terminator.

Walkthrough

Below is a video walkthrough of this exercise! Make sure to attempt it before watching this video

You can run an automated code style checker using the following command:
1511 style debug_concatenate.c

When you think your program is working, you can use autotest to run some simple automated tests:

1511 autotest debug_concatenate
Sample solution for debug_concatenate.c
// debug_concatenate.c
//
// This program given command line args produces a concatenated
// string of all command line args
//
// Written by Sofia De Bellis, z5418801 on July 2023

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

#define MAX_LEN 1028

int main(int argc, char *argv[]) {
    
    int i;
    int total_length = 0;

    for (i = 1; i < argc; i++) {
        total_length += strnlen(argv[i], MAX_LEN);
    }

    char result[MAX_LEN] = "";
    result[total_length + 1] = '\0';

    for (i = 1; i < argc; i++) {
        strncat(result, argv[i], MAX_LEN - 1);
    }

    printf("Concatenated string: %s\n", result);

    
    return 0;
}

Submission

When you are finished each exercises make sure you submit your work by running give.

You can run give multiple times. Only your last submission will be marked.

Don't submit any exercises you haven't attempted.

If you are working at home, you may find it more convenient to upload your work via give's web interface.

Remember you have until Week 8 Monday 20:00 to submit your work.

You cannot obtain marks by e-mailing your code to tutors or lecturers.

You check the files you have submitted here.

Automarking will be run by the lecturer several days after the submission deadline, using test cases different to those autotest runs for you. (Hint: do your own testing as well as running autotest.)

After automarking is run by the lecturer you can view your results here. The resulting mark will also be available via give's web interface.