qfetch/qfetchC-notes.md
2026-01-20 09:18:07 -08:00

213 lines
3.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## The mindset shift: C vs C++
In C:
- ❌ No classes, no RAII, no `std::string`
-**You control memory explicitly**
- ✅ Functions are small, focused, and composable
- ✅ Portability comes from *conditional compilation*
- “Each function returns ownership of dynamically allocated data, and the caller frees it.”*
## Feature macros (`_POSIX_C_SOURCE`)
```c
#define _POSIX_C_SOURCE 200809L
```
### Why this exists
C standard libraries hide some functions unless you *opt in*.
Without it:
* Some systems wont expose those functions
* Youll get mysterious warnings or missing symbols
---
## Headers: what C teaches you here
```c
#include <stdio.h> // printf, FILE
#include <stdlib.h> // malloc, free
#include <string.h> // strlen, strdup
#include <unistd.h> // POSIX functions
```
In C:
* Headers are **contracts**
* If its not included, the compiler assumes *nothing*
**Rule of thumb:**
> If you call a function, its header must be included — no exceptions.
---
## Conditional compilation (`#if defined(...)`)
Example:
```c
#if defined(__APPLE__) || defined(__FreeBSD__)
#include <sys/sysctl.h>
#endif
```
### What this teaches you
* C has **no runtime reflection**
* Portability happens at **compile time**
* The preprocessor literally removes code before compilation
Think of it as:
```text
“Only compile this code if the OS supports it.”
```
This is how *real* portable C software works (git, curl, openssh).
---
## The most important function in the file
```c
static char *dup_or_unknown(const char *s)
```
### Why this exists
This function enforces a **contract**:
* Every getter:
* returns a valid `char *`
* never returns `NULL`
* always returns heap memory
### Why that matters
It lets `main()` be *simple*:
```c
char *user = get_user();
/* ... */
free(user);
```
No special cases.
No defensive `if (ptr)` checks.
---
## Dynamic allocation patterns (CRITICAL)
Example:
```c
char *buf = malloc(len);
snprintf(buf, len, "...", ...);
return buf;
```
### This is idiomatic C
Rules being followed:
1. Allocate **exactly what you need**
2. Initialize before use
3. Return ownership to the caller
4. Caller must `free()`
Contrast this with C++:
* No destructors
* No smart pointers
* No safety net
---
## Why no global buffers?
You *could* have done:
```c
static char buf[256];
```
But that would:
* Break thread safety
* Break reentrancy
* Make functions non-composable
Dynamic allocation makes your functions:
* Reusable
* Testable
* Library-quality
---
## Reading system information in C
Each platform teaches a lesson:
### Linux
```c
/proc/cpuinfo
/proc/uptime
```
* Text parsing
* Line-by-line scanning
* Defensive string handling
### macOS / BSD
```c
sysctl()
```
* Structured kernel APIs
* Buffer-size negotiation
* Integer & struct-based data
---
## Time handling (classic C pain point)
```c
time_t now = time(NULL);
difftime(now, boottime.tv_sec);
```
Why this matters:
* `time_t` may not be an integer
* You *never* subtract times directly
* `difftime()` handles portability
---
## The `main()` function: clean by design
```c
char *user = get_user();
/* ... */
free(user);
```
Notice:
* No logic
* No parsing
* No platform checks
* No error handling clutter
All complexity lives *outside* `main()`.