Overview of Programming languages
Learning Objectives
- Programming languages
- Describe the differences between a high level and low level programming language
- Describe the differences between an interpreted and compiled language
- Describe the differences between a static and dynamically typed language
- Know that there are different programming paradigms such as imperative and functional
- Describe the different memory management techniques
- Be able to identify the the properties of a particular language such as rust.
Various Language Levels
-
Native code
- usually compiled output of a high-level language, directly executable on target processor
-
Assembler
- low-level but human readable language that targets processor
- pros: as fine control as in native code
- cons: not portable
-
High level languages
- various levels of closeness to the architecture: from C to Prolog
- efficiency:
- varies
- could optimize better
- pros:
- very portable
- easier to build large projects
- cons:
- some languages are resource–inefficient
Assembly Language Examples
ARM X86
. text section .text
.global _start global _start
_start: section .data
mov r0, #1 msg db 'Hello, world!',0xa
ldr r1, =message len equ 0xe
ldr r2, =len section .text
mov r7, #4 _start:
swi 0 mov edx,len ;message length
mov r7, #1 mov ecx,msg ;message to write
mov ebx,1 ;file descriptor (stdout)
.data. mov eax,4 ;system call number (sys_write)
message: int 0x80 ;call kernel
.asciz "hello world!\n" mov ebx,0 ;process' exit code
len = .-message. mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel - this interrupt won't return
Interpreted vs. compiled
Interpreted:
- An application (interpreter) reads commands one by one and executes them.
- One step process to run an application:
python hello.py
("Fully") Compiled:
- Translated to native code by compiler
- Usually more efficient
- Two steps to execute:
- Compile (Rust:
rustc hello.rs) - Run (Rust:
./hello)
- Compile (Rust:
Compiled to Intermediate Representation (IR):
- Example: Java
- Portable intermediate format
- Needs another application, Java virtual machine, that knows how to interpret it
- Example: Python
- Under some circumstances Python bytecode is created and cached in
__pycache__ - Python bytecode is platform independent and executed by the Python Virtual Machine
- Under some circumstances Python bytecode is created and cached in
Just-in-Time (JIT) compilation is an interesting wrinkle in that it can take interpreted and intermediate format languages and compile them down to machine code.
Type checking: static vs. dynamic
Dynamic (e.g., Python):
- checks if an object can be used for specific operation during runtime
- pros:
- don't have to specify the type of object
- procedures can work for various types
- faster or no compilation
- cons:
- slower at runtime
- problems are detected late
Consider the following python code.
def add(x,y):
return x + y
print(add(2,2))
print(add("a","b"))
print(add(2,"b"))
4
ab
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[1], line 6
4 print(add(2,2))
5 print(add("a","b"))
----> 6 print(add(2,"b"))
Cell In[1], line 2, in add(x, y)
1 def add(x,y):
----> 2 return x + y
TypeError: unsupported operand type(s) for +: 'int' and 'str'
There is optional typing specification, but it is not enforced, e.g. accepting ints.
import typing
def add(x:str, y:str) -> str:
return x + y
print(add(2,2)) # doesn't complain about getting integer types
print(add("ab", "cd"))
#print(add(2,"n"))
4
abcd
- You can use packages such as
pyrightormypyas a type checker before running your programs - Supported by VSCode python extension
Type checking: static vs. dynamic
Static (e.g, C++, Rust, OCaml, Java):
- checks if types of objects are as specified
- pros:
- faster at runtime
- type mismatch detected early
- cons:
- often need to be explicit with the type
- making procedures generic may be difficult
- potentially slower compilation
C++:
int add(int x, int y) {
return x + y;
}
Rust:
#![allow(unused)] fn main() { fn add(x:i32, y:i32) -> i32 { x + y } }
Type checking: static vs. dynamic
Note: some languages are smart and you don't have to always specify types (e.g., OCaml, Rust)
Rust:
#![allow(unused)] fn main() { let x : i32 = 7; let y = 3; // Implied to be default integer type let z = x * y; // Type of result derived from types of operands }
In Class Poll
Various programming paradigms
Programming languages aren’t just “a language” — they also tend to encourage (or make easy) certain styles of programming. These styles are called programming paradigms. Most real languages are multi-paradigm, meaning you can mix and match several styles depending on the problem.
- Imperative
- Functional
- Object-oriented
- Declarative / programming in logic
Imperative
im·per·a·tive (adjective) -- give an authoritive command
In an imperative style, you tell the computer how to do something step-by-step: update variables, run loops, and execute statements in a particular order. This is often the most “direct” way to map your mental model onto code.
Rust support? Yes — Rust is very comfortable as an imperative language. You can write straightforward step-by-step code, while still benefiting from Rust’s emphasis on safety and clarity.
# Python -- Imperative
def factorial(N):
ret = 1
for i in range(N):
ret = ret * i
return ret
Functional
In a functional style, you try to treat computation as evaluating functions rather than executing commands. The focus is on:
- functions as values (you can pass them around)
- avoiding mutable state when possible
- composing small pieces into larger computations
Rust support? Partially, yes. Rust is not a “pure” functional language, but it supports many functional ideas (like emphasizing immutability and using functions/closures to transform data) and you’ll often see Rust code written in a functional-ish style when that makes things clearer.
; Scheme, a dialect of lisp -- functional
(define (factorial n) (cond ((= n 0) 1)
(t (* n (factorial (- n 1))))))
Object Oriented
In an object-oriented style, you organize code around “objects” that bundle data (state) together with behavior (methods). Common OO ideas include encapsulation (hiding internal details) and polymorphism (treating different kinds of things through a shared interface).
Rust support? Rust is not a classic “class-based OO” language, but it supports many OO design ideas. You can bundle data and behavior together and you can program to interfaces (in Rust, this is done with a mechanism called traits). Rust intentionally avoids some traditional OO features like inheritance-based class hierarchies.
// C++ -- Object oriented pattern
class Factorial {
private:
int64 value;
public:
int64 factorial(int input) {
int64 temp = 1;
for(int i=1; i<=input; i++) {
temp = temp * i;
}
value = temp
}
int64 get_factorial() {
return value;
}
}
Declarative/Logic
In a declarative style, you describe what you want, and a system figures out how to produce it. Logic programming (like Prolog) is a classic example: you write facts and rules, and the language runtime searches for values that satisfy them.
Rust support? Rust is not a logic/declarative programming language in that sense. However, you can still write declarative-looking Rust when you chain together high-level operations, and in practice Rust often integrates with declarative systems (for example, querying data with SQL through libraries).
% Prolog -- declaritive / programming in logic
factorial(0,1). % Base case
factorial(N,M) :-
N>0, % Ensure N is greater than 0
N1 is N-1, % Decrement N
factorial(N1, M1), % Recursive call
M is N * M1. % Calculate factorial
Memory management: manual vs. garbage collection
At least 3 kinds:
- Manual (e.g. C, C++)
- Garbage collection (e.g. Java, Python)
- Ownership-based (e.g. Rust)
Manual
- Need to explicitly ask for memory and return it
- pros:
- more efficient
- better in real–time applications
- cons:
- more work for the programmer
- more prone to errors
- major vector for attacks/hacking
Example below in C++.
Garbage collection
- Memory freed automatically
- pros:
- less work for the programmer
- more difficult to make mistakes
- cons:
- less efficient
- can lead to sudden slowdowns
Ownership-Based
- Keeps track of memory object ownership
- Allows borrowing, references without borrowing, move ownership
- When object goes out of scope, Rust automatically deallocates
- Managed deterministically at compile-time, not run-time like garbage collection
We'll dive deeper into Rust ownership later.
Rust Language (Recap)
-
high–level (but lower level than Python)
-
imperative (but has functional and object-oriented features)
-
compiled
-
static type checking
-
ownership-based memory management
Most important difference between Python and Rust?
How do we denote blocks of code?
- Python: indentation
- Rust:
{...}
| Language | formatting | scoping |
|---|---|---|
| Python | indentation | indentation |
| Rust | indentation | braces, {} |
Example in Rust
#![allow(unused)] fn main() { fn hi() { println!("Hello!"); println!("How are you?"); } }
Don't be afraid of braces!!! You'll encounter them in C, C++, Java, Javascript, PHP, Rust, ...
Memory Structure of an Executable Program
It's very helpful to have conceptual understanding of how memory is structured in executable programs.
The figure below illustrates a typical structure, where some low starting memory address is at the bottom and then memory addresses increase as you go up in the figure.

Here's a short description of each section starting from the bottom:
- text -- the code, e.g. program instructions
- initialized data -- explicitly initialized global/static variables
- uninitialized data (bss) -- uninitialized global/static variables, generally auto-initialied to zero. BSS -- Block Started by Symbol
- heap -- dynamically allocated memory. grows as structures are allocated
- stack -- used for local variables and function calls
In Class Poll
Example of unsafe programming in C
Let's take a look at the problem with the following C program which asks you to guess a string and hints whether your guess was lexically less or greater.
- Copy the code into a file
unsafe.c - Compile with a local C compiler, for example,
cc unsafe.c - Execute program, e.g.
./a.out
Try with the following length guesses:
- guesses of string length <= 20
- guesses of string length > 20
- guesses of string length >> 20
Pay attention to the printout of
secretString!
Lecture Note: Switch to code
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(){
char loop_bool[20];
char secretString[20];
char givenString[20];
char x;
int i, ret;
memset(&loop_bool, 0, 20);
for (i=0;i<19;i++) {
x = 'a' + random() % 26;
secretString[i] = x;
}
printf("secretString: %s\n", secretString);
while (!loop_bool[0]) {
gets(givenString);
ret = strncmp(secretString, givenString, 20);
if (0 == ret) {
printf("SUCCESS!\n");
break;
}else if (ret < 0){
printf("LESS!\n");
} else {
printf("MORE!\n");
}
printf("secretString: %s\n", secretString);
}
printf("secretString: %s\n", secretString);
printf("givenString: %s\n", givenString);
return 0;
}
A Brief Aside -- The people behind the languages
Who are these people?
- Guido Van Rossum
- Graydon Hoare
- Bjarne Stroustrup
- James Gosling
- Brendan Eich
- Brian Kernighan and Dennis Ritchie
Who are these people?
- Guido Van Rossum -- Python
- Graydon Hoare -- Rust
- Bjarne Stroustrup -- C++
- James Gosling -- Java
- Brendan Eich -- Javascript
- Brian Kernighan and Dennis Ritchie -- C
Recap
- Programming languages vary along several axes: compiled vs. interpreted, static vs. dynamic type checking, and memory management strategy.
- Programming paradigms are “styles” of programming; many languages (including Rust) are multi-paradigm (imperative + some functional and OO design ideas, but not logic-programming).
- Memory management is a major practical difference between languages: manual (C/C++), garbage-collected (Python/Java), and ownership-based (Rust).
- Rust aims for performance and safety without a garbage collector; Python aims for simplicity and flexibility, often trading away low-level control/performance.
- A program’s memory is commonly organized into text, data, heap, and stack, and understanding this helps explain performance and safety issues.
- “Unsafe” C examples (like unchecked input) highlight how memory bugs can become security vulnerabilities.
- Languages come from real design communities—knowing some of the key people helps connect languages to their goals and trade-offs.