Resource Cheatsheet
Code: Coding
Code ch. 27 — from machine code to high-level languages: assemblers, compilers vs interpreters, high-level constructs, libraries and APIs.
- Updated
- Reading time
- 3 min read
- Detail
- Code (2nd ed.)
- Detail
- Ch. 27
On this page
In one line: nobody writes ones and zeros — layers of translation, from assembler up to high-level languages, let humans express ideas that machines still run as machine code.
Companion to: Charles Petzold, Code (2nd edition), chapter 27. See also TryHackMe module 4 and TCM Help Desk section 3.
The ladder of languages
| Level | Example | Who reads it |
|---|---|---|
| Machine code | B8 01 00 00 00 |
The CPU |
| Assembly | MOV EAX, 1 |
Humans, one line ≈ one instruction |
| High-level | total = a + b |
Humans, one line ≈ many instructions |
- Assembler: translates assembly mnemonics into machine code, one-to-one.
- The leap to high-level languages (Fortran, C, then everything since) let programmers stop thinking about registers and write in terms of the problem.
Compiled vs interpreted
| Compiled | Interpreted | Bytecode / JIT | |
|---|---|---|---|
| When translated | Ahead of time, to machine code | Line by line, at run time | To bytecode, then to machine code while running |
| Speed | Fast | Slower | In between |
| Portability | One binary per OS + CPU | Runs anywhere the interpreter does | Runs anywhere the runtime does |
| Examples | C, C++, Go, Rust | Python, JavaScript, Bash, PowerShell | Java, C# (.NET) |
What high-level languages give you
| Construct | Purpose |
|---|---|
| Variables and types | Named, typed storage |
| Conditionals and loops | if / while / for — jumps and flags, made readable |
| Functions | Reusable subroutines (ch. 24) |
| Data structures | Arrays, lists, dictionaries, objects |
| Libraries / modules | Prewritten code you call |
| API | The published set of functions a library or service exposes |
Higher levels trade some control and speed for a lot of productivity — abstraction again.
Try it — one job, three languages
print(sum(range(1, 101))) # Python — interpreted/* C — compiled: gcc sum.c -o sum && ./sum */
#include <stdio.h>
int main(void){ int t=0; for(int i=1;i<=100;i++) t+=i; printf("%d\n", t); return 0; }seq 1 100 | paste -sd+ | bc # shell — glue existing tools together🔐 Security and IT connections
- The language shapes the bugs.
- C and C++ let you touch memory directly, so buffer overflows and use-after-free are theirs. This is why memory-safe languages (Rust, Go) are now recommended for new security-sensitive code.
- Interpreted languages avoid those but add injection risks (
eval,os.system) if input isn’t handled carefully.
- The supply chain is code you didn’t write. Modern programs pull in hundreds of library dependencies. A poisoned package (typosquatting, a hijacked maintainer account) runs with your program’s privileges. Pin versions, check signatures, and scan dependencies.
- Source vs binary. You run compiled binaries you can’t easily read — which is why signing, hashes and trusted sources matter, and why malware analysts disassemble.
Practice drills
1. What does an assembler translate, and at what ratio?
Assembly mnemonics into machine code, roughly one instruction per line.
2. Compiled vs interpreted — which is more portable and why?
Interpreted: the same source runs anywhere the interpreter is installed, with no per-platform binary.
3. Why are memory-safe languages recommended for new security-critical code?
They prevent whole classes of memory bugs (buffer overflows, use-after-free) that plague C and C++.
Key takeaways
- Machine code → assembly → high-level languages, each more human and further from the metal.
- Compiled is fast and platform-specific; interpreted is portable and slower; bytecode sits between.
- The language and its dependencies shape the security risks — from memory bugs to supply-chain attacks.