\n");
return 1;
}
printf("Hello, %s!\n", argv[1]);
return 0;
}
```
```bash
$ ./hello World
Hello, World!
$ ./hello CS315
Hello, CS315!
```
---
## One-Step vs. Two-Step Compilation
**One step:**
```text
hello.c
|
| gcc -o hello hello.c
v
hello (executable)
```
**Two steps:**
```text
hello.c
|
| gcc -c hello.c
v
hello.o (object file)
|
| gcc -o hello hello.o
v
hello (executable)
```
---
## The Compile-and-Link Pipeline
flowchart TD
subgraph compile["Compile (per source file)"]
A[hello.c] -->|gcc -c| B[hello.o]
end
subgraph link["Link (combine objects)"]
B -->|gcc -o hello| C[hello executable]
D[C runtime startup] --> C
end
---
## Why Two Steps?
| Benefit | Explanation |
|---------|-------------|
| **Faster rebuilds** | Only recompile changed files; reuse unchanged `.o` files |
| **Code sharing** | A helper `.o` can link into several programs |
The object file (.o) contains machine code but is not runnable — it still needs the C runtime and unresolved symbols. Linking resolves these.
- `-c` flag: compile only, produce `.o`
- `-o` flag: name the output file
---
## The Lab 01 Makefile
```makefile
.PHONY: all clean
CC := gcc
CFLAGS := -g -O2 -Wall -Wextra
LDFLAGS := -g
PROGS := hello argslens
HELLO_OBJS := hello.o
ARGSLENS_OBJS := argslens.o
%.o: %.c
${CC} -c ${CFLAGS} -o $@ $<
all: ${PROGS}
hello: ${HELLO_OBJS}
${CC} ${LDFLAGS} -o $@ $^
argslens: ${ARGSLENS_OBJS}
${CC} ${LDFLAGS} -o $@ $^
clean:
rm -rf ${PROGS} ${OBJS}
```
---
## Makefile Mental Model
- **Target**: file to build (e.g., `hello`)
- **Prerequisites**: files it depends on (e.g., `hello.o`)
- **Recipe**: shell commands to build it
- `make` rebuilds only if a prerequisite is **newer** than the target
```bash
$ make # build everything
$ make clean # delete executables and .o files
$ make -n # dry run: print commands without running
```
---
## Makefile Automatic Variables
| Variable | Meaning | Example: `hello: hello.o` |
|----------|---------|---------------------------|
| `$@` | The target being built | `hello` |
| `$<` | First prerequisite | `hello.o` |
| `$^` | All prerequisites | `hello.o` |
The pattern rule `%.o: %.c` expands to:
```bash
gcc -c -g -O2 -Wall -Wextra -o hello.o hello.c
```
Tabs, not spaces! Recipe lines must start with a real tab. Spaces cause: *** missing separator. Stop.
---
## Adding a New Program to the Makefile
To add `foo`:
1. Write `foo.c`
2. Add `foo` to `PROGS`
3. Add `FOO_OBJS := foo.o`
4. Add a link rule:
```makefile
foo: ${FOO_OBJS}
${CC} ${LDFLAGS} -o $@ $^
```
The pattern rule `%.o: %.c` already handles compiling `foo.c` → `foo.o`.
---
## Command-Line Arguments: `argc` and `argv`
```c
int main(int argc, char *argv[])
```
- `argc` — **count** of command-line words, including the program name
- `argv` — **array of strings**; `argv[0]` is the program name
Running `./args foo cs315 computer architecture`:
```text
argc = 5
argv[0] = "./args"
argv[1] = "foo"
argv[2] = "cs315"
argv[3] = "computer"
argv[4] = "architecture"
argv[5] = NULL (array is always NULL-terminated)
```
---
## `argv` Memory Layout
```text
argv
+------+ +-----------------------+
| [0] | -----> | '.' '/' 'a' ... '\0' |
+------+ +-----------------------+
| [1] | -----> | 'f' 'o' 'o' '\0' |
+------+ +-----------------------+
| [2] | -----> | 'c' 's' '3' '1' '5' '\0' |
+------+ +-----------------------+
| [3] | -----> | 'c' 'o' 'm' ... '\0' |
+------+ +-----------------------+
| [4] | -----> NULL
+------+
```
Each element is a pointer to a NUL-terminated string.
---
## Printing All Arguments
```c
#include
int main(int argc, char *argv[]) {
for (int i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
```
```bash
$ ./args foo cs315
argv[0] = ./args
argv[1] = foo
argv[2] = cs315
```
Use i < argc, NOT i <= argc. Index argc is NULL — printing it with %s is undefined behavior (segfault).
---
## Checking Argument Count
Most programs require a specific number of arguments:
```c
#include
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("usage: ./hello \n");
return 1; // non-zero = error
}
printf("Hello, %s!\n", argv[1]);
return 0; // zero = success
}
```
- `argc == 2` means one user argument (plus the program name)
- The autograder and shell scripts rely on the exit status convention
---
## Converting Strings to Numbers with `atoi`
Everything in `argv` is a **string** — even numbers. `argv[1]` for `./repeat 3` is the string `"3"`, not the integer `3`.
```c
#include
#include // for atoi
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("usage: ./echorepeat \n");
return 1;
}
int count = atoi(argv[2]); // "3" -> 3
for (int i = 0; i < count; i++) {
printf("%s\n", argv[1]);
}
return 0;
}
```
```bash
$ ./echorepeat hi 3
hi
hi
hi
```
---
## `atoi` Notes
| Behavior | Detail |
|----------|--------|
| Header | `#include ` |
| Input | A numeric string like `"42"` |
| Returns | The integer value `42` |
| Bad input | Returns `0` for `"abc"`, no error reported |
| Alternative | `strtol` for robust validation |
atoi is fine for labs. For production code, use strtol which reports errors.
---
## Measuring String Length with `strlen`
`strlen` counts characters up to, but **not including**, the `'\0'`:
```c
#include
#include // for strlen
int main(int argc, char *argv[]) {
for (int i = 0; i < argc; i++) {
printf("argv[%d] = %s (%zu)\n", i, argv[i], strlen(argv[i]));
}
return 0;
}
```
```bash
$ ./argslens foo cs315
argv[0] = ./argslens (10)
argv[1] = foo (3)
argv[2] = cs315 (5)
```
---
## `strlen` Format Specifier
`strlen` returns `size_t` (unsigned). Two correct approaches:
```c
// Option A: correct specifier
printf("argv[%d] = %s (%zu)\n", i, argv[i], strlen(argv[i]));
// Option B: cast to int
printf("argv[%d] = %s (%d)\n", i, argv[i], (int)strlen(argv[i]));
```
Using %d without a cast triggers a compiler warning under -Wall -Wextra and may print wrong values. Silence the warning — the autograder expects clean, exact output.
---
## Installing the Autograder
```bash
$ ssh beagle
$ mkdir cs315 && cd cs315
$ git clone https://github.com/phpeterson-usf/autograder
```
Add to `~/.bash_profile`:
```bash
export PATH=~/cs315/autograder:$PATH
```
Log out and back in, then verify:
```bash
$ which grade
/home/you/cs315/autograder/grade
```
Also clone the tests repo: `https://github.com/USF-CS315-F25/tests`
---
## Running the Autograder
From inside your `lab01` directory:
```bash
$ grade test
. 01(25/25) 02(25/25) 03(25/25) 04(25/25) 100/100
```
flowchart LR
A[grade test] --> B[Build programs]
B --> C[Run with test inputs]
C --> D[Compare output to expected]
D --> E{Exact match?}
E -- yes --> F[Test passes]
E -- no --> G[Test fails / shows diff]
---
## Byte-Exact Output Matters
The autograder compares output **byte for byte**. These all fail:
| Your output | Expected | Problem |
|-------------|----------|---------|
| `Hello, World` | `Hello, World!` | Missing `!` |
| `Hello, World!` | `Hello, World!` | Extra space |
| `hello, World!` | `Hello, World!` | Wrong case |
Match the spec's sample output exactly — every character, space, and newline counts.
---
## Submitting with Git
```bash
$ git add Makefile hello.c argslens.c
$ git commit -m "Lab01: hello and argslens"
$ git push
```
| Submit | Do NOT submit |
|--------|---------------|
| `Makefile` | executables (`hello`, `argslens`) |
| `hello.c` | object files (`*.o`) |
| `argslens.c` | `a.out` |
Run `make clean` before committing so you don't accidentally add binaries.
---
## Common Mistakes
| Symptom | Cause | Fix |
|---------|-------|-----|
| `command not found: hello` | No `./` prefix | Use `./hello` |
| `*** missing separator. Stop.` | Spaces in recipe | Use real **tab** |
| `undefined reference to 'main'` | Running a `.o` directly | Link: `gcc -o hello hello.o` |
| `implicit declaration of 'strlen'` | Missing header | `#include ` |
| `implicit declaration of 'atoi'` | Missing header | `#include ` |
| Autograder fails "correct" output | Not byte-exact | Match spec character for character |
| Segfault in argument loop | `i <= argc` hits NULL | Use `i < argc` |
| `which grade` finds nothing | PATH not updated | Edit `~/.bash_profile`, re-login |
---
## Key Concepts Reference
| Concept | Meaning |
|---------|---------|
| `argc` | Count of args including program name |
| `argv[0]` | Program name; `argv[1]`... are user args |
| `gcc -c` | Compile only → `.o` (no link) |
| `gcc -o` | Name the output file |
| `.o` file | Machine code, not yet runnable |
| `strlen("foo")` | Returns `3` (excludes `'\0'`) |
| `atoi("42")` | Returns integer `42` |
| `$@ / $< / $^` | Make vars: target / first prereq / all prereqs |
| `.PHONY` | Target that is not a real file |
| Exit `0` / non-zero | Success / error |
---
## Summary
1. **`gcc -o hello hello.c`** compiles and links in one step; use `./hello` to run
2. **Two-step pipeline**: `gcc -c` produces `.o` (machine code, not runnable); linking adds runtime and resolves symbols
3. **`make` automates** the pipeline using pattern rules, automatic variables, and timestamp checks — recipe lines need **tabs**
4. **`main(int argc, char *argv[])`** — `argc` counts words (including program name); `argv` is an array of strings with `argv[argc] == NULL`
5. **Arguments are always strings** — use `atoi()` for integers, `strlen()` with `%zu` for lengths
6. **`grade test`** compares output byte-for-byte — exact formatting is required to pass
7. **Submit source only**: `Makefile`, `hello.c`, `argslens.c`; run `make clean` before committing