C Basics¶
Learning Outcomes
-
Declare and initialize basic variable types in C.
-
Use
stdint.htypedefs where possible, because widths of basic integer types are processor-dependent. -
Always remember that uninitialized variables contain garbage.
-
Remember that all values in C can be cast to booleans, and the only false values are
0,NULL, andfalse. -
Navigate the large laundry list of C topics here, and use the K&R reference to fill in details.
-
Pay special attention to
typedefandstruct, which are used to declare and organize more complex data types.
Variables¶
C Basic Types¶
| Type | Size (sizeof(type)) | Range | Description | Example |
|---|---|---|---|---|
int | 4 bytes | \(-2^{31} \sim 2^{31}-1\) | Integer numbers (positive and negative) | 10, -5, 0x2233 |
unsigned int | 4 bytes | \(0 \sim 2^{32}-1\) | Unsigned integer numbers | 10u, 42, 0xffu |
float | 4 bytes | About \(\pm 3.4 \times 10^{38}\) | Single-precision floating-point number | 3.14f, -0.5f |
double | 8 bytes | About \(\pm 1.7 \times 10^{308}\) | Double-precision floating-point number | 3.1415926, -2.5 |
char | 1 byte | \(-128 \sim 127\) or \(0 \sim 255\) | Single character / small integer | 'A', 'z', 65 |
short | 2 bytes | \(-2^{15} \sim 2^{15}-1\) | Short integer | 123, -20 |
long | 8 bytes | \(-2^{63} \sim 2^{63}-1\) | Long integer | 123456L, -99L |
long long | 8 bytes | \(-2^{63} \sim 2^{63}-1\) | Longer integer type | 123456789LL, -1LL |
Info
-
The size of
longmay vary across platforms, so the same code can behave differently on 32-bit and 64-bit systems. -
Whether
charis signed or unsigned depends on the compiler and target platform; usesigned charorunsigned charwhen you need to be explicit. -
floatanddoublestore approximate values, so calculations and equality comparisons can be affected by precision errors.
inttypes.h and stdint.h
In C99 and later, inttypes.h or stdint.h are recommended for portable integer types. It specifies unsigned and signed types like uint8_t and int32_t, where width is specified in bits. So int32_t x; would declare x as a 32-bit wide signed integer that uses two's complement representation.
Variable declaration and initialization¶
Attention that C variable declarations do not initialize variables to default values. So the program below will cause an error:
#include <stdio.h>
int main(int argc, char *argv[]) {
int32_t x = 0;
int32_t y;
printf("before: x=%d, y=%d\n", x, y);
x++;
y += x;
printf(" after: x=%d, y=%d\n", x, y);
return 0;
}
The program just declares y as type int; it does not initialize y to any default value.
Recall that any 32-bit value can be interpreted as a two's complement integer. At compilation time, the compiler recognizes that y += x; is a valid operation based on the types of x and y. At runtime, the program simply takes whatever 32 bits are at y and adding the value of x, then updating those 32 bits.
Booleans Type in C¶
After C23, C has a built-in boolean type bool with two values. But before that, we need to #include <stdbool.h> for definitions of true and false.
And notice that, values in C are truthy—meaning, means every value can be interpreted as true or false.
False values:
-
0, i.e., all bits of this value are \(0\) -
NULL, which is also defined as \(0\), but is commonly used for pointers. More later. -
false, ifstdbool.his used
Everything else True values.
C Syntax¶
typedef¶
typedef used to create an additional name (alias) for another data type (a little bit like using in C++).
typedef uint8_t Byte;
Byte b1 = 0x12, b2 = 0x34;
Note
Declaring multiple variables within the same value as above is not recommended, it will leads to confusing types when we introduce pointers next time.
struct¶
struct is used to declare structured groups of variables. A struct is an abstract data type definition.
Preprocessor Macros¶
#define is a C Preprocessor (CPP) macro. Before compilation, the preprocessor does text replacement based on all #define macros. For example:
#define PI (3.14159)
replaces every PI with (3.14159), so PI behaves like a “constant.”
Macros are also often used to create small “functions,” but they are not real functions—only text substitution:
#define min(X, Y) ((X) < (Y) ? (X) : (Y))
next = min(w, foo(z));
is expanded to:
next = ((w) < (foo(z)) ? (w) : (foo(z)));
Warning
If foo(z) has a side effect (e.g. modifies a variable), that side effect may run twice. Prefer real functions when side effects matter.
Constants and Enums¶
const declares a typed constant: assign once, then the value cannot change during execution.
const float golden_ratio = 1.618;
const int days_in_week = 7;
const double the_law = 2.99792458e8;
An enum groups related integer constants:
enum cardsuit { CLUBS, DIAMONDS, HEARTS, SPADES };
enum color { RED, GREEN, BLUE };
By default, the first name is 0, then 1, 2, and so on. Prefer using the names (RED, GREEN, …) instead of hard-coding their integer values.
Control Flow¶
Control flow in C is similar to Java. A few details worth remembering:
-
Curly braces: Bodies of
if/elseand loops may use braces or a single statement. Prefer braces for clarity. -
whileanddo-while:while (condition) { /* ... */ } do { /* ... */ } while (condition);do-whileruns the body at least once, then checks the condition. -
switch: Execution falls through into latercases until abreak(or the end of theswitch):switch (x) { case 1: printf("one\n"); break; case 2: printf("two\n"); break; default: printf("other\n"); }
Note
Avoid do-while and goto in everyday C. Both ideas become useful later when studying assembly control flow.
Functions¶
Every function must declare its return type and parameter types:
int number_of_people(int class1, int class2) {
return class1 + class2;
}
float dollars_and_cents(float cost) {
return cost;
}
void say_hi(void) {
printf("hi\n");
}
-
Return type sits to the left of the function name; use
voidwhen nothing is returned. -
Parameters must also be typed.
-
Variables and functions must be declared before use. In practice, put prototypes (or implementations) near the top of the file, or share them via header files.
Header files
Header files (.h) let you share function declarations and macros across multiple .c source files. See the GCC Header Files docs for details.