#include <stdio.h> /* <- make sure the I/O functions are visible to the compiler */
/* main is the functions the OS calls when a program is loaded. It gets the
* number of command-line arguments and an array with the arguments. The first
* entry is the name of the executable.
*
* Main returns an integer that is the exit status of the program (0 on success).
*/
int main(int argc, char **argv) {
printf("Hello, World!\n"); /* We've used printf before */
return 0;
}Save as hello.c and compile this program using
$ gcc hello.c -o helloNote that we don’t need to use -no-pie because we are not combining our C code with any assembly
Blocks of scope are delimited by { and }
Functions are declared pretty much like Java methods:
return_type function_name(type1 arg1, type2 arg2, ...)voidControl flow
We have the usual conditionals:
if (something) {
// do stuff
}
if (something) {
// do stuff
}
else {
// do other stuff
}While loops and do-while loops
while (condition) {
// do this while condition holds
}do {
// do this at leas once and then keep doing it again while condition holds
} while (condition);For loops
for (initializer; condition; updater) {
// 1. run the initializer expression
// 2. if condition holds go to 3, else go to 6
// 3. do stuff in body
// 4. run the updater expression
// 5. Go to 2
// 6. End
}Comparison operators: <, >, <=, >=, ==
Logical operators: !, &&, ||
You can skip the rest of the current iteration of the innermost loop with continue
You can break out of the innermost loop with break
Types
signed and unsigned integral types: char (1 byte = 8-bit), short int (16-bit), int (32-bit), long int (64-bit)unsigned short intfloat (32-bit) and double (64-bit) (most implementations: IEE 754 single- and double-precision floats)structs (see below)unions (later)12, -10.5, …' ': 'a'"Hello, World" (see below)No booleans: just 0 and non-0
_Bool now…)0 is “false”!(!b) == b does not hold as a resultNo string type, just a collection of characters
Using the * after the type name
E.g.,
int *pi; // pi is a pointer to an integer (uninitialized)Note: If a pointer isn’t set to point to anything right away, it’s good practice to initialize it to NULL:
int *pi = NULL;Getting the value that a pointer points to is called dereferencing and is denoted by putting * before the variable name:
printf("%d\n", *pi);The easiest way we can get a pointer to point to something meaningful is to give it an address
The address-of operator & serves this purpose:
int i = 42;
int *pi = &i; // pi now points to where the variable i is stored
printf("%d %d\n", i, *pi); // 42 42
i = 43;
printf("%d %d\n", i, *pi); // 43 43
*pi = 44; // note the *
printf("%d %d\n", i, *pi); // 44 44
int j = 123;
pi = &j;
printf("%d %d\n", i, *pi); // 44 123Pointers can point to other pointers:
int i = 42;
int *pi = &i;
int **ppi = &p;
printf("%d %d %d\n", i, *pi, **pi);You can have more levels of indirection
Remember char **argv?
Spoiler alert: arrays are just pointers with some fancy syntax
We will work with static (size known at compile-time) and dynamic arrays
We’ll talk about static ones first
E.g.,
float nums[4]; // create an array of 4 floatsThis makes room for 6 floats
These will be stored contiguously in memory
nums points to the first element
We can access them individually using indices, starting from 0
float nums[4]; // create an array of 4 floats
nums[0] = 0.1;
nums[1] = 3.14;
nums[2] = 1.5;
nums[3] = 3214;
printf("2nd element: %f\n", nums[1]);Arrays can also be initialized:
float nums[4] = { 0.1, 3.14, 1.5, 3214 };
printf("2nd element: %f\n", nums[1]);In C (like in Assembly for us), strings are just arrays of characters, terminated by a 0 byte (also written '\0')
A string literal "Hello, world!" is just the corresponding array of characters with an extra char for 0
I.e.
char msg1[6] = "Hello";
char msg2[6] = { 'H', 'e', 'l', 'l', 'o', '\0' };
// msg1 and msg2 define exactly the same object in memoryMemory can be allocated using the library function malloc
It’s defined in stdlib.h
It takes the number of bytes we want and returns a pointer to the block of memory (if successful)
Allocated memory needs to be freed using free
int *one_int = malloc(4);
*one_int = 42;
free(one_int);We will mostly use malloc to allocate arrays and structs (below)
int *fifty_ints = malloc(50 * sizeof(int));
for (int i = 0; i < 50; ++i) {
fifty_ints[i] = i * i;
}
free(fifty_ints);Structs are the most useful user-defined data types in C
Think of them as Java classes, but everything is public (thought make the whole struct opaque) and there are no methods
They’re actually more similar to the define-struct in Fundies I, but there are types
A struct allows us to store multiple values of different types together
It is defined using the struct keyword:
struct address {
unsigned int house_no;
char street[32];
char city[24];
char state[3];
unsigned int zip;
}Note that structs occupy a separate namespace - to create a struct variable of the above type, we need to use the struct keyword again:
struct address home, work; // inside a function this will allocate two
// structs on the stackTo access a field, we use the .:
work.house_no = 360;
strcpy(work.street, "Huntington Ave"); // see man 3 strcpy
strcpy(work.city, "Boston");
strcpy(work.state, "MA");
work.zip = 02115;Structs can, of course, be nested:
struct person {
char first[32];
char last[32];
struct address home;
}They can be passed to and returned from a function:
struct address get_address(struct person p) { ... }typedefWriting out struct every time can be tiring
C allows us to introduce type synonyms using typedef:
typedef person_t struct person; // now we can use person_t to mean struct persontypedef can be used with any type to make code more readable:
typedef age_t unsigned char;Of course, we can have pointers to structs:
struct person *p; // OR
person_t *p;We can use the address-of operator & to get the address of a struct:
struct address *current = &work;We can also allocate memory for structs dynamically, using malloc and sizeof:
struct person *ferd = malloc(sizeof(struct person)); // or equivalently:
person_t *ferd = malloc(sizeof(person_t));We can also create arrays of structs:
person_t class[80];
person_t *friends = malloc(5 * sizeof(person_t));
// ...
for (int i = 0; i < 5; ++i) {
if (strcmp(friends[i].home.street, "Huntington Ave") == 0) {
printf("%s lives close!\n", friends[i].first);
}
}Often, pointers are used to pass a struct to a function, to avoid copying the contents into the function’s stack frame
When accessing fields via a pointer, we can use -> to make things more readable:
int lives_in_boston(person_t *p) {
return strcmp(p->home.city, "Boston") != 0; // equivalent to
return strcmp((*p).home.city, "Boston") != 0;
}#define#include#ifdef/#ifndef…
…
…