C Module 0: Introduction
Create your repository: https://classroom.github.com/a/0JA3zMle
Then edit the README.md file in the GitHub web editor.
Due: Tuesday 9/4 at 11:59PM, see submission instructions.
Updates:
- 8/30/18: Added submissions instructions. Clarified Exercise 0.22
Objectives
The goal of this module is:
- To introduce the syntax of basic C: variables, assignment, operators, and control flow.
History
A brief history of C:
- Developed in 1972 at AT&T Bell Labs by Dennis Ritchie and Brian Kernighan.
- Primary purpose: provide a higher-level programming language than assembly in which to write Unix.
- Why the name C?
- Classic book: "The C Programming Language" by Kernighan and Ritchie.
- Since then:
- C has gone through many changes.
- Original C is called "K&R C".
- ANSI defined a standard in 1989, now called ANSI C89.
- Most common: update in 1999 called ANSI C99.
- Current: 2011, called ANSI C11.
- See The Development of the C Language, a history of C written by Dennis Ritchie.
Why learn C?
Highlights of C:
- C has many high-level programming features, but also let's you work at the lowest level (manipulating bits).
- C is amenable to compiler optimization: compiled C code is generally very efficient.
- C gives the programmer a lot of freedom in accessing memory and hardware.
- Most operating systems, embedded systems are written in C.
- A lot of open-source projects are written in C.
The world of C:
- Software development in C usually involves using additional C-related tools.
- Tools for programming:
- Development environments, e.g., Eclipse, Emacs
- Debuggers, e.g. gdb
- Useful tools/libraries, e.g., lex and yacc.
- Tools for managing projects, e.g., make, ant.
- Tools for configuring software, e.g., autoconf.
- Tools for GUI development: tk/tcl, QT, GTK.
- Scripting languages: perl, awk.
Open-source software:
- GNU-Linux is written in C, as are most tools from GNU.
- Most open-source projects today are written in C
- see
www.sourceforge.net - Open-source projects tend to use the gcc compiler (itself an open-source project) and related tools.
Going from Java to C
For a Java programmer:
- C's syntax is similar.
- Large parts of C will appear to be a subset of Java.
- But ...
- C has pointers, with a lot of freedom to manipulate them (direct memory access).
- C has less type-checking than Java.
- C's variable declarations can look strange.
- C provides no support for objects.
- C has function declarations.
- C has a preprocessor language that's processed before compilation.
- The programming experience in C:
- Because of the additional freedom in the language, it's much easier to create errors.
- There's less help from the compiler, and no help from the runtime system.
- You need to use a debugger to track pointer errors.
- C's freedom in programming allows one to construct some
highly unreadable code. For example, here is one winning entry
from the International Obfuscated C Code Contest:
int i;main(){for(;i["] < i;++i){--i;}"];read('-'-'-',i+++"hell\ o, world!\n",'/'/'/'));}read(j,i,p){write(j/p+p,i---j,i/i);}
(It prints "Hello world!" to the screen).
HelloWorld in C
Here is the classic HelloWorld program in C:
#include <stdio.h> int main () { printf ("Hello World!\n"); }
Note:
- The #include command is a preprocessor directive to load the stdio library.
- Execution starts in a function (method) called main:
- There are other signatures for main as we will see.
- Although a return type is declared, nothing needs to be returned.
- The printf method is used for screen output.
- The "newline" character \n is explicitly required.
Exercise 0.1: Use the editor below to write your first C program mimicking the one above, but with a more creative message. Press the Play button to compile and execute the code. Paste your code into your readme in a code block.
C-related languages
Languages related to C:
- C++
- C++ is "mostly" a superset of C, offering support for objects.
- gcc can compile C++ as well - this is useful for projects that combine C and C++.
- Objective C
- Another C enhancement to support objects.
- Popular on Apple/MAC platforms.
- C#
- Microsoft's answer to Java.
- Many other experimental variations: TinyC, Cyclone, D.
Reserved words and identifiers
C's 37 reserved words:
Identifiers in C:
Open this link:
https://repl.it/@twood02/EmptyC
in a new tab/window for the next set of exercises.
About C compiler errors:
Let's revisit helloworld and point out some syntactic features:
Next, let us get accustomed to errors produced by the compiler
when the syntax is incorrect.
There are two types of comments in ANSI C99:
auto enum restrict unsigned
break extern return void
case float short volatile
char for signed while
const goto sizeof _Bool
continue if static _Complex
default inline struct _Imaginary
do int switch
double long typedef
else register union
Note:
int myAge;
is different from
int MyAge;
int abcdefghijklmnopqrstuvwxyz12345;
and
int abcdefghijklmnopqrstuvwxyz12346;
but compilers are not guaranteed to distinguish between
int abcdefghijklmnopqrstuvwxyz123456;
and
int abcdefghijklmnopqrstuvwxyz123457;
num_courses_taken = compute_num_courses_taken (student_ID);
numCoursesTaken = computeNumCoursesTaken (studentID);
for (i=0; i < 10; i++) {
A[i] = B[i] * C[i];
}
Compilation and basic compiler errors
#include <stdio.h>
Int main ()
{
}
This is because the C reserved word int is mis-spelt as
Int.
Exercise 0.2
Type up and compile the above program.
What is the compiler error reported?
Exercise 0.3
What is the error produced by compiling this program?
#include <stdio.h>
int Main ()
{
}
Exercise 0.4
What is the error produced by compiling this program?
#include <stdio.h>
int main ()
{
Printf ("Hello World!\n");
}
#include <stdio.h>
int main ()
{
printf ("Hello World!\n");
}
Exercise 0.5
What errors are reported by the compiler with this program?
#include <stdio.h>
int main ( // Forgot the matching right parenthesis
{
printf ("Hello World!\n");
}
Exercise 0.6
What errors are reported by the compiler with this program?
#include <stdio.h>
int main ()
{
printf ("Hello World!\n); // Forgot the matching right quote (")
}
Exercise 0.7
What errors are reported by the compiler with this program?
#include <stdio.h>
int main ()
{
printf ("Hello World!\n") // Forgot the semi-colon.
}
Exercise 0.8
What errors are reported by the compiler with this program?
#include <stdio.h>
int main ()
{
printf ("Hello World!\n");
// Missing brace
Exercise 0.9
What errors are reported by the compiler with this program?
#include <stdio.h>
main ()
{
printf ("Hello World!\n");
}
Exercise 0.10:
What kind of a compiler error do you get if you try to use
a reserved word as an identifier? Use a reserved word to
declare an integer identifier.
Comments
to the end of the line.
#include <stdio.h> /* This is a block comment. Everything between the begin-comment symbol and the end-comment symbol is ignored by the compiler. It is conventional to use a separate line for the end-comment symbol. */ // This is an in-line comment. int main (/* A strange place for a block comment */) { printf ("Hello World!\n"); // Another inline comment. }
Include's
The first line, and perhaps the next few, typically have #include's.
- For our
helloworld.c example,
we had
#include <stdio.h>
- We will later understand how to work with multiple files and libraries, but for now think of this as including some library/API functionality that we can use.
- The typical
#include's
we will need are:
#include <stdio.h> #include <math.h> #include <stdlib.h> #include <string.h>
- Important:
- To avoid clutter, our examples will not necessarily have these #include's.
- When you write your code, make sure to have these "include" lines be the first lines in your program.
- Thus, for example, we might describe our
helloworld.c example
as
int main () { printf ("Hello World!\n"); }
with the understanding that the actual file will look like#include <stdio.h> #include <math.h> #include <stdlib.h> #include <string.h> int main () { printf ("Hello World!\n"); }
Data types
About C's data types:
- There are basic types: int, char, float, double, void.
- There are type modifiers: const, short, long, signed, unsigned.
- C99 has additional types: _Bool, _Complex, _Imaginary.
- There are two complexities with regard to C's types:
- The type rules are somewhat complicated.
- Variable declarations can be made complicated (if desired).
- Storage sizes are not standard: for example an int can
be either:
- 2 bytes: -32,768 to 32,767
- 4 bytes: -2,147,483,648 to 2,147,483,647
Integer types (ANSI C99 additions shown in bold):
Type | Typical storage size | Range | Print specifier |
int | 2 or 4 bytes | -32,768 to 32,767 (2 bytes)
-2,147,483,648 to 2,147,483,647 (4 bytes) |
%d |
unsigned int | 2 or 4 bytes | 0 to
65,535 (2 bytes) 0 to 4,294,967,295 (4 bytes) |
%u |
short | 2 bytes | -32,768 to 32,767 | %d |
unsigned short | 2 bytes | 0 to 65,535 | %u |
long | 4 bytes | -2,147,483,648 to 2,147,483,647 | %ld |
unsigned long | 4 bytes | 0 to 4,294,967,295 | %lu |
long long | 8 bytes | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | %lld |
unsigned long long | 8 bytes | 0 to 18,446,744,073,709,551,615 | %llu |
Let's look at an example:
int main () { int numDaysInYear = 365; long int numStarsInUniverse = 2000000000L; unsigned long long int largestIntegerInC = 18446744073709551615LLU; printf ("numDaysInYear = %d\n", numDaysInYear); printf ("numStarsInUniverse = %ld\n", numStarsInUniverse); printf ("largestIntegerInC = %llu\n", largestIntegerInC); }
Exercise 0.11: Implement the above in your repl.it editor. What happens if you omit the "L" or "LLU" when declaring numStarsInUniverse and largestIntegerInC? What happens if you use %d for the largestIntegerInC print statement?
Note:
- Where to declare variables:
- Prior to C99, all variables had to be declared either at the top of a function, or globally (outside any function).
- Since ANSI C99, variables can also be declared inside
blocks, as in:
int main () { int numDaysInYear = 365; printf ("numDaysInYear = %d\n", numDaysInYear); if (numDaysInYear < 366) { long int numStarsInUniverse = 2000000000L; printf ("Thank your lucky %ld stars\n", numStarsInUniverse); } else { unsigned long long int numStarsInUniverse = 1844674407370955161ULL; printf ("Thank your lucky %llu stars\n", numStarsInUniverse); } }
- Two commonly used declarations in other languages (Java,
C++) were NOT permitted in older versions of C:
- Declaration in a for-loop:
int main () { int i; for (i=0; i<10; i++) { // Allowed. printf ("i=%d\n", i); } for (int j=0; j<10; j++) { // Not allowed in C89. j needs to be declared at the top. printf ("j=%d\n", j); } }
- Declare as needed:
int main () { int numDaysInYear = 365; // Allowed. printf ("numDaysInYear = %d\n", numDaysInYear); long int numStarsInUniverse = 2000000000L; // Not allowed. printf ("Number of stars = %ld stars\n", numStarsInUniverse); }
- Declaration in a for-loop:
- To make matters confusing, some compilers will allow both types of declarations (for-loop, as-needed) above.
- To be safe, you can follow ANSI C89 and always declare every needed variable at the top of the method or as a global.
- Variables can be initialized and declared in one statement,
or declared and initialized separately:
#include <stdio.h> // Declaration and assignment. int numDaysInYear = 365; int main () { // Declaration. long int numStarsInUniverse; printf ("numDaysInYear = %d\n", numDaysInYear); // Assignment. numStarsInUniverse = 2000000000L; printf ("numStarsInUniverse = %ld\n", numStarsInUniverse); }
For variety, we have declared numDaysInYear as a global variable. - ANSI C99 has added the long long type, not available in C89 or earlier.
- The int reserved word may be left out if modifiers
are present:
int main () { int numDaysInYear = 365; long numStarsInUniverse = 2000000000L; unsigned long long largestIntegerInC = 18446744073709551615ULL; printf ("numDaysInYear = %d\n", numDaysInYear); printf ("numStarsInUniverse = %ld\n", numStarsInUniverse); printf ("largestIntegerInC = %llu\n", largestIntegerInC); }
- The letter L is appended to the end of a long constant.
- The letter LL is appended to the end of a long long constant.
- The letter U is appended to the end of an unsigned constant, e.g., LLU.
Screen output: (HW)
- The printf library function is used for output to the screen.
- The arguments to printf are:
printf (format-string [zero or more variables])
- The format string needs to specify all variables that follow
the format string:
- The idea is, printf will look at additional arguments only based on what's in the format string.
- In the first printf above, the %d symbol specifies an integer.
- Similarly, %ld specifies a long and %llu specifies an unsigned long long int.
- The format string can specify any number of variables, separated
by arbitrary text, for example:
int main () { int numDaysInYear = 365; long int numStarsInUniverse = 2000000000L; unsigned long long int largestIntegerInC = 18446744073709551615ULL; printf ("numDaysInYear = %d numStarsInUniverse = %ld largestIntegerInC = %llu\n", numDaysInYear, numStarsInUniverse, largestIntegerInC); }
- The format string can also specify the amount of space to reserve for the printed number by using %Nd, where N is a number indicating how much space to use:
HW Exercise 0.12 Adjust the amount of space used in the printf call. What happens if x has more digits than are reserved? What happens if it has fewer digits?
Floating-point types:
Type | Typical storage size | Range | Print specifier | Approximate precision |
float | 4 bytes | 1.2 x 10-38 to 3.4 x 1038 | %f | 6 decimal places |
double | 8 bytes | 2.3 x 10-308 to 1.7 x 10308 | %lf | 15 decimal places |
long double | 10 bytes | 3.4 x 10-4932 to 1.1 x 104932 | %LG | 19 decimal places |
Consider this example:
Exercise 0.13: What print command would you use to print PI with a reserved width of 10 characters and only one digit after the decimal place? Does the reserved width include the decimal point and "e+" notation characters?Note:
- The letter F is appended to the end of a "decimal"
type constant:
float PI = 3.141F;
- Floating point constants can also be specified in exponent
format:
double doublePI = 314.159265E-2;
- The letter L is used for a long double constant.
- The print format string specifies both the total width of the field and the number of digits that follow the decimal point.
- Use %e in the format string for exponent format.
HW Exercise 0.14: It's a common error to mistype the format string. What happens when you reverse the %d and %f specifiers in the program above?
Character types:
Type | Typical storage size | Range | Print specifier |
char | 1 byte | -128 to 127 | %c |
unsigned char | 1 byte | 0 to 255 | none |
signed char | 1 byte | -128 to 127 | none |
Consider this example:
int main () { char letter = 'a'; unsigned char letter2 = 'b'; signed char letter3 = 'c'; printf ("letter = %c\n", letter); printf ("letter2 = %c\n", letter2); printf ("letter3 = %c\n", letter3); }Note:
- Generally pronounced "char" (like charred meat) or "car"
- Characters are used to represent either letters or small numbers. The letter symbols you are used to are converted from numbers using the ASCII standard.
- When assigning to a letter, you must use single quotes around the letter.
- There is no special print-format specifier for signed and unsigned char.
- char variables are also used to access memory on a byte-by-byte basis, as we will see shortly.
HW Exercise 0.15 What happens if you use double quotes when assigning a char? What happens if you put multiple letters within single quotes?
HW Exercise 0.16 Use the ASCII table above to assign c1 and c2 to numeric values that represent your initials. Then print them using both the %d and %c format specifiers.
Casting
Consider this example:
int main () { int i = 5; long j = 6; double d = 3.141; j = i; // Works fine. Implicit cast from int to long. d = i; // Works fine. Implicit cast from int to double i = j; // May not compile. i = d; // May not compile. d = 3.141; i = (int) j; // Compiles. Explicit cast from long to int. i = (int) d; // Compiles. Explicit cast from double to int. // Cast's can be used in any expression: printf ("The int part of d=%lf is %d\n", d, (int) d); }
Note:
- To assign a variable higher in the cast hierarchy to one lower, an explicit cast is required.
- An explicit cast is the desired type in parentheses placed
before the target of the cast:
i = (int) d;
- Many compilers don't warn you if you are "down" casting: this is a common source of error.
- The implicit cast hierarchy is:
short → int → unsigned int → long → unsigned long → long long → float → double → long double - Use an explicit cast in any assignment down the hierarchy.
Exercise 0.17: Use the editor below to find out what this program will print out.int main () { printf ("%lf\n", ( (double) (int) 3.141 ) ); }
Operators and expressions
An example with arithmetic operators
int main () { double x = 6, y = 5; int i = 8, j = 5; // Standard: plus, minus, multiple, divide printf ("x+y=%lf\n", x+y); // Prints 11.0 printf ("x-y=%lf\n", x-y); // Prints 1.0 printf ("x*y=%lf\n", x*y); // Prints 30.0 printf ("x/y=%lf\n", x/y); // Prints 1.2 // Integer divide and remainder: printf ("i/j=%d\n", i/j); // Prints 1 printf ("i mod j=%d\n", i%j); // Prints 3 // Post and pre-increment: printf ("i++ = %d\n", i++); // Prints 8 printf ("++j = %d\n", ++j); // Prints 6 // Assignment shortcut example. i += j; printf ("i=%d\n", i); // Prints 15 }
An example with bitwise operators
int main () { int a = 9; int b = 4; printf ("a&b = %d\n", a&b); // Bitwise AND: Prints 0 printf ("a|b = %d\n", a|b); // Bitwise OR: Prints 13 printf ("a^b = %d\n", a^b); // Bitwise EOR: Prints 13 printf ("~a = %d\n", ~a); // Complement: Prints -10 printf ("b << 1 = %d\n", (b << 1)); // Left shift by 1: Prints 8 printf ("b >> 2 = %d\n", (b >> 2)); // Right shift by 2: Prints 1 }
An example with boolean operators
int main () { double x = 5, y = 6, z = 6; int result; printf ("x < y = %d\n", (x < y)); // Prints 1 (true) printf ("x <= y = %d\n", (x <= y)); // Prints 1 printf ("y > z = %d\n", (y > z)); // Prints 0 (false) printf ("y >= z = %d\n", (y >= z)); // Prints 1 printf ("x == y = %d\n", (x == y)); // Prints 0 printf ("y == z = %d\n", (y == z)); // Prints 1 printf ("x != y = %d\n", (x != y)); // Prints 1 result = (x < y); printf ("result=%d\n", result); // Prints 1 if (result == 1) { // Equality comparison. printf ("x < y\n"); } else { printf ("x >= y\n"); } // Prints "x < y" }
Note:
- In C, boolean operators result in 1 (indicating "true") or 0 (indicating "false").
- These results can be assigned to int-compatible variables.
Boolean types: (HW)
- C originally did not provide any boolean types.
- There are now four ways of working with boolean's:
- Use the original approach of 1 and 0:
int main () { double x = 5, y = 6; int result = (x < y); if (result == 1) { printf ("x < y\n"); } else { printf ("x >= y\n"); } }
-
Define your own boolean types using the pre-processor:
#define boolean int #define true 1 #define false 0 int main () { double x = 5, y = 6; boolean result = (x < y); if (result == true) { printf ("x < y\n"); } else { printf ("x >= y\n"); } }
- Use a recent addition to the standard C library:
#include <stdio.h> #include <stdbool.h> int main () { double x = 5, y = 6; bool result = (x < y); if (result == true) { printf ("x < y\n"); } else { printf ("x >= y\n"); } }
-
Use the new C99 _Bool type:
#include <stdio.h> #include <stdbool.h> int main () { double x = 5, y = 6; _Bool result = (x < y); if (result == true) { printf ("x < y\n"); } else { printf ("x >= y\n"); } }
- Use the original approach of 1 and 0:
- We recommend using the stdbool package, as shown in (3) above.
HW Exercise 0.18 : Put the following code into the editor below. What does it print out? What is its significance?int main () { int k = 13; int i; for (i=0; i < 8; i++) { printf ("%d ", (k % 2)); k >>= 1; } printf ("%\n"); }
Constants (HW)
There are two ways of defining constants in C:
- The simple and traditional way is to use the preprocessor:
#define PI 3.14159 int main () { double radius = 5.0; printf ("Area of Circle with radius %lf is %lf\n", radius, PI*radius*radius); }
- Another way is to use the const modifier that makes a
variable immutable:
const double PI = 3.14159; int main () { double radius = 5.0; printf ("Area of Circle with radius %lf is %lf\n", radius, PI*radius*radius); }
The latter approach is more general, and can be applied to function parameters as well:
HW Exercise 0.19 What happens when you try to modify one of the const variables? Is this a compile-time or a run-time error?
Control flow
An overview of C's control flow statements via examples:
int main () { int i = 1; // if-statement: if (i == 0) { printf ("i is zero\n"); } // if-statement with compound expression: if ( (i >= -1) && (i <= 1) ) { printf ("-1 <= i <= 1\n"); } // if-else combination if (i == 1) { printf ("one\n"); } else if (i == 2) { printf ("two\n"); } else { printf ("larger than two\n"); } // Variation of if-else above: if (i == 1) { printf ("one\n"); } else { if (i == 2) { printf ("two\n"); } else { printf ("larger than two\n"); } } // Equivalent switch statement: switch (i) { case 1: { printf ("one\n"); break; } case 2: { printf ("two\n"); break; } default: { printf ("larger than two\n"); } } // for-loop example: printf ("Numbers 0 through 9: \n"); for (i=0; i < 10; i++) { printf (" %d\n", i); } // while-loop equivalent: printf ("Numbers 0 through 9: \n"); i = 0; while (i < 10) { printf (" %d\n", i); i++; } // do-while equivalent: printf ("Numbers 0 through 9: \n"); i = 0; do { printf (" %d\n", i); i++; } while (i < 10); // Example of using "break" printf ("Numbers 0 through 9: \n"); i = 0; while (1) { if (i == 10) { break; } printf (" %d\n", i); i++; } printf ("Odd numbers less than 10:\n"); i = 0; while (1) { // If we've reached the limit, break out of the loop. if (i == 10) { break; } // If it's an even number, skip to next iteration of loop. if (i % 2 == 0) { i++; continue; } // Print odd number. printf (" %d\n", i); i++; } }
Note:
- Between a for-loop and a while-loop, use a while-loop only if the while-loop is easier to write.
- Similarly, prefer a while-loop over an equivalent do-while.
- ANSI C99 allows declaration of the for-loop variable in
the for-loop:
for (int i=0; i < 10; i++) { // ... }
Versions prior to C99 do not. For compatibility with many textbooks, we will declare variables at the top.
Exercise 0.20 : There is a bug in this code! Why won't it print "i is not 5" like the developer expected? Is this a compile-time or run-time error?int main(void) { int i = 10; if (i = 5) printf ("i is equal to 5"); else printf("i is not 5"); return 0; }
Exercise 0.21 : Without running it, what is the error in the following code?int i = 10; while (i >= 1) printf ("i=%d\n", i); i--;
Now for some practice with control loops...
Use this code window to solve the exercises below.
HW Exercise 0.22 : Use for-loops to print "hourglasses" of different sizes. For example, here is the size=3 glass:X------------X X--------X X----X XX X----X X--------X X------------Xand here is the size=5 one:X--------------------X X----------------X X------------X X--------X X----X XX X----X X--------X X------------X X----------------X X--------------------XThen, repeat using only while-loops. Paste your code for both versions into your README file.
Submission Instructions
Do your work in a repository created using this link.
- Answer all of the exercise questions above in your README.md file.
- We suggest you edit the file directly in the GitHub web editor, but alternatively you can clone the repository to your computer or CodeAnywhere.
- Submit your assignment by committing your README.md file to your git repository (and pushing to GitHub if you are not editing through its website).
- You should check the contents of the README.md file through the GitHub website to ensure that it has all of your correct content!