Systems Architecture
1. Introduction to C
Boni García
boni.garcia@uc3m.es
Telematic Engineering Department
School of Engineering
2024/2025
Table of contents
1. Introduction
2. “Hello world” in C
3. The build process
4. Data types
5. Variables
6. Constants
7. Code style
8. Takeaways
Systems Architecture - 1. Introduction to C 2
Table of contents
1. Introduction
- Main features of C
- General-purpose vs domain-specific
- Application and system programming
- Programming language levels
- Compiled vs. interpreted
- Programming paradigms
- Type system
2. “Hello world” in C
3. The build process
4. Data types
5. Variables
6. Constants
7. Code style
8. Takeaways
Systems Architecture - 1. Introduction to C 3
1. Introduction
C is a general purpose programming language developed by Dennis Ritchie
between 1969 and 1972 at Bell Laboratories
Originally C was oriented to the implementation of operating systems, specifically Unix
(with Ken Thompson)
It was first standardized by the ANSI (American National Standards Institute) in 1989
It was ratified as an ISO (International Organization for Standardization) standard in
1990
Systems Architecture - 1. Introduction to C 4
Source: Dennis Ritchie biography by Moisés Cuevas
Source: Wikipedia
https://en.wikipedia.org/wiki/Dennis_Ritchie
1. Introduction
Today C is still one of the most widely used languages
Systems Architecture - 1. Introduction to C 5
https://www.tiobe.com/tiobe-index/
1. Introduction
C is considered the foundation of modern programming languages
Systems Architecture - 1. Introduction to C 6
© Image created by Hugo Tobio
Source: Bonilista 647, 3 September 2023, La Historia del lenguaje C
1. Introduction - Main features of C
The C programming language can be classified in several ways:
C is a general-purpose programming language
C can be used both application programming and system programming
C is a high-level programming language
Although it allows certain low level handling (direct memory access)
For that reason, it is sometimes classified as a medium-level language
C is a compiled language (the C source code must be converted into machine
code to be executed)
C is imperative (based on statements that are executed sequentially) and
procedural (it relies on subroutines called functions to perform computations)
C is statically-typed (the type of a variable is known at compile-time) and
weakly-typed (it allows variables of one type to be used as if they were of
another)
Systems Architecture - 1. Introduction to C 7
1. Introduction - General-purpose vs domain-specific
A general-purpose language (GPL) is a programming language that can
create all types of programs
For instance: C, Java, Python, among others
GPLs are Turing complete, which means that they can theoretically solve any
computational problem
A domain-specific language (DSL) is a computer language specialized
to a particular application domain
For instance: MATLAB (intended primarily for numeric computing), SQL (for
relational database queries), VHDL (for hardware description)
DSLs can be (or not) Turing complete
Systems Architecture - 1. Introduction to C 8
1. Introduction - Application and system programming
An applications programming language is used for implementing user
applications, like desktop applications, command-line interface tools,
or mobile apps
For instance: C, Java, Python, JavaScript, and others
Application software generally don’t directly access hardware or low-level
resources. Instead these languages use system calls
A system programming language is used for implementing system
software, i.e., software designed to provide a platform for other
software, such as operating systems, devices drivers, or server-side
components
For instance: C, C++, Go, Rust, and others
Unlike application software, most system software are not directly used by end
users
Systems Architecture - 1. Introduction to C 9
1. Introduction - Programming language levels
Systems Architecture - 1. Introduction to C 10
High level program
Machine code
#include <stdio.h>
int main() {
printf("Hello world\n");
return 0;
}
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
It provides abstractions (close to the
natural language) independent of a
particular type of computer.
Programmer friendly, portable, easier
to create, debug, and maintain
Low-level program (assembly)
org 0x100 ; .com files always start 256 bytes into the segment
mov dx,msg ; the address of or message in dx
mov ah,9; ah=9 - "print string" sub-function
int 0x21 ; call dos services
mov ah,0x4c ; "terminate program" sub-function
int 0x21 ; call dos services
msg db 'Hello, World!',0x0d,0x0a,'$' ; $-terminated message
Mnemonics that represent basic
instructions that can be directly
translated to machine code. Highly
memory efficient but non-portable
and much more difficult to create,
debug, and maintain
Executable code (1’s and 0’s). Each
instruction causes the CPU to
perform a specific task (e.g. load
value, arithmetic operation, etc.)
1010101110101011111010101010101010101011010101010101010101010110101
1101110000110100010101010010101010101011010001010101010101010110101
1. Introduction - Compiled vs. interpreted
Compiled languages need a build step, i.e., they need to be manually
compiled to be executed on a computer
This process is sometimes called ahead-of-time (AOT) compilation
Examples of pure compiled languages are C, C++, Erlang, Haskell, Rust, and Go
Systems Architecture - 1. Introduction to C 11
Source code
#include <stdio.h>
int main() {
printf("Hello world\n");
return 0;
}
Machine code
101010111010101111101010101
010101010101101010101010101
010101011010111011100001101
000101010100101010101010110
100010101010101010101101011
Compilation
Compile-time
Machine code
101010111010101111101010101
010101010101101010101010101
010101011010111011100001101
000101010100101010101010110
100010101010101010101101011
Runtime
1. Introduction - Compiled vs. interpreted
Interpreted languages are executed directly into machine-language
instructions without a previous compilation
Nevertheless, there are few implementations of pure interpreted languages
nowadays, because interpreting source code directly would be quite slow
Systems Architecture - 1. Introduction to C 12
Source code Machine code
101010111010101111101010101
010101010101101010101010101
010101011010111011100001101
000101010100101010101010110
100010101010101010101101011
Interpretation
Runtime
1. Introduction - Compiled vs. interpreted
Instead, modern interpreted languages use an alternative approach
called just-in-time (JIT)
JIT involves the compilation during execution of a program (at runtime) rather
than before execution
Different optimizations are done in runtime to provide a better performance
Examples of interpreted languages using JIT are Python, JavaScript, or Ruby
Systems Architecture - 1. Introduction to C 13
Source code Machine code
101010111010101111101010101
010101010101101010101010101
010101011010111011100001101
000101010100101010101010110
100010101010101010101101011
Compilation +
optimization
Runtime
Java Virtual Machine
1. Introduction - Compiled vs. interpreted
Other languages, like Java, use a mixed strategy:
1. Compile-time: Compilation of the source code into some intermediate
format called bytecode
2. Runtime: bytecodes are interpreted (also using JIT) in the Java Virtual
Machine (JVM)
Systems Architecture - 1. Introduction to C 14
Source code Byte code
10101011101
01011111010
10101010101
01010110101
01010101010
Compilation
Compile-time
Machine code
Runtime
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
Byte code
10101011101
01011111010
10101010101
01010110101
01010101010
Interpretation
10101011101
01011111010
10101010101
01010110101
01010101010
1. Introduction - Programming paradigms
Programming paradigms are a way to classify programming languages
based on their features
The two major programming paradigms nowadays are:
Systems Architecture - 1. Introduction to C 15
Imperative
Programs are based on
statements (or commands) that
are executed sequentially
Procedural
It relies on procedures (or
subroutines) to perform
computations
Object-oriented
It relies on objects (entities
with an state and behavior)
to implement programs
Declarative
Programs describe their desired
results without explicitly listing
the commands or steps that must
be performed
Functional
Programs are constructed by
applying and composing
functions
Logic
Programs are based on
formal logic rules and facts
1. Introduction - Type system
Systems Architecture - 1. Introduction to C 16
In programming, a type is a set of value (e.g. integers, characters, etc.)
The rules that applies for types in a given programming languages is called
type system, and it is usually classified as static or dynamic, and as
strongly-typed or weakly-typed
The type of a variable is
known at compile-time.
Type checking (i.e., the
process of verifying and
enforcing the constraints
of types on values)
occurs before runtime
strongly-typed
weakly-typed
dynamically-typed statically-typed
The type of a variable
is known at runtime.
Type checking occurs
also at runtime
The type system allows
conversions between
unrelated types implicitly
The type system don’t
allow implicit conversions
between unrelated types
C
C++
Java
C# Rust
Groovy
Ruby
Python
PHP
JavaScript
Perl
Table of contents
1. Introduction
2. “Hello world” in C
3. The build process
4. Data types
5. Variables
6. Constants
7. Code style
8. Takeaways
Systems Architecture - 1. Introduction to C 17
2. “Hello worldin C
The main() function is used by convention as the entry point of the
program
Systems Architecture - 1. Introduction to C 18
#include <stdio.h>
int main() {
printf("Hello world\n");
return 0;
}
https://github.com/bonigarcia/c-programming Repository with code
examples
hello_world.c
2. “Hello worldin C
Statements end with semicolons ;
We define blocks of statements in curly brackets (braces) { }
The printf() function displays a text string in the standard output (stdout)
To use that function, we need to include its code from the standard library stdio.h
using the directive #include
With return we terminate the function, returning a value to the calling process
The exit code is a numerical value returned by a program to the operating system upon its
completion.
The exit code 0means success. Different than 0means some error (e.g., 1means general error)
Systems Architecture - 1. Introduction to C 19
#include <stdio.h>
int main() {
printf("Hello world\n");
return 0;
}
2. “Hello worldin C
Systems Architecture - 1. Introduction to C 20
gcc hello.c
#include <stdio.h>
int main() {
printf("Hello world\n");
return 0;
}
First, we use the compiler gcc
(GNU Compiler Collection) from the
shell to compile a c program (.c file)
By convention, the generated
executable is called a.out by
default. We can explicitly set a
different binary name using the flag
-o <name>
gcc hello.c -o hello
Compile-time Runtime
./hello
Then, we invoke the
binary name from
the shell to execute it
./a.out
Table of contents
1. Introduction
2. “Hello world” in C
3. The build process
4. Data types
5. Variables
6. Constants
7. Code style
8. Takeaways
Systems Architecture - 1. Introduction to C 21
3. The build process
The build process in C has actually 4 stages:
Systems Architecture - 1. Introduction to C 22
hello.c hello.i
1. Preprocessing
hello.s
2. Compilation 3. Assembly
hello.o
4. Linkage
hello
Source code
(C language)
Intermediate code
(expanded C
language)
Assembly code
(low-level code)
Object file
(machine-
understandable
code)
Binary file
(executable
code)
3. The build process
1. Preprocessing: The preprocessor converts the source code (.c) into some
intermediate file (.i) doing the following:
Removing comments
Expanding macros (expressions defined using the #define directive)
Expanding included files (include content of files defined using the #include directive)
2. Compilation: The compiler converts the intermediate file into an assembly file
(.s) which has low-level instructions
3. Assembly: The assembler will convert the assembly code into object code (.o)
Object code holds the translated machine code from the original source code. Object
code is not yet executable because:
It may contain references to functions or variables that are defined in other object files
It does not have an entry point required by the operating system to start the program execution
4. Linkage: The linker merges all the object(s) code into a single one (executable)
Systems Architecture - 1. Introduction to C 23
3. The build process
Others GCC commands:
Systems Architecture - 1. Introduction to C 24
gcc hello.c -o hello -save-temps
gcc hello.c -c -o hello.o Only generates object file (.o)
Generates binary file but does not delete
temporal files (.i, .s, and .o)
gcc hello.c -E -o hello.i Only generates intermediate code (.i)
gcc hello.c -S -o hello.s Only generates assembly code (.s)
gcc hello.c -o hello -Wall Generates binary file and check all
warnings
Table of contents
1. Introduction
2. “Hello world” in C
3. The build process
4. Data types
- Basic types
- Enumerated types
- Type definitions
- Type conversions
5. Variables
6. Constants
7. Code style
8. Takeaways
Systems Architecture - 1. Introduction to C 25
4. Data types
A data type defines the set of values for a variable
There are four groups of data types in C:
Systems Architecture - 1. Introduction to C 26
Basic
Primary data types (integers,
characters, etc.)
Derived
Arrays, unions, structures, and
pointers
Enumerated
Integer values associated to
labels
Void
No value (for functions without
return or generic pointers)
4. Data types - Basic types
The basic types in C are the following:
Systems Architecture - 1. Introduction to C 27
Data type
Description
Typical memory
size (in bytes)
Range
Format specifier
char
Characters with sign
1
-
128 to 127
%c
unsigned char
Characters without sign
1
0 to 255
%c
short
Short integers with sign
2
−32,768 to 32,767
%hi
or %hd
unsigned short
Short integers without sign
2
0 to 65,535
%hu
int
Integers with sign
4
-
2,147,483,648 to 2,147,483,647
%
ior %d
unsigned int
Integers without sign
4
0 to 4,294,967,295
%u
long
Long integer with sign
8
-
9.2e18 to 9.2e18
%li
or %ld
unsigned long
Long integer
8
0 to 1.8E19
%
lu
float
Decimal
4
1.1e
-38 to 3.4e38
%f
double
Decimal with double precision
8
2.2e
-308 0 to 1.7e308
%
lf
4. Data types - Basic types
A character variable holds ASCII value, i.e., an integer number between
0 and 127
Systems Architecture - 1. Introduction to C 28
Source: Wikipedia
https://en.wikipedia.org/wiki/ASCII
4. Data types - Basic types
Systems Architecture - 1. Introduction to C 29
#include <stdio.h>
int main() {
char character = 'c';
int integer = 255;
float float_num = 1.2;
double double_num = 3.1e33;
printf("This is a character: %c\n", character);
printf("This is an integer: %d\n", integer);
printf("This is a float: %f\n", float_num);
printf("This is a double: %g\n", double_num);
printf("This is an integer in hexadecimal: %X\n", integer);
return 0;
}
This is a character: c
This is an integer: 255
This is a float: 1.200000
This is a double: 3.1e+33
This is an integer in hexadecimal: FF
The first argument in function printf (i.e., a string) can include
format specifiers (i.e., subsequences beginning with %)
In this case, the additional arguments are formatted and inserted in the
resulting string replacing their respective specifiers
4. Data types - Basic types
Systems Architecture - 1. Introduction to C 30
#include <stdio.h>
int main() {
printf("The size of a CHAR is %ld bytes\n", sizeof(char));
printf("The size of a SHORT is %ld bytes\n", sizeof(short));
printf("The size of a INT is %ld bytes\n", sizeof(int));
printf("The size of a LONG is %ld bytes\n", sizeof(long));
printf("The size of a FLOAT is %ld bytes\n", sizeof(float));
printf("The size of a DOUBLE is %ld bytes\n", sizeof(double));
return 0;
}
The size of a CHAR is 1 bytes
The size of a SHORT is 2 bytes
The size of a INT is 4 bytes
The size of a LONG is 4 bytes
The size of a FLOAT is 4 bytes
The size of a DOUBLE is 8 bytes
The operator sizeof generates the storage size (in bytes) of an
expression or a data type
4. Data types - Enumerated types
Enumeration are a user defined data types used to assign names to
integral constants
These names make a program easy to read and maintain
The keyword enum is used to declare new enumeration types in C
Systems Architecture - 1. Introduction to C 31
#include <stdio.h>
int main() {
enum days {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
};
enum days today = SATURDAY;
enum days tomorrow = SUNDAY;
printf("Today is %d\n", today);
printf("Tomorrow is %d\n", tomorrow);
if (today == SATURDAY || today == SUNDAY) {
printf("It's the weekend!\n");
}
return 0;
}
Today is 5
Tomorrow is 6
It's the weekend!
4. Data types - Type definitions
The keyword typedef in C allows to create an additional name (alias)
for another data type
Systems Architecture - 1. Introduction to C 32
My character is: d
#include <stdio.h>
int main() {
typedef unsigned char byte;
byte character = 'd';
printf("My character is: %c\n", character);
return 0;
}
4. Data types - Type conversions
Converting one type explicitly into another is known as type casting
(or type-conversion)
We convert the values from one type to another explicitly using the
cast operator as follows:
Systems Architecture - 1. Introduction to C 33
(type) expression
#include <stdio.h>
int main() {
int sum = 17, count = 5;
double mean;
mean = (double) sum / count;
printf("Mean: %f\n", mean);
return 0;
}
Mean: 3.400000
4. Data types - Type conversions
Implicit type conversion is known as type promotion (or coercion)
Systems Architecture - 1. Introduction to C 34
Sum: 66
#include <stdio.h>
int main() {
int i= 1;
char c = 'A'; // The ASCII value of 'A' is 65
int sum;
sum = i + c;
printf("Sum: %d\n", sum);
return 0;
}
4. Data types - Type conversions
Implicit type conversion is known as type promotion (or coercion)
Systems Architecture - 1. Introduction to C 35
65 as a character is A
#include <stdio.h>
int main() {
int i = 65;
char c = i; // 65 is the ASCII value of 'A'
printf("%d as a character is %c\n", i, c);
return 0;
}
Table of contents
1. Introduction
2. “Hello world” in C
3. The build process
4. Data types
5. Variables
- Scope
- Shadowing
6. Constants
7. Code style
8. Takeaways
Systems Architecture - 1. Introduction to C 36
5. Variables
Variables are containers for storing data values
We distinguish three ways of handling variables:
1. Declaration: statement to specify the variable name and its data type
2. Assignment: set a value to the variable using the operator =
3. Initialization: initial assignment during declaration
Systems Architecture - 1. Introduction to C 37
#include <stdio.h>
int main() {
int a; // declaration
a = 10; // assignment
int b = 10; // initialization
return 0;
}
5. Variables - Scope
The scope is the range within a program for which an item (e.g., a
variable ) is valid(beyond that, it cannot be accessed)
There are two types of variables depending on its scope:
Systems Architecture - 1. Introduction to C 38
#include <stdio.h>
/*
These are global variables
(multi-line comment)
*/
int a = 1;
int b;
int main() {
int c, d = 2; // local variables
char e;
e = 'z'; // assignments
b = 7;
c = 5;
printf("a=%d b=%d c=%d d=%d e=%c\n", a, b, c, d, e);
return 0;
}a=1 b=7 c=5 d=2 e=z
Global variables: defined outside a
function, usually on top of the program
Local variables: defined inside a
function or block. They can be used
only by statements that are inside that
function or block of code
5. Variables - Scope
We need to be careful with the variable scope. For example:
Systems Architecture - 1. Introduction to C 39
#include <stdio.h>
int main() {
int a = 1;
if (a > 0) {
int b = 2;
printf("a=%d and b=%d\n", a, b);
}
printf("a=%d and b=%d\n", a, b);
return 0;
}
What happen here?
5. Variables - Shadowing
Shadowing appears when a variable is defined in a scope with the
same name of another one valid in a higher level scope
Systems Architecture - 1. Introduction to C 40
#include <stdio.h>
int b = 0;
int main() {
int a = 1;
if (a > 0) {
int b = 2;
printf("a=%d and b=%d\n", a, b);
}
printf("a=%d and b=%d\n", a, b);
return 0;
}
What happen here?What happen here?
Table of contents
1. Introduction
2. “Hello world” in C
3. The build process
4. Data types
5. Variables
6. Constants
7. Functions
8. Code style
9. Takeaways
Systems Architecture - 1. Introduction to C 41
6. Constants
Constants refer to fixed values that the program may not alter during
its execution. There are different ways to define constants in C:
Using a macro defined with the preprocessor directive #define
Using the keyword const
Using the enumerated types
Systems Architecture - 1. Introduction to C 42
#include <stdio.h>
#define MAX 64
int main() {
const int num = 15;
enum parity {
ODD = 1, EVEN = 2
};
printf("MAX=%d\n", MAX);
printf("num=%d\n", num);
printf("EVEN=%d ODD=%d\n", EVEN, ODD);
return 0;
}
MAX=64
num=15
EVEN=2 ODD=1
Table of contents
1. Introduction
2. “Hello world” in C
3. The build process
4. Data types
5. Variables
6. Constants
7. Code style
8. Takeaways
Systems Architecture - 1. Introduction to C 43
7. Code style
Some usual guidelines in C:
Use meaningful names for variables, constants and functions
Use snake-case (underscores for multi-word names, e.g. file_name) and not
camel-case (use of capital letter except the initial word, e.g. fileName)
Use the same indentation level (3 or 4 spaces, or 1 tab)
Use the same style for opening braces (in the same line or just above)
Define constants macros in uppercase
Best practices:
Avoid code duplication (DRY - Don’t Repeat Yourself)
Group related functions in a separate module
Use an automated code formatter, for example:
Systems Architecture - 1. Introduction to C 44
Windows and Linux: Ctrl + Shift + F
macOS: Command + Shift + F
Windows: Shift + Alt + F
Linux: Ctrl + Shift + I
macOS: Shift + Option + F
Table of contents
1. Introduction
2. “Hello world” in C
3. The build process
4. Data types
5. Variables
6. Constants
7. Code style
8. Takeaways
Systems Architecture - 1. Introduction to C 45
8. Takeaways
C is a programming language: general-purpose, high-level, compiled,
imperative and procedural, statically and weakly-typed
C programs are organized using procedures called functions. At least,
the function main() should be defined (i.e., the program entry point)
The build process in C is done with GCC and has 4 steps: preprocessing,
compilation, assembly, and linkage
There are four groups of data types in C: basic (int, char, etc.),
enumerated (enum), void (void), and derived (arrays, unions,
structures, and pointers)
Depending its scope, there are two types of variable in C: global
(defined outside a function) and local (defined inside a function or
block)
Systems Architecture - 1. Introduction to C 46