college/Spring-2024/CS-2124/Assignment-1
2024-05-12 01:18:21 -05:00
..
assets Rename Spring-2023 -> Spring-2024 2024-05-12 01:18:21 -05:00
src Rename Spring-2023 -> Spring-2024 2024-05-12 01:18:21 -05:00
.clang-format Rename Spring-2023 -> Spring-2024 2024-05-12 01:18:21 -05:00
.gitignore Rename Spring-2023 -> Spring-2024 2024-05-12 01:18:21 -05:00
Assignment.pdf Rename Spring-2023 -> Spring-2024 2024-05-12 01:18:21 -05:00
CMakeLists.txt Rename Spring-2023 -> Spring-2024 2024-05-12 01:18:21 -05:00
README.org Rename Spring-2023 -> Spring-2024 2024-05-12 01:18:21 -05:00

Assignment 1

ABC123: zfp106

Name: Price Hiller

Course: CS2124

Section: 0C3

Semester: Spring 2024

Running the Programs

  1. Install cmake version 3.25 or greater.
  2. Ensure you have recent version of make at the time of writing. This project successfully compiles with GNU make version 4.4.1.
  3. Go the directory with CMakeLists.txt and run cmake . to generate a Makefile.
  4. Run make all to compile all the programs.
  5. Go into the newly created bin directory where all the compiled programs will be output to.
  6. Run the programs in sequence, from C1 to C12 to ensure the file operations work as expected.

Program Outputs and Question Solutions (C's)

C1 (Creation of a new file)

/Price/college/media/branch/Development/Spring-2024/CS-2124/Assignment-1/assets/C1-output.png

C2 (Opening and closing an existing file)

/Price/college/media/branch/Development/Spring-2024/CS-2124/Assignment-1/assets/C2-output.png

C3 (Writing to a file)

/Price/college/media/branch/Development/Spring-2024/CS-2124/Assignment-1/assets/C3-output.png

C4 (Reading from file)

/Price/college/media/branch/Development/Spring-2024/CS-2124/Assignment-1/assets/C4-output.png

C5 (Moving to a specific location in a file (end of file))

/Price/college/media/branch/Development/Spring-2024/CS-2124/Assignment-1/assets/C5-output.png

C6 (Computes string's length and copies one string into another string)

/Price/college/media/branch/Development/Spring-2024/CS-2124/Assignment-1/assets/C6-output.png

C7 (Concatenates(joins) two strings)

/Price/college/media/branch/Development/Spring-2024/CS-2124/Assignment-1/assets/C7-output.png

C8 (Compares two strings)

/Price/college/media/branch/Development/Spring-2024/CS-2124/Assignment-1/assets/C8-output.png

C9 (Converts String to lowercase)

/Price/college/media/branch/Development/Spring-2024/CS-2124/Assignment-1/assets/C9-output.png

C10 (Converts string to uppercase)

/Price/college/media/branch/Development/Spring-2024/CS-2124/Assignment-1/assets/C10-output.png

C11 (Dynamic Memory with Malloc)

/Price/college/media/branch/Development/Spring-2024/CS-2124/Assignment-1/assets/C11-output.png

C12 (Dynamic Memory with Calloc)

/Price/college/media/branch/Development/Spring-2024/CS-2124/Assignment-1/assets/C12-output.png

C13 (Describe the difference between malloc and calloc)

malloc and calloc both return a pointer to allocated memory for some purpose. The major difference is that calloc, by default, will initialize all elements in that allocated memory to the value 0 whereas malloc does not modify anything in that block of memory before returning a pointer.

You can achieve a poor man's calloc by using malloc with memset to initialize the newly returned block of memory to 0 yourself. This will almost certainly be slower than most calloc usages as calloc "knows" the kernel will return something known as a virtual zero page and thus will be receiving (on most systems) a chunk of memory that has all its values initialized to 0 by default. Thus calloc can "know" when it needs to allocate those 0's itself or if it can skip the process and thereby be faster than malloc + memset.

C14 (Explain pointers and pointers to pointers using code)

Write code for pointer and use that code to explain pointers in your own words
#include <stdio.h>

int main() {
    char *str = "Hello";
    while (*str != *"\0") {
        printf("Pointer Location: %p, Remaining Letters: %s\n", str, str);
        str++;
    }
}

If you run the code above it will output something similar to the following:

Pointer Location: 0x402008, Remaining Letters: Hello
Pointer Location: 0x402009, Remaining Letters: ello
Pointer Location: 0x40200a, Remaining Letters: llo
Pointer Location: 0x40200b, Remaining Letters: lo
Pointer Location: 0x40200c, Remaining Letters: o

As you can see we're progressively incrementing the pointer location by one and outputting the remaining letters from the current pointer location to the end of the string until we hit the NULL character.

The line: char *str = "Hello"; is really a pointer to some location that starts with the letter H and ends with a NULL character, \0.

By incrementing the pointer by one, we're moving the location it's pointing at by one. After the first iteration through the while loop the *str pointer points at the letter e. After the second iteration, *str points at l, after the third iteration, l, and so on.

In reality a pointer is something that points to a place in memory. It doesn't contain a value itself, it merely points to where a value is stored. Hence why the output has the weird hex codes for Pointer Location like 0x402008. That hex code is a location in memory known as a memory address that *str, the pointer, points to.

Write a code for pointer to pointer and use that code to explain pointer to pointer in your own words

A pointer to pointer can be seen in the following code:

#include <stdio.h>

int main() {
    int some_integer = 106;
    int *pointer = &some_integer;
    int **pointer_to_pointer = &pointer;
    printf("Value: %d\nPointer: %p\nPointer to Pointer: %p\n", some_integer, pointer, pointer_to_pointer);
}

When we run the above it will output (pointer values may vary):

Value: 106
Pointer: 0x7ffc87ea34d4
Pointer to Pointer: 0x7ffc87ea34c8

Notice that the pointer variable has a different address it points to than pointer_to_pointer.

The pointer variable points to the memory location of some_integer and the pointer_to_pointer variable points to the memory location of where the pointer variable is stored.

With the above in mind, we can then think of a pointer to a pointer as a double indirection. See the following diagram for what this effectively "looks" like:

Pointer to Pointer ----> Pointer ----> Value

Thus, if we only had the variable pointer_to_pointer we could access the value all the pointers ultimately point to with a multiple dereference like so:

#include <stdio.h>

int main() {
    int some_integer = 106;
    int *pointer = &some_integer;
    int **pointer_to_pointer = &pointer;
    printf("Deref'd Value from Pointer to Pointer: %d\n", **pointer_to_pointer);
}

Which will output:

Deref'd Value from Pointer to Pointer: 106