Python: dynamically typed
a = 42 # Type of a is int.
a = "hello" # Type of a becomes str.
The type of a variable
can be changed, and
is not necessarily known until we run the program.
C: statically-typed
int a = 42; // Type of a is int.
a = "hello"; // Error! Types mismatch!
The type of a variable
A type-related error in C is (usually) a compile error:
To declare a variable, we need to specify its type and name.
Type name;
Example:
int x; // Declares a variable named `x`, whose type is `int`.
double y; // Declares a variable named `y`, whose type is `double`.
We may declare multiple variables of a same type in one declaration statement, separated by ,:
int x, y; // Declares two variables `x` and `y`, both having type `int`.
A variable declaration can be placed
#include <stdio.h>
int x, y; // global variables
int main(void) {
scanf("%d%d", &x, &y);
printf("%d\n", x + y);
}
#include <stdio.h>
int main(void) {
// local variables in `main`
int x, y;
scanf("%d%d", &x, &y);
printf("%d\n", x + y);
}
Which one do you prefer?
#include <stdio.h>
int x, y; // global variables
int main(void) {
scanf("%d%d", &x, &y);
printf("%d\n", x + y);
}
#include <stdio.h>
int main(void) {
// local variables in `main`
int x, y;
scanf("%d%d", &x, &y);
printf("%d\n", x + y);
}
#include <stdio.h>
// Other #includes
int x, y; // What are these two variables used for?
int moveSpaceShuttle(SpaceShuttle *shuttle, Coord to, Vehicle *by) {
// 109 lines
}
int makePreparations(Environment *env, Task tasks[], Time time) {
// 73 lines
}
LaunchResult launchSpaceShuttle(SpaceShuttle *shuttle, Task tasks[]) {
// 35 lines
}
// Other 136 functions, 3325 lines in total
int main(void) {
// 120 lines
}
[Best practice] Declare the variable when you first use it!
[Best practice] Use meaningful names!
a, b, c, d, x, y, cnt, cnt_2, flag1, flag2, flag3 everywhere.sumOfScore, student_cnt, open_success, ...Readability is very important. Many students debug day and night simply because their programs are not human-readable.
One reason for using global variables is to have them shared between functions:
int input;
void work(void) {
printf("%d\n", input);
}
int main(void) {
scanf("%d", &input);
work();
}
void work(void) {
// Error: `input` was not decared
// in this scope.
printf("%d\n", input);
}
int main(void) {
int input;
scanf("%d", &input);
work();
}
A variable can be initialized on declaration.
int x = 42; // Declares the variable `x` of type `int`,
// and initializes its value to 42.
int a = 0, b, c = 42; // Declares three `int` variables, with `a` initialized
// to 0, `c` initialized to 42, and `b` uninitialized.
This is syntactically different (though seems equivalent) to
int x; // Declares `x`, uninitialized.
x = 42; // Assigns 42 to `x`.
[Best practice] Initialize the variable if possible. Prefer initialization to later assignment.
Refer to this page for a complete, detailed and standard documentation.
Is int equivalent to
int can represent?Is int equivalent to
int can represent?Experiment:
#include <stdio.h>
int main(void) {
int x = 1;
while (1) {
printf("%d\n", x);
x *= 2; // x = x * 2
getchar();
}
}
On 64-bit Ubuntu 22.04 and compiled with GCC 13, after printing 1073741824 (0.
1073741824
-2147483648
0
0
Information is stored in computers in binary.
A bit is either
A byte is
A 32-bit number:
Suppose now we have
Suppose now we have
An integer type in C is either signed or unsigned, and has a width denoting the number of bits that can be used to represent values.
Suppose we have an integer type of
The keyword int is optional in types other than int:
short int and short name the same type.unsigned int and unsigned name the same type."Unsigned-ness" needs to be written explicitly: unsigned int, unsigned long, ...
Types without the keyword unsigned are signed by default:
signed int and int name the same type.signed long int, signed long, long int and long name the same type.| type | width (at least) | width (usually) |
|---|---|---|
short |
16 bits | 16 bits |
int |
16 bits | 32 bits |
long |
32 bits | 32 or 64 bits |
long long |
64 bits | 64 bits |
unsigned counterpart.sizeof(short) sizeof(int) sizeof(long) sizeof(long long).
sizeof(T) is the number of bytes that T holds.The standard states that the exact width of the integer types is implementation-defined.
int is the most optimal integer type for the platform.
int for integer arithmetic by default.long long if the range of int is not large enough.short, or even unsigned char) for memory-saving or other special purposes.unsigned types for special purposes. We will see some in later lectures.
"Floating-point": The number's radix point can "float" anywhere to the left, right, or between the significant digits of the number.
Real floating-point types can be used to represent some real values.
C has three types for representing real floating-point values:
float: single precision. Matches IEEE754 binary32 format if supported.double: double precision. Matches IEEE754 binary64 format if supported.long double: extended precision. A floating-point type whose precision and range are at least as good as those of double.Details of IEEE754 formats are not required in CS100.
Range of values can be found in this table.
Use double for real floating-point arithmetic by default.
float is not enough.double arithmetic is not necessarily slower than float.Do not use floating-point types for integer arithmetic!
scanf/printfRefer to the table in this page.
| type | format specifier |
|---|---|
short |
%hd |
int |
%d |
long |
%ld |
long long |
%lld |
| type | format specifier |
|---|---|
unsigned short |
%hu |
unsigned |
%u |
unsigned long |
%lu |
unsigned long long |
%llu |
%f for float, %lf for double, and %Lf for long double.Write the "A+B" program for real numbers. Which type do you decide to use? How do you read and print the values?
Write the "A+B" program for real numbers. Which type do you decide to use? How do you read and print the values?
#include <stdio.h>
int main(void) {
double a, b;
scanf("%lf%lf", &a, &b);
printf("%lf\n", a + b);
return 0;
}
The C standard provides three different character types: signed char, unsigned char and char.
Let T signed char, unsigned char, char
1 == sizeof(T) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long).
T takes exactly 1 byte.Question: What is the valid range of signed char? unsigned char?
Question: What is the valid range of signed char? unsigned char?
signed char: unsigned char: What? A character is an integer?
A character is represented in computers as its ASCII code, which is a small integer.
A character is represented in computers as its ASCII code, which is a small integer.
A character is nothing but an integer! In C, there is no "conversion" between characters and ASCII code!
Important things to remember:
'0''9''A''Z''a''z'Example: Given a lowercase letter, return its uppercase form.
char to_uppercase(char x) {
return x - 32;
}
What is the meaning of 32 here?
char to_uppercase(char x) {
return x - 32;
}
Write it in a more human-readable way:
char to_uppercase(char x) {
return x - ('a' - 'A');
}
Some special characters are not directly representable: newline, tab, quote, ...
We use escape sequences, e.g.
| escape sequence | description |
|---|---|
\' |
single quote |
\" |
double quote |
\\ |
backslash |
| escape sequence | description |
|---|---|
\n |
newline |
\r |
carriage return |
\t |
horizontal tab |
char, signed char and unsigned char are three different types.
char is signed or unsigned is implementation-defined.char is signed (unsigned), it represents the same set of values as the type signed char (unsigned char), but they are not the same type.
T and signed T are the same type for T short, int, long, long longFor almost all cases, use char (or, sometimes int) to represent characters.
signed char and unsigned char are used for other purposes.
To read/print a char using scanf/printf, use %c.
Even though the standard provides wchar_t, char8_t (since C23), char16_t and char32_t to handle wide/Unicode characters, there are still a lot of problems.
C++23 has some improvement.
bool (since C99)A type that represents true/false, 0/1, yes/no, ...
To access the name bool, true and false, <stdbool.h> is needed. (until C23)
Example: Define a function that accepts a character and returns whether that character is a lowercase letter.
Before C99, using int, 0 and 1:
int is_lowercase(char c) {
if (c >= 'a' && c <= 'z')
return 1;
else
return 0;
}
Since C99, using bool, false and true:
bool is_lowercase(char c) {
if (c >= 'a' && c <= 'z')
return true;
else
return false;
}
bool (since C99)Before C99, using int, 0 and 1:
int is_lowercase(char c) {
if (c >= 'a' && c <= 'z')
return 1;
else
return 0;
}
Since C99, using bool, false and true:
bool is_lowercase(char c) {
if (c >= 'a' && c <= 'z')
return true;
else
return false;
}
Both return values can be used as follows:
char c; scanf("%c", &c);
if (is_lowercase(c)) {
// do something when c is lowercase ...
}
Just return the result of the condition expression.
int is_lowercase(char c) {
return c >= 'a' && c <= 'z';
}
bool is_lowercase(char c) {
return c >= 'a' && c <= 'z';
}
We will introduce the operators (&&, <=, >=) involved here in later lectures.
Write a simple calculator that handles input of the form x op y, where x and y are floating-point numbers and op '+', '-', '*', '/' if-else statements like this:
if (op == '+') {
// ...
} else if (op == '-') {
// ...
} else if (op == '*') {
// ...
} else if (op == '/') {
// ...
} else {
// report an error
}