CSE 101

CSE 101 · WEEK 05 · READING

Programming languages and introduction to C

How a description becomes an executing program—and why the values and operations you choose affect its answer.

Read the language concepts first, then predict the C examples before revealing the Check answers. The final two worked sections are optional extensions. This chapter supports Week 5 and Lab 4; it does not add a new assignment or require Arduino hardware this week.

1. Languages connect people and machines

An algorithm describes a method. A programming language gives us precise rules for expressing that method so tools can implement it. The processor still executes machine instructions, but a person can work with meaningful names, expressions and larger structures rather than manually selecting every register operation. This is abstraction: using a description that exposes what matters for a task while hiding details that another layer handles.

Consider a temperature conversion. We want to express a relationship between Fahrenheit and Celsius, give the quantities clear names and display a labelled result. The processor does not understand “temperature.” The translation tools must connect our chosen operations to instructions, memory and output facilities. The programmer remains responsible for choosing the correct relationship and units.

A historical view of increasing abstraction. Generations describe a direction in language design, not a score for how good a language is.
Figure 1. A historical view of increasing abstraction. Generations describe a direction in language design, not a score for how good a language is. CSE101 Week 5, slide 4. Includes a Brookshear textbook figure retained in the course slides. Source slide.

Machine language encodes instructions for a processor architecture. Assembly gives those operations readable names and labels. A high-level language lets us express a calculation without specifying its exact instruction sequence. One source statement can become many instructions; optimization can also combine or remove work. Counting source lines is therefore not the same as counting executed instructions.

Check: Does using a high-level language mean that machine instructions are no longer executed?

No. The compiler, interpreter and runtime connect the description to actual execution. Ultimately the physical processor executes machine instructions, even when the programmer works at a much higher level.

2. Different ways to describe a solution

A paradigm is a style of organizing computation. Imperative programming describes operations that change state. Functional programming emphasizes evaluating and combining functions. Object-oriented programming organizes data and behavior around objects. Declarative programming emphasizes a condition or relationship the result must satisfy. These approaches overlap: one language and even one application can use several.

Brookshear’s historical language-family chart, ending around 2000. It illustrates overlapping approaches rather than current popularity or a complete modern timeline.
Figure 2. Brookshear’s historical language-family chart, ending around 2000. It illustrates overlapping approaches rather than current popularity or a complete modern timeline. CSE101 Week 5, slide 7. Includes a Brookshear textbook figure retained in the course slides. Source slide.
One task: select readings above a limit
ApproachHow the solution is described
ImperativeVisit each reading; if it exceeds the limit, add it to the result.
FunctionalApply a filtering function to the collection.
Object-orientedAsk an object representing the readings for the qualifying values.
DeclarativeSpecify the condition that the returned readings must satisfy.

These are descriptions of approaches, not four syntaxes to memorize. Language choice also depends on the target machine, available libraries, development tools and the people who will maintain the program. A convenient numerical language and a small-device language may emphasize different needs without one being universally better.

Check: Can an object-oriented program contain imperative loops?

Yes. Objects organize responsibilities and data, while a method can use a loop to update state. Paradigms are not mutually exclusive boxes.

3. Syntax, meaning and the intended problem

Three questions should be kept separate. Syntax asks whether a phrase follows the language grammar. Semantics concerns what a valid phrase means, including the role of types. Problem correctness asks whether those operations produce the result we wanted. A translator can reject many invalid programs, but accepting a program does not prove that it solves the intended problem.

total = price + ;       // incomplete expression
total = price + tax;    // add tax
total = price - tax;    // valid form, different rule

The last line can be legal C when the names and types are appropriate. It is wrong for a requirement that says to add tax. Conversely, the first line cannot express a complete addition because an operand is missing. Different mistakes need different evidence: diagnostics for invalid language, and examples or reasoning for an incorrect rule.

A translator first recognizes tokens. In total = price + 12;, the names are identifiers, = and + are operators, 12 is one integer literal and ; ends the statement. A parser then recognizes how tokens fit the grammar. The characters alone are not yet a structured expression.

A parse tree groups x + y × z as x + (y × z). The multiplication belongs inside the right-hand expression.
Figure 3. A parse tree groups x + y × z as x + (y × z). The multiplication belongs inside the right-hand expression. CSE101 Week 5, slide 11. Includes a Brookshear textbook figure retained in the course slides. Source slide.

With x = 2, y = 3 and z = 4, that grouping gives 14. Writing (x + y) * z in C requests a different grouping and gives 20. Precedence describes grouping; it should not be confused with a general guarantee about the order in which every part of a C expression is evaluated.

Check: A program builds and prints a plausible temperature. What remains to be checked?

Whether it uses the intended formula, units, input assumptions and arithmetic types. A plausible value can still be wrong. Compare several independently known input/output pairs.

4. Translation, interpretation and execution

A simplified translator: lexical analysis recognizes tokens, parsing determines structure, and code generation produces a target representation.
Figure 4. A simplified translator: lexical analysis recognizes tokens, parsing determines structure, and code generation produces a target representation. CSE101 Week 5, slide 13. Includes a Brookshear textbook figure retained in the course slides. Source slide.

A compiler translates a program into another representation. That target may be machine code, bytecode or even another source language. Real compilers can use several intermediate representations and optimization passes; the figure is an introductory model, not a complete engineering diagram.

An interpreter is a running program that carries out operations described by another program representation. It tracks values and control flow and invokes the required actions. Saying that it runs “one source line at a time” is only a rough analogy: parsing and conversion into bytecode can occur before execution.

A deliberately small interpreter example
Toy operationInterpreter actionState or output
SET x 3Associate x with 3x = 3
ADD x 2Read x, add 2 and store the resultx = 5
PRINT xRead x and request output5

The table uses an invented notation, not C or a real processor instruction set. It separates the program being described from the program implementing its operations. The interpreter itself runs on the physical machine.

Two common execution paths. A runtime can interpret bytecode or compile it further; both routes eventually cause machine instructions to execute.
Figure 5. Two common execution paths. A runtime can interpret bytecode or compile it further; both routes eventually cause machine instructions to execute. CSE101 Week 5, slide 25. Source slide.

Bytecode targets an abstract execution model rather than a particular physical processor. A compatible runtime implements its operations. Just-in-time compilation (JIT) generates machine code during execution. Interpretation and compilation can therefore appear in the same system, at different stages. A virtual machine for bytecode need not be an emulated whole PC with its own desktop operating system.

Check: If source becomes bytecode, is the program necessarily “not interpreted”?

No. Compilation describes the translation to bytecode; interpretation can describe how that bytecode is executed. They answer questions about different stages.

5. Where is CPython when you run Python?

Python is the language; CPython is its usual reference implementation, principally written in C. If you installed the usual Python distribution, the executable called python or python3 is commonly CPython already. It is not normally a second application you must launch separately.

When you run python3 program.py with CPython, Python source is compiled to Python bytecode and the interpreter executes it. You normally do not perform a separate native-executable build. This is why describing Python as interpreted is compatible with its internal compilation step. Python’s FAQ and bytecode glossary entry describe these two views.

To identify an installation rather than guess from its version number, run this optional command. The result describes the implementation behind that command; it does not install or change Python.

python3 -c "import platform; print(platform.python_implementation())"

A normal CPython result is CPython. Other implementations exist. Bytecode and caching details depend on implementation and version, so a .pyc file is not a universal native executable. This chapter uses Python only to explain execution mechanisms; its programming examples remain C.

Check: You use Python 3.11 and have never installed anything labelled CPython. Is that inconsistent?

No. “3.11” identifies a language/runtime release series; the ordinary Python executable can already be the CPython implementation. The implementation query above gives direct evidence for the installation you are using.

6. A language survey: mechanisms can overlap

The tables below summarize the lecture survey for recognition, not memorization. The execution route belongs to a particular implementation. An interactive prompt does not prove that no compilation happens, and a compiled representation does not guarantee that it is native machine code. The application examples suggest common uses, not restrictions.

Common native-compilation routes
LanguagesExample useIllustrative route
C, C++, Rust, GoSystems and applicationsCompilers commonly produce native executables.
FortranScientific numerical workGNU Fortran is a native compiler.
COBOLBusiness recordsGnuCOBOL can translate to C and then build native code.
Pascal, AdaTeaching and dependable systemsFree Pascal and GNAT provide compiler examples.
Runtime and interactive implementations
LanguagesExample useIllustrative route
Java, C#Applications and servicesJVM bytecode or .NET IL; runtime execution often includes JIT compilation.
JavaScript, TypeScriptWeb applicationsV8 combines interpretation and compilation; TypeScript usually becomes JavaScript first.
Python, LuaAutomation and scriptingCPython and standard Lua use bytecode interpreters.
R, Julia, MATLABNumerical and analytical workR has bytecode compilation; Julia uses JIT compilation; MATLAB’s execution engine also includes JIT.
APLArray-oriented workDyalog supplies an interpreter; other implementations can differ.
Families and specialized languages
Language or familyWhat to notice
BASICGW-BASIC interprets; FreeBASIC compiles. The family name does not settle the mechanism.
ForthGforth has interpretation and compilation states; a definition can be compiled and then executed.
LispSBCL normally compiles Common Lisp to native code, including interactive evaluation.
SQLA database parses, plans and executes a query; some engines also compile parts of execution.
SAS languageDATA steps have compilation and execution phases; PROC steps invoke procedures.
SPSS command syntaxCommands request transformations and analyses from the SPSS runtime.

SAS and SPSS also name software products. Here the comparison concerns their languages or command syntax, not a claim that an entire statistical product is one interpreter. Concrete implementation references are provided in the expandable source list at the end.

Check: A colleague says “BASIC is interpreted, so BASIC programs can never be compiled.” What is missing?

The implementation. GW-BASIC and FreeBASIC illustrate different routes for the same language family. Similar care is needed for Lisp, Forth and other families.

7. Building, linking, loading and portability

A historical translation–linking–loading model. The file produced by translation is not necessarily ready to run by itself.
Figure 6. A historical translation–linking–loading model. The file produced by translation is not necessarily ready to run by itself. CSE101 Week 5, slide 27. Includes a Brookshear textbook figure retained in the course slides. Source slide.

In the PC C workflow, preprocessing handles directives such as #include; compilation translates C; assembly produces object code; and linking connects object files and required implementations. A compiler driver such as gcc can coordinate these stages. Loading is a later operating-system activity that prepares an executable for execution. These roles connect directly to Week 4’s discussion of programs and processes. See the GCC overview of stages.

A header provides declarations, such as the information needed to call printf. A library supplies reusable implementations. An IDE combines tools for editing, building and debugging; it is neither the programming language nor necessarily the compiler itself. A missing declaration and a missing implementation can therefore cause failures at different stages.

The same source-level idea may be built for different targets. Matching libraries, interfaces and language support are still required.
Figure 7. The same source-level idea may be built for different targets. Matching libraries, interfaces and language support are still required. CSE101 Week 5, slide 29. Source slide.

Source portability and executable compatibility are different. An executable has a target instruction set and operating-system conventions. Even recompiling source is not enough if it assumes unavailable libraries or different device interfaces. On Arduino, the PC builds firmware for a selected board and uploads it; the board then executes it. The sketch environment uses C++ and Arduino support libraries. The Arduino build-process documentation explains that preparation and toolchain.

Check: Could you copy a PC executable to an Arduino board and expect it to run because both programs were written with C-style syntax?

No. Syntax similarity does not establish instruction-set, executable-format, library or hardware compatibility. Build for the target and use its interfaces.

8. A first C program and a dependable work cycle

Conceptual illustration of a PC program beside a board. A temperature-looking display does not by itself show how data was measured or transferred.
Figure 8. Conceptual illustration of a PC program beside a board. A temperature-looking display does not by itself show how data was measured or transferred. CSE101 Week 5, slide 37. Source slide.

Start with a complete, small PC program. Save the following plain text as hello.c. The include directive makes standard input/output declarations available. main is the entry function for this hosted C program; braces group its statements. The string contains a newline escape, and returning zero reports successful completion to the host.

#include <stdio.h>

int main(void) {
    printf("Hello, C!\n");
    return 0;
}

In the course’s Unix-like terminal environment, build and then run with:

gcc -std=c17 -Wall -Wextra -Wconversion hello.c -o hello
./hello

The first command asks GCC to build an executable named hello, using C17 and useful warnings. The second requests execution from the current directory. Expected output is Hello, C! followed by a newline. Commands can differ in another toolchain or shell; use the course environment for these examples.

The dependable cycle is edit, save, build, inspect diagnostics, run. A failed build may leave yesterday’s executable in place. Running that file can make a correct source edit appear ineffective. Check build success before treating output as evidence about the new source.

Check: You change the greeting but see the old greeting again. Must printf be broken?

No. First check that you saved the intended file, built it successfully and ran the intended executable. An old executable may remain after an unsuccessful build.

9. Variables, assignment and types

int count = 3; declares an object named count, gives it type int and initializes its value. The assignment count = count + 1; reads the old value, computes a new one and stores it back. It is an update, not a mathematical assertion that a number equals itself plus one. Initialize a local variable before using its value.

Assignment copies the current value. After a becomes 9, b still holds the 5 copied earlier.
Figure 9. Assignment copies the current value. After a becomes 9, b still holds the 5 copied earlier. CSE101 Week 5, slide 45. Source slide.
int a = 5;
int b = a;
a = 9;
b = b + 2;
printf("%d %d\n", a, b);
Check: What does the assignment example print, and why does b not follow later changes to a?

It prints 9 7. The initialization copied 5 into b; it did not establish a permanent relationship. The later addition changes b from 5 to 7 independently.

Types used in this chapter
TypeExampleReason to use it
intint itemCount = 12;A whole-number count within its supported range.
doubledouble temperatureC = 23.5;A fractional calculation with finite precision.
floatfloat gain = 0.5f;Another floating-point type; supported precision and range matter.
charchar grade = 'A';A small integer type commonly used for character codes.

A type describes more than how many digits should be printed. Integer types have finite ranges; floating-point types have finite precision and range. C does not require every target to use a four-byte int. The optional measurement example on slide 71 uses sizeof and <limits.h> to inspect the actual implementation.

Do not rely on signed integer overflow wrapping around. Nor will a larger destination repair an expression that already overflowed while being evaluated. A character code is also a representation: on an ASCII-compatible target, 'A' has code 65; the language distinction between a character and its numeric presentation remains important.

Check: Does declaring a result as double guarantee that all arithmetic used to obtain it is floating-point?

No. The operands determine the arithmetic in the expression. Assignment converts the result afterward if necessary. The division examples next make this visible.

10. Expressions: where fractions disappear

C provides +, -, * and / for familiar arithmetic, and % for integer remainder. Parentheses state the intended grouping. For example, 2 + 3 * 4 is 14, while (2 + 3) * 4 is 20. Division and multiplication group from the left at the same precedence: 20 / 5 * 2 gives 8, while 20 / (5 * 2) gives 2.

The operand types decide the division
ExpressionResultExplanation
7 / 23Both operands are integers.
7.0 / 23.5A floating-point operand makes this floating-point division.
-7 / 2-3Integer division truncates toward zero.
7 % 21The integer remainder: 7 = 2 × 3 + 1.
double a = 7 / 2;
double b = 7.0 / 2;
printf("%.1f %.1f\n", a, b);

The output is 3.0 3.5. For a, C first computes integer 3, then converts it to double. The lost half cannot be recovered by assignment. For variables, (double)total / count converts before division, whereas (double)(total / count) converts after it. These examples assume a nonzero divisor. Division by zero is not a valid operation to use as a calculation strategy.

Check: With total = 17 and count = 5, what does double average = total / count store? How would you repair it?

It stores 3.0. Use double average = (double)total / count; to obtain approximately 3.4. Printing it with %.2f gives 3.40. Here count is known to be nonzero.

Check: With a = 9 and b = 4, predict a / b, (double)a / b and a % b.

They produce 2, 2.25 and 1 respectively. The first uses integer division; the second converts before division; the remainder satisfies 9 = 4 × 2 + 1.

11. From a formula to a tested calculation

The intended conversion is C = (F − 32) × 5 / 9. Name the input and output with their units. Before writing C, choose known examples: 32°F should give 0°C and 212°F should give 100°C. A negative example and a fractional example test other aspects of the calculation.

A straight-line algorithm: establish the Fahrenheit value, calculate Celsius, then print both quantities.
Figure 10. A straight-line algorithm: establish the Fahrenheit value, calculate Celsius, then print both quantities. CSE101 Week 5, slide 55. Source slide.

The flowchart separates the calculation from its presentation. Every arrow leads to the next operation; there is no branch or repetition in this version. In the rectangle, the decimal constants deliberately request floating-point arithmetic.

#include <stdio.h>

int main(void) {
    double temperatureF = 100.0;
    double temperatureC =
        (temperatureF - 32.0) * 5.0 / 9.0;
    printf("%.1f F = %.2f C\n",
           temperatureF, temperatureC);
    return 0;
}

Expected output: 100.0 F = 37.78 C. Contrast three expressions when F is an int holding 100:

Similar-looking formulas can compute different answers
ExpressionIntermediate reasoningResult
(5 / 9) * (F - 32)5 / 9 is integer 0; multiply by 680
5 * (F - 32) / 9340 / 9 uses integer division37
(F - 32.0) * 5.0 / 9.0Floating-point subtraction, multiplication and divisionAbout 37.7778
Independent expected results
FahrenheitCelsiusPurpose
320Offset
212100Scale
-40-40Negative input
100About 37.78Fractional result
5010Another familiar value
Check: Why is testing only F = 32 particularly weak for detecting the erroneous factor 5 / 9?

At F = 32, the factor multiplies zero. Both the correct expression and an expression with a zero scale factor can give zero. F = 212 or F = 100 exposes the error.

12. Output formatting and approximate numbers

The format string tells printf how to interpret each corresponding argument. %d expects an int; %f is used for a double argument; %.2f requests two fractional digits. A float passed to this variadic function is promoted to double. %c prints a character using an integer character code, and %zu is appropriate for the size_t result of sizeof. A mismatched format is not a request to convert the argument to the desired type.

Many decimal fractions cannot be stored exactly in binary floating-point. Printing more digits does not increase the accuracy of the original measurement. Printing fewer digits changes the displayed text, not the value stored in the variable. For the temperature exercise, compare the expected answers at the requested output precision.

double x = 37.7777777778;
printf("%.2f\n", x);
printf("%.6f\n", x);

These lines display 37.78 and 37.777778. A width such as %8.2f requests at least eight character positions, including the decimal point and fractional digits; it does not truncate a wider result. Spaces are useful for aligning a table.

Check: After printf("%.2f", x), has x been rounded and stored back with two decimal places?

No. The formatting produces a textual representation. It does not assign a new value to x.

13. Reading a supplied loop

A supplied loop lets us repeat the conversion while keeping the focus on arithmetic and output. Trace it using the control-flow ideas from Week 3. Initialization happens once; the condition is checked before each body execution; the update changes the state used by the next check.

The update returns to the test, not to initialization. The No branch ends the loop.
Figure 11. The update returns to the test, not to initialization. The No branch ends the loop. CSE101 Week 5, slide 61. Source slide.
#include <stdio.h>

int main(void) {
    int f = 0;
    while (f <= 100) {
        double c = (f - 32.0) * 5.0 / 9.0;
        printf("%3d %7.2f\n", f, c);
        f = f + 20;
    }
    return 0;
}
Full trace of the supplied repetition
f at testConditionPrinted Celsiusf after update
0True-17.7820
20True-6.6740
40True4.4460
60True15.5680
80True26.67100
100True37.78120
120FalseNo rowNo update

There are six rows and seven condition checks. The seventh check is necessary to establish that execution should stop. The update makes progress toward the stopping condition; resetting f to zero inside the body would destroy that progress. Independently designing richer C loops is later work, but explaining this supplied structure is part of the present reading.

Check: If the increment becomes 25, which Fahrenheit values are printed?

0, 25, 50, 75 and 100: five rows. The next value is 125, for which the condition is false.

Check: Would replacing f <= 100 with f < 100 change the original table?

Yes. With a step of 20, the row for 100 disappears. The last printed input becomes 80. A small change at a boundary changes the result.

14. Find evidence before changing code

Different stages expose different mistakes. A missing semicolon can produce a diagnostic at the next token, because that is where the compiler can no longer continue parsing. A link failure can mean that a declared function’s implementation was not supplied. A successful build with a wrong value calls for examining the formula, operand types and intermediate results. An unchanged old message calls for checking which executable actually ran.

A useful first check
Observed symptomEvidence to inspect
Compile errorThe first diagnostic and the preceding source line.
Link errorFunction spelling and the object/library inputs.
Wrong numeric resultOperand types, grouping, units and a hand calculation.
Old result after editingSaved filename, successful build and executable path.

For Week 5 practice, aim to explain a calculation rather than merely copy a working line. Give quantities meaningful names, predict a result, compile with warnings and compare several test cases. When asking for help, include the exact input, expected output, observed output and relevant diagnostic. That makes the difference between a requirement mistake and an execution mistake visible.

Check: A missing semicolon makes the compiler highlight printf on the next line. Should you delete printf?

Not automatically. Inspect the preceding line and the first diagnostic. The point where a parser notices a problem may be after the actual omission.

15. Optional worked example: minutes and seconds

Suppose a nonnegative whole-number duration is already stored as seconds. We want whole minutes and leftover seconds. The expected answer for 125 seconds is 2 minutes and 5 seconds. This is a case where discarding the fractional quotient is useful rather than a bug: division finds complete groups of 60, and remainder finds what is left.

Quotient and remainder produce two different outputs from the same nonnegative input.
Figure 12. Quotient and remainder produce two different outputs from the same nonnegative input. CSE101 Week 5, slide 78. Source slide.
#include <stdio.h>

int main(void) {
    int totalSeconds = 125;
    int wholeMinutes = totalSeconds / 60;
    int remainingSeconds = totalSeconds % 60;
    printf("%d min %d s\n",
           wholeMinutes, remainingSeconds);
    return 0;
}

For the stated input range, totalSeconds = 60 * wholeMinutes + remainingSeconds, with a remainder between 0 and 59. This relationship explains the design and supplies a check. It does not silently extend the intended meaning to negative durations.

Check: Predict the results at 59, 60 and 61 seconds. Why choose those inputs?

They give 0 min 59 s, 1 min 0 s and 1 min 1 s. They surround the boundary where a complete minute first appears, so they test both the quotient change and the remainder reset.

16. Optional preview: a calculation controls time

An Arduino sketch uses C++ with Arduino libraries and a board-specific build. The familiar ideas of variables, assignments and expressions remain useful, but the framework provides entry points different from the PC example. After reset, setup() performs initialization once; the framework repeatedly calls loop(). This is a preview, not a new Week 5 hardware task.

Initialization happens once after reset. The four actions in loop repeat: set HIGH, wait, set LOW, wait.
Figure 13. Initialization happens once after reset. The four actions in loop repeat: set HIGH, wait, set LOW, wait. CSE101 Week 5, slide 87. Source slide.

On a board with an active-high built-in LED, this alternates approximately half a second on and half a second off. LED_BUILTIN is supplied by the selected board definition; LED behavior can be board-dependent. The nominal cycle is 1,000 ms, with small execution overhead. delay pauses normal sketch execution while waiting; richer timing designs come later. See Arduino’s Blink example.

Check: If the on time becomes 100 ms and the off time 900 ms, is the nominal period different?

No. It is still 1,000 ms, but the active-high LED flashes for a smaller fraction of the cycle. Changing both times to 1,000 ms gives a nominal 2,000 ms period.

17. Sources and further study

The 91-page Week 5 slide set supplies the course sequence and worked examples. The reading expands the explanations into a chapter; its numbered figures are extracted images, not screenshots with slide headers. Figures 1–4 and 6 are retained Brookshear illustrations from the supplied seventh-edition chapter slides. The course’s twelfth Global Edition book places programming languages in Chapter 6, particularly sections 6.1 and 6.4; chapter numbering differs across editions. Figures 5 and 7–13 are the authored conceptual illustrations and flowcharts already used in the approved course deck, including AI-generated visuals. They are teaching models, not evidence of a measured experiment.

Choose a resource for a question you still have. For example: explain the difference between an object file and an executable using GCC’s stages; explain where CPython fits when running a script; or predict a changed temperature calculation before running it. Further study should deepen the mechanism rather than turn the language survey into a list of labels to memorize.

Implementation references for the language survey

The survey retains the implementation examples in the reviewed lecture; these links provide the corresponding primary documentation.

Search courses