1 of 51

Data and I/O

03603111 Programming Fundamentals I

Department of Computer Engineering, Faculty of Engineering at Sriracha

2 of 51

Course outline and schedule

  • L01 Introduction
  • L02 Data and I/O (today!)
  • L03 Conditionals
  • L04 Loops
  • L05 Arrays, strings, and pointers
  • L06 Functions
  • Lab exam #1
  • L07 Nested loops and I/O handling
  • L08 Sorting
  • L09 Searching
  • L10 Structures and dynamic memory allocation
  • L11 Linked data structures
  • L12 Unions and tagged structures
  • Lab exam #2

2

3 of 51

Overview

  • Values, variables, and types
  • Expressions and statements
  • Arithmetic operators
  • Inputs and outputs
  • #define and value abstraction

3

4 of 51

Previous lesson recaps

4

5 of 51

Let’s make it clear once again

  • No plagiarism!
    • You may not submit the work, or a direct derivative thereof, of another person as your own
    • You may not have another person or an AI assistant (such as ChatGPT or Copilot) do the work for you and claim it as your own
    • However, you may discuss assignments and problems with other people, as long as the actual work is done by you

  • Remember: Learning = Coding + Mistakes
    • The best way to understand this deeply is by writing a lot of code and making a lot of mistakes — that’s how your brain truly learn
    • Getting stuck is normal – if you’re not stuck, you’re not learning

5

6 of 51

Basic computer architecture

  • CPU computes and controls other parts of the computer
    • ALU performs arithmetic and logic operations
    • CU directs and controls flow of execution and data transfers
    • Registers are small high-speed memory inside CPU for storing contents being computed
  • Memory unit holds data and instructions
    • Main memory (RAM and ROM) is directly accessible by CPU
    • Secondary memory, e.g., HDD or USB drives, is non-volatile and not directly accessible by CPU

6

Image by Amila Ruwan 20 - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=95966225

7 of 51

Program execution

  • A program must be loaded into memory before it can be run
  • CPU executes machine instructions in multiple stages
    • Fetch reads next instruction from the memory address stored in the program counter
    • Decode determines what type of operation needs to be performed
    • Execute activates relevant parts of the CPU to perform the operation
  • These stages are simplification of actual CPUs, which might have many more stages

7

Fetch

Decode

Execute

8 of 51

Program translation

  • Programs written in C cannot be run directly
  • We need a compiler to translate the C source code into object files
  • An object file contains machine code translated from a source file, but it’s not yet a complete executable file
  • We then use a linker to combine all the object files and related libraries (e.g., standard I/O) into an executable file
  • The executable file contains the machine code that can be run
  • The process of compiling then linking is collectively called building the executable file – to build is to compile and link

8

Compiler

Linker

Source file(s) (.c)

Object file(s)

Executable file

Included libraries

Building process

9 of 51

Relationship between base-2, -10, and -16 numbers

9

  • 00002 = 0+0+0+0 = 010 = 016
  • 00012 = 0+0+0+1 = 110 = 116
  • 00102 = 0+0+2+0 = 210 = 216
  • 00112 = 0+0+2+1 = 310 = 316
  • 01002 = 0+4+0+0 = 410 = 416
  • 01012 = 0+4+0+1 = 510 = 516
  • 01102 = 0+4+2+0 = 610 = 616
  • 01112 = 0+4+2+1 = 710 = 716
  • 10002 = 8+0+0+0 = 810 = 816
  • 10012 = 8+0+0+1 = 910 = 916
  • 10102 = 8+0+2+0 = 1010 = A16
  • 10112 = 8+0+2+1 = 1110 = B16
  • 11002 = 8+4+0+0 = 1210 = C16
  • 11012 = 8+4+0+1 = 1310 = D16
  • 11102 = 8+4+2+0 = 1410 = E16
  • 11112 = 8+4+2+1 = 1510 = F16

If we keep going up, we’ll have:

  • 1 00002 = 16+0+0+0+0 = 1610 = 1016
  • 1 00012 = 16+0+0+0+1 = 1710 = 1116

… and so on …

  • 01 11112 = 0+16+8+4+2+1 = 3110 = 1F16
  • 10 00002 = 32+0+0+0+0+0 = 3210 = 2016

10 of 51

Working with data

11 of 51

What we’re going to cover today

#include <stdio.h>

int main()

{

// Variable declarations

int height;

float weight;

float bmi;

int cm2_to_m2 = 10000;

// Read body measurement

printf("Enter height (cm): ");

scanf("%d", &height);

printf("Enter weight (kg): ");

scanf("%f", &weight);

// Compute body mass index (BMI)

bmi = weight / (height * height) * cm2_to_m2;

printf("BMI = %f\n", bmi);

return 0;

}

Types of the variables

A literal or “written value” – used here to initialize the cm2_to_m2 variable

An expression – something that can be evaluated to a value

Variables – used for storing values

Another integer literal – a literal is by itself also an expression, as it can be evaluated to a value

Statements specify actions to be carried out – a program is a sequence of statements

12 of 51

Programs and data

  • Computer programs take input data, process it, and produce desired outputs
    • The program shown earlier takes height and weight and computes the BMI
    • This data is made up of numerical values
  • There are basically two types of numerical values: integer values and real values (we’ll ignore complex values for now)

  • We also want to process other types of values, e.g., texts

  • We need to represent those values in some way in our programs

12

13 of 51

Basic C data types

  • In C, char is actually an integer type, but commonly used to represent a character (more precisely, the numeric code for that character)
  • Integer types are signed by default (except char, which may be signed or unsigned by default, depending on the compiler)
  • You can create variations of char and int by adding prefixes like long or unsigned – we’ll explore those later
  • The double type is like float, but with more precision – it can store more significant digits and larger or smaller values

13

Type

Description

char

1-byte character or integer

int

Basic integer type

float

32-bit (4-byte) “single-precision” floating-point number

double

64-bit (8-byte) “double-precision” floating-point number

14 of 51

Types define a set of possible values and operations

  •  

14

15 of 51

Values and types

  • The followings are C literals*:
    • 100
    • 1.234f
    • 3.14
    • 'x'
    • '9'
    • 3.56e-4
    • '\n'
    • 0xf3
    • 0b1011

* Literals mean “written values”

  • These literals are of the types:
    • int
    • float
    • double
    • char
    • char
    • double
    • char
    • int (24310, written in hexadecimal)
    • int (1110, written in binary)

15

16 of 51

Variables

  • Variables are containers for storing values
  • Variables have a name and an associated type
    • There are unnamed variables, e.g., temporary variables, but we’re not discussing them here
  • Values can be changed (unless declared as constant)
  • Variables must be declared before use

16

17 of 51

Declaring variables

  • Variables must be declared before use
  • Declaration syntax: <type> <name>, <name>, …;
  • With initializers: <type> <name> = <value>, <name> = <value>, …;

int height; // single variable

float weight, bmi; // declare two variables at once

int cm2_to_m2 = 10000; // declare and initialize its value

17

18 of 51

Default values and initialization

  • Variables declared inside the main() function are local variables
  • Local variables have undefined default values (except static variables)

int count;

int height;

printf("%d, %d\n", count, height);

~> 4156548, -538089401

  • We often need to explicitly initialize them
  • We’ll talk about global variables and why local variables do not have default values when we learn about functions

18

19 of 51

C identifiers

  • Identifiers are names for variables, constants, user-defined types, functions, and other things that need a user-defined name
  • Valid identifiers
    • Combination of letters (A-Z, a-z), digits (0-9), and underscores (_)
    • Not beginning with a digit
    • Not a C reserved word (e.g., int, if, unsigned)
  • C is case-sensitive, e.g., pi and PI are different identifiers

19

20 of 51

C naming conventions

  • No real consensus: C has been around for a very long time, and coding styles vary a lot
    • So, use this as just a guideline, but be consistent – stick to a style

  • There are generally 3 common naming conventions: snake_case, camelCase, and PascalCase
  • For variable names, snake_case is the most common
    • Examples: a, x_n, http_response, num_running_instances

  • Also commonly used for file names: snake_case.c, snake_case.h

20

INTERESTING BITS

21 of 51

Expressions

  • An expression is a syntactic unit that can be evaluated to a value
    • Therefore, an expression has a type
  • All of these are expressions:
    • x + 5
    • 2 * (a - b) + 5 * cos(30)
    • 13
    • x

  • But this is not:
    • return x + 5; // this is a statement

  • An expression is usually part of a statement

21

22 of 51

Arithmetic operators, precedence, and grouping

  • +, -, *, and / do what you expect
  • % computes the remainder of the division
    • a % b equals 1 if a = 10 and b = 3
  • How is this expression evaluated? (if x = 15 and y = 10)
    • 2 + x / 3 * y - 4
  • Use explicit grouping with parentheses to make it more readable
    • 2 + (x / 3 * y) - 4

22

Operator

Description

+

Add

-

Subtract

*

Multiply

/

Divide

%

Modulo

Operator

Associativity

Precedence

( )

left to right

highest

+ - (unary)

right to left

* / %

left to right

+ - (binary)

left to right

lowest

23 of 51

Statements

  • Statements specify actions to be carried out
  • Statements have side effects – they change the state of the program
  • A program is a sequence of statements

printf("Enter weight (kg): "); // output statement

scanf("%f", &weight); // input statement

/* Below is an assignment statement */

bmi = weight / (height * height) * cm2_to_m2;

return 0; // return statement

23

24 of 51

Assignment operators

  • Suppose that integers a = 10, b = 5, and c = 1 in all examples below

  • a = b + c; ~> a = 6
  • a = a + 10; ~> a = 20
  • a += b + c; ~> a = 16
  • a -= b + c; ~> a = 4
  • a *= b + c; ~> a = 60
  • a /= b + c; ~> a = 1
  • a %= b + c; ~> a = 4

24

Operator

Description

=

Assign

+=

Add to

-=

Subtract from

*=

Multiply assign

/=

Divide assign

%=

Modulo assign

Note that = is for assignment, not equality

25 of 51

Inputs and outputs

26 of 51

Formatted printing

printf("BMI = %f\n", bmi);

printf(format-string, arg1, arg2, …);

  • Format string specifies what to print and how each positional argument is displayed using format specifiers
    • There are many format specifiers – look them up

  • In this example: "%f" tells printf() to display a floating-point number after the text "BMI = " and before new line

26

27 of 51

Format strings and format specifiers

printf("%d is equal to 0x%x (%X)\n", 1234, 1234, 1234);

~> 1234 is equal to 0x4d2 (4D2)

printf("%d is also the ASCII code of '%c'\n", 70, 70);

~> 70 is also the ASCII code of 'F’

printf("%f + %.1f = [%8.2f]\n", 12.5f, 21.3f, 12.5f + 21.3f);

~> 12.500000 + 21.3 = [ 33.80]

// '0' flag -> prefixed with 0s, '-' flag -> left-aligned

printf("Mr.%s, %06d is billed at [%-4d]PM.\n", "Kim", 25, 12);

~> Mr.Kim, 000025 is billed at [12 ]PM.

// Note the inaccuracy in the output

printf("A large float: %f\n", 2.7182818284e20);

~> A large float: 271828182839999987712.000000

printf("There's a %u%% OFF SALE right now.\n", 50);

~> There's a 50% OFF SALE right now.

27

%d

int (decimal)

%u

unsigned int

(decimal)

%x, %X

int

(hexadecimal)

%c

char (character)

%f

float

%lf

double

%s

char[] (string)

%%

Printing %

flag

field width

precision

type

%

-, 0

8

.2

f

28 of 51

Reading inputs

scanf("%d %f", &height, &weight);

scanf(format-string, &var1, &var2, …);

  • Format string specifies the type of each positional argument using format specifiers
  • The & operator before the variable name means passing the address of the variable (instead of its value) to scanf() so that scanf() can modify it
    • We’ll talk more about this in a later lesson

28

29 of 51

Understanding scanf() format specifiers

These are 3 simplified rules for using scanf() (assuming the user enters input correctly):

  1. Most format specifiers (e.g., %d, %f, %s, etc.) automatically skip leading whitespace

  • The %c (character) format specifier does not – it reads the next character exactly as it is, even if it’s a space, tab, or newline

  • Adding a space before %c in the format string tells scanf() to skip any leading whitespace – this is particularly useful when reading characters after other input

29

30 of 51

scanf() examples (red is user inputs)

int a; char c, d, e; float x; double y;

scanf("%d %f %c", &a, &x, &c);

123 4.5 x

~> 123, 4.5, 'x'

scanf("%d%lf%c", &a, &y, &c);

123 4.5 x

~> 123, 4.5, ' '

25

3.9f

~> 25, 3.9, 'f'

scanf("%c%c%c", &c, &d, &e);

A1$2

~> 'A', '1', '$'

A 1 $ 2 // Note the leading space

~> ' ', 'A', ' '

scanf(" %c %c %c", &c, &d, &e);

A1$2

~> 'A', '1', '$'

A 1 $ 2 // note the leading space

~> 'A', '1', '$'

scanf("%d", &a);

scanf("%c", &c);

42

x

~> 42, '⏎' // it won’t even wait for 'x'

scanf("%d", &a);

scanf(" %c", &c);

42

x

~> 42, 'x'

30

Skip any leading whitespace

Use %lf for double

31 of 51

BMI example revisited

#include <stdio.h>

int main()

{

int height;

float weight;

float bmi;

int cm2_to_m2 = 10000;

printf("Enter height (cm): ");

scanf("%d", &height);

printf("Enter weight (kg): ");

scanf("%f", &weight);

bmi = weight / (height * height) * cm2_to_m2;

printf("BMI = %.2f\n", bmi);

return 0;

}

31

Enter height (cm): 168

Enter weight (kg): 62

BMI = 21.97

Result:

32 of 51

More on types

33 of 51

Integer types with type modifiers

33

Modifier(s)

Type

Minimum Size (bytes)

Range

signed

char

1

-128 to 127

unsigned

1

0 to 255

short

int

2

-32,768 to 32,767

unsigned short

2

0 to 65535

signed

2 (normally 4)

Refer to either above or below

unsigned

2 (normally 4)

long

4

-2,147,483,648 to 2,147,483,647

unsigned long

4

0 to 4,294,967,295

long long

8

-263 to 263-1

unsigned long long

8

0 to 264-1

IMPORTANT: Assignments or operations that produce values beyond the range of the type will cause numerical overflow or wrapping

34 of 51

What data type should be used for the followings?

  • 123
  • 'z'
  • 3.14159
  • 3215359290
  • -5.173e-104
  • '7'
  • 0x5F2A
  • Weight of a person
  • English letter
  • Your GPA
  • World population
  • Intergalactic distance in km
  • Diameter of a cell in the body
  • Product price

34

35 of 51

char numeric character code usually follows the ASCII standard

35

By Shabaz1000 - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=150153452

36 of 51

Two sides of a char

  • The char type represents both a small integer and a character at the same time

char c, d, e, f;

c = 'A';

d = 65;

e = 'a';

f = 97;

printf("%c %c %c %c\n", c, d, e, f);

~> A A a a

printf("%d %d %d %d\n", c, d, e, f);

~> 65 65 97 97

// Move up 3 characters

c += 3;

printf("'%c' = %d\n", c, c);

~> 'D' = 68

// Character distance

printf("'G'-'%c' = %d\n", c, 'G'-c);

~> 'G'-'D' = 3

// Numeric characters aren't numbers!

c = '8';

d = '1';

printf("c - d = %d\n", c - d);

~> c - d = 7

printf("c + d = %d\n", c + d);

~> c + d = 105

36

37 of 51

Literal value suffixes

  • Literal values have types which are specified by their suffixes
  • They affect type conversion (discussed later) when used in expressions

  • Common suffixes are:
    • U or u for unsigned, e.g.,
    • L or l for long, e.g.,
    • F or f for float, e.g.,
    • U and L can be combined, e.g., 123UL

  • Fractional literal values are double unless the F or f suffix is specified

37

INTERESTING BITS

38 of 51

Abstraction

39 of 51

What’s wrong with this code?

#include <stdio.h>

int main()

{

float r = 5.0;

printf("Radius for the circle = %f\n", r);

printf("Area = %f\n", 3.14159 * r * r);

printf("Circumference = %f\n", 2 * 3.14159 * r);

return 0;

}

39

The value 3.14159 is used in many (two) places

40 of 51

Why does it matter?

  • The code is harder to understand – what exactly is 3.14159?
  • The code is harder to change – if we want to change it to 3.1415926535, we have to change it in all places

  • What can we do?
    • We can name it to give it a meaning
    • We can put its definition in once place to make it easier to change

  • The #define directive is used to define a value or macro, and is translated by the preprocessor before compilation
  • Any place in the code that refers to the defined name is replaced by the defined value by the preprocessor

40

41 of 51

#include <stdio.h>

#define PI 3.14159

int main()

{

float r = 5.0;

printf("Radius for the circle = %f\n", r);

printf("Area = %f\n", PI * r * r);

printf("Circumference = %f\n", 2 * PI * r);

return 0;

}

41

Benefits of using constant values

  • Code more readable (PI instead of 3.14159)
  • Reuse the name PI, less repetitions, less errors
  • Easier to change – only one place

Macro (constant value)

42 of 51

Naming a value is a kind of abstraction

  • When we define a new thing by giving it a name, hiding its details or complexity, and then think about it at a higher level (using that name), we are creating an abstraction

  • Let’s look at some real-world examples:
    • Pi is an abstraction of the ratio between the circumference and diameter of a circle
    • The concept “human” is an abstraction of a complex being
    • The concept “walking” is an abstraction of a complex coordination of limbs in order to move us toward a destination

  • We deal with 3 types of abstraction in this course:
    • Value abstraction (as we did here with #define)
    • Function abstraction
    • Data abstraction

  • Abstraction is a core concept in programming

42

43 of 51

More naming conventions

  • No real consensus: C has been around for a very long time, and coding styles vary a lot
    • So, use this as just a guideline, but be consistent – stick to a style

  • Variables: snake_case
    • a, x_n, http_response, num_running_instances
  • Constants and enum values: ALL_CAPS_NAME
  • File names: snake_case.c, snake_case.h

43

INTERESTING BITS

44 of 51

Types and limitations

44

45 of 51

What follows is an advanced topic –

not required, but may deepen your understanding

45

46 of 51

Representing integers

  • In the decimal world, if we limit an integer to only 3 digits, we can have 103 = 1,000 possible values, ranging from 0 to 999

  • In computers, we use binary digits (bits) instead of decimal digits
  • If we limit a binary integer to 8 bits (1 byte), we can have 28 = 256 possible values, ranging from 0 to 255
  • If we reserve one bit to represent the sign (+ or -), we have 7 bits left for the magnitude – this gives us 27 = 128 possible values on each side, ranging from 0 to 127 on the positive side and from -128 to -1 on the negative side
  • The former is called an unsigned integer and the latter a signed integer

  • In C, the basic integer types include char (typically 1 byte) and variations of int types (2 to 4 bytes or more), with both signed and unsigned versions

46

47 of 51

Integer overflow

  • Again, in the decimal world, if we limit an integer to only 3 digits, we can store at most 103 = 1,000 possible values – from 0 to 999
  • If we add 50 to 985, we should get 1,035
  • Since we can only store 3 digits, only 035 is kept, resulting in an incorrect value
  • This is called an integer overflow, where the value wraps around after reaching the maximum

  • In computers, we use binary digits, so the limits are based on powers of 2
  • For an 8-bit unsigned integer (unsigned char), the range is 0 to 255
    • If we add 20 to 250, we expect 270, but we get 14 instead – the results wraps around to zero
  • For an 8-bit signed integer (signed char), the range is -128 to 127
    • Overflow in this case may cause the result to wrap across the sign boundary, flipping the sign
    • For example, adding 20 to 120 should give 140, but instead we get -116

47

48 of 51

Representing real numbers

  • Consider a decimal scientific notation, such as 1.27x103 (which equals 1,270) or -3.25x10-2 (which equals -0.0325)
  • This notation consists of two key parts: the significand (e.g., 1.27, -3.25) and the exponent (e.g., 3, -2) – both can be positive or negative
  • If we limit the significand to 3 digits and the exponent to 1 digit, we can represent a number as large as 9.99x109 = 9,990,000,000 or as small as 1.00x10-9 = 0.000000001
  • However, even though the range spans very large and very small numbers, we can’t represent numbers like 9,999,000,000 or 0.1234 or even 9,999 precisely – because this would require a 4-digit significand

  • So, the significand determines the precision and the exponent determines the range (how large how small the number can be)

48

49 of 51

Representing real numbers

  • Now, in computers, these numbers are represented in binary and the exponent is a power of 2 instead of 10
  • We represent real numbers using a system similar to scientific notation, although slightly more complex – we call it the floating-point representation
    • There is also fixed-point representation, but that’s beyond our scope here

  • Suppose we have 32 bits available, we might assign:
    • 23 bits to the significand (also called the mantissa)
    • 1 bit for the sign of the number
    • 7 bits for the exponent
    • and 1 bit for the sign of the exponent
  • Due to a special arrangement, the 23-bit significand effectively becomes 24 bits, providing roughly 7 decimal digits of precision
  • The 7-bit exponent allows the range of -127 to 127 as powers of 2, i.e., numbers as small as 2-127 and as large as 2127

49

50 of 51

Floating-point types

  •  

50

51 of 51

That’s all for today!

51