0% found this document useful (0 votes)
18 views

Intro To C - Module 1

Uploaded by

Andrew Fu
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
18 views

Intro To C - Module 1

Uploaded by

Andrew Fu
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 6

Introduction to C: Module 1 (August 19, 2024)

Weekly Reading:

● Beej Guide, Chapter 2,


● "Should You Learn C To Learn How the Computer Works?" by Steve Klabnik.

Why Learn C?

You will almost certainly encounter C if you ever:


● do any systems programming.
● need to write optimized code.
● want to truly know why C++, Java, and even Python are the way that they are.
● have to debug a low-level issue.
● study operating systems, high-performance computing, or computer architecture.

C is far from perfect, and we will explore many of its weaknesses here, but it's been the lingua
franca for systems programming for four decades, and that's unlikely to change any time soon.
There are wide swaths of computer science that require a working knowledge of the language.

You may have heard that Python “is slow” and that C “is fast.” Well... sort of. There are dozens
of factors that will determine your software’s performance. What low-level languages, and
especially C, give you is control. You decide when memory is allocated and when it is freed from
the program. You decide what data structures to use—you can build them from scratch—and
you can lay them out in memory to improve cache utilization. You decide when to use the stack,
and when to use the heap. When you need the kind of control that high-performance code
requires, it's hard to beat C.

But is it a warty and sometimes difficult language? Also yes. The good news is that it's not a
"big" language. Within the first half of this course, you should have enough intuition to be able to
build, in C, anything you've written in a higher-level language. The second half will focus on
techniques and patterns, so you recognize them and know what a piece of code's authors
intended to do.

What This Course Is Not

This is not a C++ course. C++ is a radically different language with so many features (some
ferociously ill-considered) that it would take multiple semesters to explore it all.

This is not a systems programming course—if the field interests you, consider taking CS 6200
(“Graduate Introduction to Operating Systems”) and CS 6210 (“Advanced Operating Systems”).

This is not a software engineer course. In fact, we'll see some features that should rarely, if ever,
be used.
This is not an introductory programming course; it assumes familiarity with a higher-level
language such as Java or Python.

This course is designed for intermediate or advanced programmers to get them versed well
enough in C that, when they take courses using it in the future, the language itself is not a
stumbling block.

Danger! C Assumes You Know What You're Doing

... and this should probably never be assumed about anyone, ever. The fact that you're
voluntarily taking a course on a hard programming language means that you're probably in the
top 10% of programmers. But you create bugs. I create bugs. We all do.

C, by design, is so close to the hardware that familiar protective abstractions are not in place. C
lets you do things that are weird and unsafe that higher-level languages would never allow.

Let me be concrete. If you're working in Python and have a list—or, in Java, an array—called b
of length 10, and you try to write to b[137], what happens? You get an exception, and if it's
uncaught, the program crashes. This is, in event of a bad index, what you want. C, on the other
hand, will let you write to the memory address that b[137] would have, if it existed. Why? Trust
me, we'll explore that later. What happens when you read from, or write to, this b[137]? It's
anyone's guess.

It could crash the program immediately—that would usually be the happiest ending, because
you can debug and fix it. It could corrupt memory that is used elsewhere—in the bad old days
before memory protection, this sort of thing could take out a whole system; today, you'll only
lose your program. Or, you could entirely get away with it, because it so happens that this bad
address is legal and unused—the cost being a fragile program that depends on a bug.

Java and Python have decided that you must check your bounds; they do it for you, and throw
exceptions on out-of-bounds access attempts. C does not. If you want it done, write it yourself.
Any time you're unsure whether your index is in bounds, you must check it. But consider the two
programs below:

int LENGTH = 100000000;


int a[LENGTH];
for (int i = 0; i < LENGTH; i++) {
a[i] = i;
}

versus...

int LENGTH = 100000000;


int a[LENGTH];
for (int i = 0; i < LENGTH; i++) {
if (i >= 0 && i < LENGTH) {
a[i] = i;
}
}

The second version is inefficient. We know how i is initialized, updated, and checked, so we
know it will always be in bounds. Thus, bounds checks are redundant. The first version is
completely safe; the second one does extra work we don't need.

On the other hand, if you find yourself later needing random access into the array—for example,
a[j] where j is determined by user behavior and could easily be out of bounds, then check it.

Oh, and never trust users. We'll see in Module 6 how C's laissez-faire attitude toward memory
access has been used for great evil in the past.

C lets you decide. If you want to write faster code when you know you can safely skip bounds
checks, do so. If you know your indices might be out of bounds and want to check manually, you
can—and must, if you want a working program. Programming is full of tradeoffs—performance
vs. safety is only one of them—and C allows you to decide where on each spectrum you want to
be.

Compile and Run Time

C is a compiled language. Though interpreted languages like Python and Clojure use
compilation to achieve performance, there is a difference in philosophy. An interpreted language
treats your code itself as a live object, and this can make for an excellent interactive
environment (or, in terms inherited from Lisp, read-eval-print-loop, also known as REPL.) C
doesn't have that, and there's a good reason for it: a C compiler's job is not to implement your
program as you have written it, but to come up with the best implementation it can that is faithful
to your program. This is a subtle, but important, distinction. Let's say you've written a squaring
function:

int square(int x) {
return x * x;
}

and somewhere later you invoke it:

...
int b = square(17);
...
The compiler, when it generates executable assembly code, has the right to skip your square
function altogether; to remove the cost of a function call, it could inline it:

...
int b = 17 * 17;
...

and then it could replace the constant expression with its result:

...
int b = 289;
...

The compiler may therefore improve your program so that your square function never exists at
all. In the context of an interactive runtime, for contrast, a contractual obligation to preserve
such code entities exists, making these sorts of optimizations impractical. This is one reason
why programs in interpreted languages tend to be slower. In C, the compiler might transform
your code into something unrecognizable, but with better performance.

To achieve interactivity, or to probe language features—you'll be doing a lot of this—you will


have to get comfortable with the compiler. Start with small programs.

Some Advice

● Compile often. Make incremental changes. Small change, compile; small change,
compile. Code that usefully does something is closer to your target than code that
doesn't compile.
● Start at the first compilation error. You'll often get a list of a dozen errors when you
compile. As a general principle, focus on the first one—the one that threw the compiler
off. The rest of the code may be correct.
● Treat warnings as errors. If the compiler says, "You shouldn't do this," it is usually right
and it has probably recognized the cause of a runtime crash—or, worse, undefined
behavior. Aim for a program that compiles without warnings.
● printf is your friend. We may have time this semester to discuss more powerful
debuggers, such as gdb, but the class project can be tackled with printf-debugging
alone. Use it.
● Test early and often. The compiler will check some of your errors, but not all of them.

The Project

This course is project-based—you will be building larger and larger C projects, up to an


interpreter for a bare-bones Lisp. This project's purpose is to give you intuition for the sorts of
things that, when you use a higher-level language like Python, the runtime does to support you.
Why is dynamic typing slow? What is garbage collection and why is it important? You'll learn all
this. We'll build up to this task, using the skills and knowledge taught each week, and I'll share
lessons learned from the best implementations.

This, for a 1-credit course, is an ambitious project, and this is the first time the course is being
offered. So, it's possible that this will be more difficult than I anticipated. If you can't get all the
features to work, please let me know what you had trouble with. Communicate frequently,
because if you're struggling with something, a lot of people are, too.

Grading

This course is pass/fail and it is not designed to be stressful. So long as you turn something in
to show evidence of effort and engagement, you will pass.

Office Hours

We will meet on Mondays at 8:00pm Eastern time. Attendance is not mandatory, and office
hours will be recorded. Platform is TBD, but probably Zoom.

Textbook

We’ll be referring to Beej’s guide to C Programming, available for free here. Only free resources
will be used for the course.

Getting Started

You can use any operating system for this course. However, we will be assuming a Linux or
Linux-like command-line environment. C is a compiled language, and you will be compiling and
running code at the command line, as well as writing (simple) Makefiles later on. You can use
any text editor; I use emacs.

Module 1 Question

1.1: What do you hope to learn from this course? What skills do you seek to acquire?

Module 1 Project

Your objective this week is to write a program—any program—and compile, then run it. It can
print "Hello, world" to the console. It can add 27 to 42 and print that. It can count the number
of command line arguments, and return that. Play around, explore.

1.2: What is your program? How do you know it works?

1.3: What operating system did you use? What command did you use to compile? What
command did you use to run your code?
Writeup (Due August 29)

Please submit your answers to 1.1, 1.2, and 1.3 by PDF in Canvas. Your answers should fit
within one page.

You might also like