CS100 · Fall 2026 · Programming Assignment

HW3: Palindrome, Strings and Memory

C17 · Online questions and Git submission on Gradescope

Released · 发布

Thu, Oct 23 · 12:00

⏰ Deadline · 截止

Tue, Nov 4 · 23:59

Download starter (ZIP)

Overview

HW3 consists of four problems on two Gradescope assignments. Problem 1 is a set of online objective questions; Problems 2–4 are programming problems.

ProblemTopicGradescope assignmentPoints
Problem 1Basic knowledge III (C: functions, strings, dynamic memory)HW3_p1 (online questions)100
Problem 2PalindromeHW3_programming100
Problem 3String and character manipulationHW3_programming100
Problem 4Memory Leak CheckerHW3_programming100

Problem 1 lives on the online assignment HW3_p1. The full statements of the three programming problems are below. Read the Submission section carefully before your first upload.

P1

Basic knowledge III

100 pts · HW3_p1

Problem 1 is a set of online questions on the Gradescope assignment HW3_p1: single-choice and multiple-choice questions about functions, pointers to functions, strings, and dynamic memory in C.

Answer the questions directly on Gradescope. Each question is graded all-or-nothing (a multiple-select question is correct only if your selection matches exactly). Your total score is visible immediately after submission, and you may resubmit as many times as you like until the deadline; the latest answers count.

P2

Palindrome

100 pts · HW3_programming

A string s0s1⋯sm−1 is said to be a palindrome if si = sm−1−i holds for every i ∈ {0, 1, ⋯, m−1}.

Read n strings and test whether each of them is a palindrome.

Input format

On the first line, a nonnegative integer n.

Then 2n lines follow. The (2i−1)-th line is a positive integer representing the length of the i-th string. The 2i-th line is the i-th string, which is guaranteed to contain no whitespace characters.

Output format

n lines. For every i ∈ {1, ⋯, n}, print Yes on the i-th line if the i-th string is a palindrome; print No otherwise.

Example

Input

5
1
a
4
(())
2
[[
5
abcba
8
ab0220ba

Output

Yes
No
Yes
Yes
Yes

Notes

There are no guarantees on the maximum possible value of n or the length of the strings. Dynamic memory should be used to store each input string.

P3

String and character manipulation

100 pts · HW3_programming

It is often a good exercise to implement your own versions of some standard library functions. In this task, you need to implement some standard library functions related to character and string manipulations.

The functions you need to implement are listed below.

int hw3_islower(int ch);
int hw3_isupper(int ch);
int hw3_isalpha(int ch);
int hw3_isdigit(int ch);
int hw3_tolower(int ch);
int hw3_toupper(int ch);

size_t hw3_strlen(const char *str);
char *hw3_strchr(const char *str, int ch);
char *hw3_strcpy(char *dest, const char *src);
char *hw3_strcat(char *dest, const char *src);
int hw3_strcmp(const char *lhs, const char *rhs);

Your implementations should conform to the C standard. The standard documentations and examples on these functions can be found at cppreference. Do not trust materials from any other sources like Wikipedia, Baidu Zhidao, CSDN, Zhihu, etc.

Notes

Keep it simple

Make your implementation as simple as possible:

Write the six functions only — no main. The autograder compiles your p3.c together with its own test driver. Violations that the OJ cannot detect (e.g. calling the standard library versions directly) are checked manually afterwards and will cost you all points for this problem.

P4

Memory Leak Checker

100 pts · HW3_programming

Background

Making a C program memory-safe is hard: every memory access must be valid, and all dynamically allocated memory must be correctly freed. Tools such as AddressSanitizer, UndefinedBehaviorSanitizer and Valgrind exist to catch these bugs automatically. In this problem you will write a very simple such tool yourself.

Description

Write a tool that detects memory leaks and invalid calls to free. A memory leak happens when a block allocated on the heap (by malloc or calloc) is never freed. A call to free(ptr) is valid if and only if

Before starting, read the cppreference pages on malloc, calloc and free (ignore the thread-safety parts). Do not trust materials from other sources. You do not need to handle realloc or aligned_alloc.

Main ideas

Recording file and line

It helps to record not only the address but also the source file and line number, so an error message can look like

100 bytes memory not freed (allocated in file hw3_problem1.c, line 32)

or

Invalid free in file hw3_problem1.c, line 49

So each array element should be a struct:

#define MAX_RECORDS 1000

struct Record {
  void *ptr;              // address of the allocated memory
  size_t size;            // size of the allocated memory
  int line_no;            // line number of the malloc/calloc call
  const char *file_name;  // file in which the call happens
};

struct Record records[MAX_RECORDS];

Pay attention to the type of size: on 64-bit machines size_t may be 64-bit, and a block can exceed the range of 32-bit integer types. The file name and line number come from the predefined macros __FILE__ and __LINE__ (see predefined macros).

The magic: intercepting the calls

Use #define to replace calls to malloc, calloc and free so they are captured by your own functions:

void *recorded_malloc(size_t size, int line, const char *file) {
  void *ptr = malloc(size);
  if (ptr != NULL) {
    // record this allocation
  }
  return ptr;
}

void *recorded_calloc(size_t cnt, size_t each_size, int line, const char *file) {
  void *ptr = calloc(cnt, each_size);
  if (ptr != NULL) {
    // record this allocation
  }
  return ptr;
}

void recorded_free(void *ptr, int line, const char *file) {
  // check validity, then free or report
}

#define malloc(SIZE) recorded_malloc(SIZE, __LINE__, __FILE__)
#define calloc(CNT, EACH_SIZE) recorded_calloc(CNT, EACH_SIZE, __LINE__, __FILE__)
#define free(PTR) recorded_free(PTR, __LINE__, __FILE__)

Leak checking at exit

To run a check when the program terminates, declare a function with __attribute__((destructor)):

void check_leak(void) __attribute__((destructor));

void check_leak(void) {
  // walk the array; anything still non-NULL is leaked
}

Even with an empty main, such a program will run check_leak on exit.

Usage

Place your code in p4.c as a header-style file with include guards (the course driver #includes it, like memcheck.h above):

// p4.c
#ifndef CS100_MEMCHECK_H
#define CS100_MEMCHECK_H 1

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

#define MAX_RECORDS 1000

struct Record { /* ... */ };

/* ... your recorded_* functions and check_leak ... */

#define malloc(SIZE) recorded_malloc(SIZE, __LINE__, __FILE__)
#define calloc(CNT, EACH_SIZE) recorded_calloc(CNT, EACH_SIZE, __LINE__, __FILE__)
#define free(PTR) recorded_free(PTR, __LINE__, __FILE__)

#endif // CS100_MEMCHECK_H

Output format

When the program terminates, print a summary. First a line Summary:, then for every leaked block a line

<size> bytes memory not freed (allocated in file <file>, line <line>)

and finally a closing line — either All allocations have been freed. or <k> allocation records not freed (<bytes> bytes in total).. An invalid free prints Invalid free in file <file>, line <line> at the point where it happens.

No main in p4.c. The autograder compiles a course driver that #includes your file and runs a set of test programs through it; your macros redirect every malloc/calloc/free in those programs to your record

Submission

Starter package: download HW3_student_starter.zip — the three source files (already correctly named) and a README that walks you through the compile commands. Fill in your code and submit the files as-is.

Problem 1: answer the online questions on the Gradescope assignment HW3_p1. You may update your answers any number of times before the deadline.

Problems 2–4: submit to the Gradescope assignment HW3_programming. Write one C source file per problem, named exactly p2.c, p3.c and p4.c. Submission is via a GitHub repository only (no file upload): create a private repository with the three files at the top level, then on Gradescope choose the GitHub submission method, select your repository and branch. Each problem is graded independently, and the three problems show up as three rows of 100 points each.

The autograder builds your code with the course's own trusted commands — you do not need to write a Makefile. P2 is compiled as a standalone program; P3 is compiled together with the course's test driver (so your p3.c must contain only the hw3_* functions, no main); P4 is #included by the course's driver (header-style, no main).

Compilation

You compile each problem by typing a gcc command yourself in the terminal (WSL). The command has the same shape for all problems; only the file name changes. The minimal form is:

gcc p2.c -o p2

That builds and runs, but we recommend the advanced form — it turns on the C17 standard, all the useful warnings, and the sanitizers that catch memory errors and undefined behaviour while you test:

gcc p2.c -o p2 -std=c17 -Wall -Wpedantic -Wextra -Werror -fsanitize=undefined -fsanitize=address

Type it out yourself (muscle memory beats copy-paste), and replace both p2's with the problem you are building. P3 and P4 have no main, so to test them locally, write a separate small test.c with a main that calls your code, and compile them together: gcc p3.c test.c -o test -std=c17 ... (likewise for p4.c).

What the autograder requires

On Gradescope your code is built by the course's trusted commands (any Makefile you submit is ignored) with -std=c17. P3 and P4 are compiled with the course drivers described in their statements. There is no -Werror or sanitizer gate on the OJ side; we still recommend compiling locally with the advanced command above, because its warnings and sanitizers catch bugs before the test cases do.

Each problem's 100 points are split across its test cases, and a problem's score is the sum of the test cases you pass. A run is stopped if it exceeds its time limit, produces excessive output, or crashes.

Tip: press the up arrow key in the terminal to bring back the last command you typed — press it repeatedly to walk further back through your history. You will never have to retype a long gcc line.