CS 61C Spring 2024 | Lab 1: C

476 阅读5分钟

Exercise 1: Hello!

Edit ex1_hello.c with your editor of choice and make the program print out the string "Hello 61C" instead of "Hello World". Make sure to save your edited file, but do NOT recompile yet.

My answer:

#include <stdio.h>

int main() {
  printf("Hello 61C\n");

  return 0;
}

Run the executable with ./ex1_hello. You should still see Hello World, which is the old output.

gcc -o ex1_hello ex1_hello.c
./ex1_hello
Hello World

Now, recompile your program with gcc -o ex1_hello ex1_hello.c, and then run the executable with ./ex1_hello again. You should now see Hello 61C.

My answer:

gcc -o ex1_hello ex1_hello.c
./ex1_hello
Hello 61C

Exercise 2: Pointer Basics

Edit ex2_pointer_basics.c using your editor of choice and fill in the blanks.

My answer:

#include <stdio.h>

int main() {
  // Assign x (an integer) to 5
  int x = 5;

  // TODO: create a pointer to x
  // Hint: the first blank should be a variable type
  //       the second blank should be the address of x
  int * pointer_to_x = &x;

  // This line should print 5
  printf("%d\n", *pointer_to_x);

  return 0;
}

Compile and run the program and check that the output matches what you expect.

My answer:

$ gcc ex2_pointer_basics.c
$ ./a.out
5

Exercise 3: Pointers and Functions

Edit ex3_pointers_and_functions.c using your editor of choice and fill in the blanks.

My answer:

#include <stdio.h>

void add_one(int input) {
  input += 1;
}

void add_one_pointer(int* input) {
  // TODO: add one to the integer that input points to
  *input += 1;
}

int main() {
  // Assign x (an integer) to 5
  int x = 5;

  // Call add_one on x
  add_one(x);

  // This line should print 5
  // Why doesn't this work?
  printf("add_one: %d\n", x);

  // Let's try using add_one_pointer

  // TODO: use add_one_pointer to increment x
  // Hint: compare the type of x with the type of the argument
  //       for add_one_pointer
  add_one_pointer(&x);

  // This line should print 6
  printf("add_one_pointer: %d\n", x);

  return 0;
}

Compile and run the program and check that the output matches what you expect.

My answer:

$ gcc ex3_pointers_and_functions.c
$ ./a.out
add_one: 5
add_one_pointer: 6

Exercise 4: Arrays

Edit ex4_arrays_updated.c using your editor of choice and fill in the blanks.

My answer:

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

int main() {
  // Creates an integer with value 5
  // Note: int8_t is a numerical datatype that takes up 1 byte of memory
  int8_t x = 5;

  // TODO: allocate an int8_t array of size 4
  int8_t some_array[4];
  printf("address of the start of the array: %p\n", some_array);

  // TODO: compute the address of the element at index 2 (0-indexed)
  int8_t * ptr_to_idx_2 = some_array + 2;
  printf("address of index 2: %p\n", ptr_to_idx_2);

  // TODO: store the value 10 at index 2, using ptr_to_idx_2
  *ptr_to_idx_2 = 10;

  // TODO: print the value at index 2
  // Hint: this blank should be the same as the previous blank
  //       please don't hard code 10
  printf("value at index 2: %d\n", some_array[2]);

  return 0;
}

Compile and run the program and check that the output matches what you expect.

My answer:

$ gcc ex4_arrays_updated.c
$ ./a.out 
address of the start of the array: 0x7ffc1712eaa4
address of index 2: 0x7ffc1712eaa6
value at index 2: 10

Read the output of your program. Note that the relationship between the address of the start of the array and the address of index 2 (hint: they're two bytes apart).

Exercise 5: Pointer Arithmetic

Edit ex5_pointer_arithmetic_updated.c using your editor of choice and fill in the blanks.

My answer:

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

int main() {
  // Creates an integer with value 5
  // Note: int16_t is a numerical datatype that takes up 2 bytes of memory
  int16_t x = 5;

  // TODO: allocate an int16_t array of size 4
  int16_t some_array[4];
  printf("address of the start of the array: %p\n", some_array);

  // TODO: compute the address of the element at index 2 (0-indexed)
  int16_t * ptr_to_idx_2 = some_array + 2;
  printf("address of index 2: %p\n", ptr_to_idx_2);

  // TODO: store the value 10 at index 2, using ptr_to_idx_2
  *ptr_to_idx_2 = 10;

  // TODO: print the value at index 2
  // Hint: this blank should be the same as the previous blank
  //       please don't hard code 10
  printf("value at index 2: %d\n", some_array[2]);

  return 0;
}

Compile and run the program and check that the output matches what you expect.

My answer:

$ gcc ex5_pointer_arithmetic_updated.c && ./a.out 
address of the start of the array: 0x7ffd584ad740
address of index 2: 0x7ffd584ad744
value at index 2: 10

Read the output of your program. Note that the relationship between the address of the start of the array and the address of index 2 is different from the relationship in the previous exercise.

Exercise 6: Strings

Edit ex6_strings.c using your editor of choice and fill in the blanks.

My answer:

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

int main() {
  // TODO: allocate memory to store the string "hello"
  // Hint: how many bytes do we need to store this string?
  char hello_str[10];

  // TODO: store the characters one at a time
  // Hint: don't forget the null terminator
  // Note: we use single quotes for characters
  hello_str[0] = 'h';
  hello_str[1] = 'e';
  hello_str[2] = 'l';
  hello_str[3] = 'l';
  hello_str[4] = 'o';

  // TODO: store the null terminator
  hello_str[5] = '\0';

  // Prints hello_str
  printf("prints hello: %s\n", hello_str);

  // TODO: print the length of hello_str
  printf("length of hello: %lu\n", strlen(hello_str));

  // TODO: allocate memory to store the string "world"
  char world_str[10];

  // TODO: fill in the type
  // Note: this automatically stores the string "world" in static memory
  //       but static memory is immutable, so you may need to copy it
  //       to the stack or the heap
  char * static_world_str = "world";

  // TODO: use strcpy and static_world_str to store "world" into world_str
  // Hint: strcpy takes two arguments:
  //       first the destination, then the source
  strcpy(world_str, static_world_str);

  // Prints world_str
  printf("prints world: %s\n", world_str);

  // Prints the address of world_str
  printf("address of world_str: %p\n", world_str);

  // TODO: compute the address of the letter r using world_str
  char * ptr_to_r = &world_str[2];
  printf("address of 'r': %p\n", ptr_to_r);

  // TODO: allocate memory to store the string "hello world"
  char hello_world_str[20];

  // TODO: use strcpy and hello_str to store
  //       the string "hello" into hello_world_str
  strcpy(hello_world_str, hello_str);

  // TODO: store the space character in "hello world" at the correct index
  // Note: a space is not the same as null terminator
  //       a null terminator is represented by '\0'
  hello_world_str[5] = ' ';

  // TODO: use strcpy, pointer arithmetic, and world_str to store
  //       the string "world" into hello_world_str
  strcpy(hello_world_str + 6, world_str);

  // Prints hello_world_str
  printf("prints hello world: %s\n", hello_world_str);

  return 0;
}

Compile and run the program and check that the output matches what you expect.

My answer:

$ gcc ex6_strings.c && ./a.out                   
prints hello: hello
length of hello: 5
prints world: world
address of world_str: 0x7ffd6e7d2376
address of 'r': 0x7ffd6e7d2378
prints hello world: hello world

Exercise 7: Copying Strings

Edit ex7_strcpy.c using your editor of choice and fill in the blanks. When copying the string, use strcpy (documentation).

My answer:

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

int main() {
  // TODO: Allocate memory to store the string "hello"
  // You may use your solution from a previous exercise;
  char message[20];
  message[0] = 'h';
  message[1] = 'l';
  message[2] = 'e';
  message[3] = 'l';
  message[4] = 'o';
  message[5] = '\0';

  // Print out the value before we change message
  printf("Before copying: %s\n", message);

  // Creates another_string that contains a longer string
  char* long_message = "Here's a really long string";

  // TODO: Copy the string in long_message to message
  strcpy(message, long_message);

  // Print out the value after we change message
  printf("After copying: %s\n", message);

  return 0;
}

Compile and run the program. Notice that the program crashes, why?

My answer:

$ gcc ex7_strcpy.c && ./a.out 
ex7_strcpy.c: In function ‘main’:
ex7_strcpy.c:23:3: warning: ‘strcpy’ writing 28 bytes into a region of size 20 overflows the destination [-Wstringop-overflow=]
   23 |   strcpy(message, long_message);
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ex7_strcpy.c:8:8: note: destination object ‘message’ of size 20
    8 |   char message[20];
      |        ^~~~~~~
Before copying: hlelo
After copying: Here's a really long string
*** stack smashing detected ***: terminated
[1]    46324 IOT instruction (core dumped)  ./a.out
  • strcpy is an unsafe function. We only allocate enough space in message to store the message hello, but then we try to store a longer message. When this happens, we overflow the space we allocated for message, and crash the program!
  • To prevent this, we can use strncpy (documentation) so that we don't overflow the space.

Fix the program in ex7_strcpy_fixed.c so that it stores as many characters of longer_message as you can in message without changing the size of message.

My answer:

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

int main() {
  // TODO: Allocate memory to store the string "hello"
  // You may use your solution from a previous exercise;
  char message[20];
  message[0] = 'h';
  message[1] = 'l';
  message[2] = 'e';
  message[3] = 'l';
  message[4] = 'o';
  message[5] = '\0';

  // Print out the value before we change message
  printf("Before copying: %s\n", message);

  // Creates another_string that contains a longer string
  char* long_message = "Here's a really long string";

  // TODO: Copy the string in long_message to message
  strncpy(message, long_message, sizeof(message) / sizeof(char) - 1);

  // Print out the value after we change message
  printf("After copying: %s\n", message);

  return 0;
}

Compile and run the program again and check that the output matches what you expect.

My answer:

$ gcc ex7_strcpy_fixed.c && ./a.out 
Before copying: hlelo
After copying: Here's a really lon

Exercise 8: Structs

Edit ex8_structs.c using your editor of choice and fill in the blanks.

My answer:

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

// Structs allow you to hold data items of different types in a single variable
// Struct definitions can be used to declare a struct variable within your program
// Struct definitions are typically done outside of a function
struct Student {
    int id;
    char* name;
};

int main() {
  // TODO: declare a variable student of type struct Student
  // Note: this struct is stored on the stack
  struct Student student;

  // TODO: print out the size of a struct Student
  // While this may seem out of place now, it will be useful in the future!
  // Hint: there's an operator that can calculate this for you!
  printf("Size of a struct Student: %lu bytes\n", sizeof(struct Student));

  // TODO: set student's id field to 5
  // Hint: the dot notation accesses a struct's fields
  student.id = 5;

  // TODO: print out student's id field
  printf("Student's ID: %d\n", student.id);

  return 0;
}

Compile and run the program and check that the output matches what you expect.

$ gcc ex8_structs.c && ./a.out     
Size of a struct Student: 16 bytes
Student's ID: 5

Optional: typedefs

Sometimes, you may see a typedef when declaring a struct:

typedef struct {
    int id;
} Student;

In these cases, you may use Student as the type instead of struct Student.

My answer:

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

// Structs allow you to hold data items of different types in a single variable
// Struct definitions can be used to declare a struct variable within your program
// Struct definitions are typically done outside of a function
typedef struct {
    int id;
    char* name;
} Student;

int main() {
  // TODO: declare a variable student of type struct Student
  // Note: this struct is stored on the stack
  Student student;

  // TODO: print out the size of a struct Student
  // While this may seem out of place now, it will be useful in the future!
  // Hint: there's an operator that can calculate this for you!
  printf("Size of a struct Student: %lu bytes\n", sizeof(Student));

  // TODO: set student's id field to 5
  // Hint: the dot notation accesses a struct's fields
  student.id = 5;

  // TODO: print out student's id field
  printf("Student's ID: %d\n", student.id);

  return 0;
}