Fix c tutorial

Fixes #678

Still missing symbols, though
This commit is contained in:
Nami-Doc 2014-07-17 10:46:24 +02:00
parent 57c384420e
commit 15353d1938

View File

@ -16,15 +16,15 @@ memory management and C will take you as far as you need to go.
```c ```c
// Single-line comments start with // - only available in C99 and later. // Single-line comments start with // - only available in C99 and later.
/* /*
Multi-line comments look like this. They work in C89 as well. Multi-line comments look like this. They work in C89 as well.
*/ */
// Constants: #define <keyword> // Constants: #define <keyword>
#define DAYS_IN_YEAR 365 #define DAYS_IN_YEAR 365
// Enumeration constants are also ways to declare constants. // Enumeration constants are also ways to declare constants.
enum days {SUN = 1, MON, TUE, WED, THU, FRI, SAT}; enum days {SUN = 1, MON, TUE, WED, THU, FRI, SAT};
// MON gets 2 automatically, TUE gets 3, etc. // MON gets 2 automatically, TUE gets 3, etc.
// Import headers with #include // Import headers with #include
@ -34,11 +34,11 @@ enum days {SUN = 1, MON, TUE, WED, THU, FRI, SAT};
// (File names between <angle brackets> are headers from the C standard library.) // (File names between <angle brackets> are headers from the C standard library.)
// For your own headers, use double quotes instead of angle brackets: // For your own headers, use double quotes instead of angle brackets:
#include "my_header.h" //#include "my_header.h"
// Declare function signatures in advance in a .h file, or at the top of // Declare function signatures in advance in a .h file, or at the top of
// your .c file. // your .c file.
void function_1(char c); void function_1();
int function_2(void); int function_2(void);
// Must declare a 'function prototype' before main() when functions occur after // Must declare a 'function prototype' before main() when functions occur after
@ -48,374 +48,373 @@ int add_two_ints(int x1, int x2); // function prototype
// Your program's entry point is a function called // Your program's entry point is a function called
// main with an integer return type. // main with an integer return type.
int main() { int main() {
// print output using printf, for "print formatted" // print output using printf, for "print formatted"
// %d is an integer, \n is a newline // %d is an integer, \n is a newline
printf("%d\n", 0); // => Prints 0 printf("%d\n", 0); // => Prints 0
// All statements must end with a semicolon // All statements must end with a semicolon
/////////////////////////////////////// ///////////////////////////////////////
// Types // Types
/////////////////////////////////////// ///////////////////////////////////////
// ints are usually 4 bytes // ints are usually 4 bytes
int x_int = 0; int x_int = 0;
// shorts are usually 2 bytes // shorts are usually 2 bytes
short x_short = 0; short x_short = 0;
// chars are guaranteed to be 1 byte // chars are guaranteed to be 1 byte
char x_char = 0; char x_char = 0;
char y_char = 'y'; // Char literals are quoted with '' char y_char = 'y'; // Char literals are quoted with ''
// longs are often 4 to 8 bytes; long longs are guaranteed to be at least // longs are often 4 to 8 bytes; long longs are guaranteed to be at least
// 64 bits // 64 bits
long x_long = 0; long x_long = 0;
long long x_long_long = 0; long long x_long_long = 0;
// floats are usually 32-bit floating point numbers // floats are usually 32-bit floating point numbers
float x_float = 0.0; float x_float = 0.0;
// doubles are usually 64-bit floating-point numbers // doubles are usually 64-bit floating-point numbers
double x_double = 0.0; double x_double = 0.0;
// Integral types may be unsigned. // Integral types may be unsigned.
unsigned short ux_short; unsigned short ux_short;
unsigned int ux_int; unsigned int ux_int;
unsigned long long ux_long_long; unsigned long long ux_long_long;
// chars inside single quotes are integers in machine's character set. // chars inside single quotes are integers in machine's character set.
'0' // => 48 in the ASCII character set. '0'; // => 48 in the ASCII character set.
'A' // => 65 in the ASCII character set. 'A'; // => 65 in the ASCII character set.
// sizeof(T) gives you the size of a variable with type T in bytes // sizeof(T) gives you the size of a variable with type T in bytes
// sizeof(obj) yields the size of the expression (variable, literal, etc.). // sizeof(obj) yields the size of the expression (variable, literal, etc.).
printf("%zu\n", sizeof(int)); // => 4 (on most machines with 4-byte words) printf("%zu\n", sizeof(int)); // => 4 (on most machines with 4-byte words)
// If the argument of the `sizeof` operator is an expression, then its argument // If the argument of the `sizeof` operator is an expression, then its argument
// is not evaluated (except VLAs (see below)). // is not evaluated (except VLAs (see below)).
// The value it yields in this case is a compile-time constant. // The value it yields in this case is a compile-time constant.
int a = 1; int a = 1;
// size_t is an unsigned integer type of at least 2 bytes used to represent // size_t is an unsigned integer type of at least 2 bytes used to represent
// the size of an object. // the size of an object.
size_t size = sizeof(a++); // a++ is not evaluated size_t size = sizeof(a++); // a++ is not evaluated
printf("sizeof(a++) = %zu where a = %d\n", size, a); printf("sizeof(a++) = %zu where a = %d\n", size, a);
// prints "sizeof(a++) = 4 where a = 1" (on a 32-bit architecture) // prints "sizeof(a++) = 4 where a = 1" (on a 32-bit architecture)
// Arrays must be initialized with a concrete size. // Arrays must be initialized with a concrete size.
char my_char_array[20]; // This array occupies 1 * 20 = 20 bytes char my_char_array[20]; // This array occupies 1 * 20 = 20 bytes
int my_int_array[20]; // This array occupies 4 * 20 = 80 bytes int my_int_array[20]; // This array occupies 4 * 20 = 80 bytes
// (assuming 4-byte words) // (assuming 4-byte words)
// You can initialize an array to 0 thusly: // You can initialize an array to 0 thusly:
char my_array[20] = {0}; char my_array[20] = {0};
// Indexing an array is like other languages -- or, // Indexing an array is like other languages -- or,
// rather, other languages are like C // rather, other languages are like C
my_array[0]; // => 0 my_array[0]; // => 0
// Arrays are mutable; it's just memory! // Arrays are mutable; it's just memory!
my_array[1] = 2; my_array[1] = 2;
printf("%d\n", my_array[1]); // => 2 printf("%d\n", my_array[1]); // => 2
// In C99 (and as an optional feature in C11), variable-length arrays (VLAs) // In C99 (and as an optional feature in C11), variable-length arrays (VLAs)
// can be declared as well. The size of such an array need not be a compile // can be declared as well. The size of such an array need not be a compile
// time constant: // time constant:
printf("Enter the array size: "); // ask the user for an array size printf("Enter the array size: "); // ask the user for an array size
char buf[0x100]; char buf[0x100];
fgets(buf, sizeof buf, stdin); fgets(buf, sizeof buf, stdin);
// strtoul parses a string to an unsigned integer // strtoul parses a string to an unsigned integer
size_t size = strtoul(buf, NULL, 10); size_t size2 = strtoul(buf, NULL, 10);
int var_length_array[size]; // declare the VLA int var_length_array[size2]; // declare the VLA
printf("sizeof array = %zu\n", sizeof var_length_array); printf("sizeof array = %zu\n", sizeof var_length_array);
// A possible outcome of this program may be: // A possible outcome of this program may be:
// > Enter the array size: 10 // > Enter the array size: 10
// > sizeof array = 40 // > sizeof array = 40
// Strings are just arrays of chars terminated by a NULL (0x00) byte, // Strings are just arrays of chars terminated by a NULL (0x00) byte,
// represented in strings as the special character '\0'. // represented in strings as the special character '\0'.
// (We don't have to include the NULL byte in string literals; the compiler // (We don't have to include the NULL byte in string literals; the compiler
// inserts it at the end of the array for us.) // inserts it at the end of the array for us.)
char a_string[20] = "This is a string"; char a_string[20] = "This is a string";
printf("%s\n", a_string); // %s formats a string printf("%s\n", a_string); // %s formats a string
printf("%d\n", a_string[16]); // => 0 printf("%d\n", a_string[16]); // => 0
// i.e., byte #17 is 0 (as are 18, 19, and 20) // i.e., byte #17 is 0 (as are 18, 19, and 20)
// If we have characters between single quotes, that's a character literal. // If we have characters between single quotes, that's a character literal.
// It's of type `int`, and *not* `char` (for historical reasons). // It's of type `int`, and *not* `char` (for historical reasons).
int cha = 'a'; // fine int cha = 'a'; // fine
char chb = 'a'; // fine too (implicit conversion from int to char) char chb = 'a'; // fine too (implicit conversion from int to char)
//Multi-dimensional arrays: //Multi-dimensional arrays:
int multi_array[2][5] = { int multi_array[2][5] = {
{1, 2, 3, 4, 5}, {1, 2, 3, 4, 5},
{6, 7, 8, 9, 0} {6, 7, 8, 9, 0}
}; };
//access elements: //access elements:
int array_int = multi_array[0][2]; // => 3 int array_int = multi_array[0][2]; // => 3
/////////////////////////////////////// ///////////////////////////////////////
// Operators // Operators
/////////////////////////////////////// ///////////////////////////////////////
// Shorthands for multiple declarations: // Shorthands for multiple declarations:
int i1 = 1, i2 = 2; int i1 = 1, i2 = 2;
float f1 = 1.0, f2 = 2.0; float f1 = 1.0, f2 = 2.0;
int a, b, c; int b, c;
a = b = c = 0; b = c = 0;
// Arithmetic is straightforward // Arithmetic is straightforward
i1 + i2; // => 3 i1 + i2; // => 3
i2 - i1; // => 1 i2 - i1; // => 1
i2 * i1; // => 2 i2 * i1; // => 2
i1 / i2; // => 0 (0.5, but truncated towards 0) i1 / i2; // => 0 (0.5, but truncated towards 0)
f1 / f2; // => 0.5, plus or minus epsilon f1 / f2; // => 0.5, plus or minus epsilon
// Floating-point numbers and calculations are not exact // Floating-point numbers and calculations are not exact
// Modulo is there as well // Modulo is there as well
11 % 3; // => 2 11 % 3; // => 2
// Comparison operators are probably familiar, but // Comparison operators are probably familiar, but
// there is no Boolean type in c. We use ints instead. // there is no Boolean type in c. We use ints instead.
// (Or _Bool or bool in C99.) // (Or _Bool or bool in C99.)
// 0 is false, anything else is true. (The comparison // 0 is false, anything else is true. (The comparison
// operators always yield 0 or 1.) // operators always yield 0 or 1.)
3 == 2; // => 0 (false) 3 == 2; // => 0 (false)
3 != 2; // => 1 (true) 3 != 2; // => 1 (true)
3 > 2; // => 1 3 > 2; // => 1
3 < 2; // => 0 3 < 2; // => 0
2 <= 2; // => 1 2 <= 2; // => 1
2 >= 2; // => 1 2 >= 2; // => 1
// C is not Python - comparisons don't chain. // C is not Python - comparisons don't chain.
int a = 1; // WRONG:
// WRONG: //int between_0_and_2 = 0 < a < 2;
int between_0_and_2 = 0 < a < 2; // Correct:
// Correct: int between_0_and_2 = 0 < a && a < 2;
int between_0_and_2 = 0 < a && a < 2;
// Logic works on ints // Logic works on ints
!3; // => 0 (Logical not) !3; // => 0 (Logical not)
!0; // => 1 !0; // => 1
1 && 1; // => 1 (Logical and) 1 && 1; // => 1 (Logical and)
0 && 1; // => 0 0 && 1; // => 0
0 || 1; // => 1 (Logical or) 0 || 1; // => 1 (Logical or)
0 || 0; // => 0 0 || 0; // => 0
//Conditional expression ( ? : ) //Conditional expression ( ? : )
int a = 5; int e = 5;
int b = 10; int f = 10;
int z; int z;
z = (a > b) ? a : b; // => 10 "if a > b return a, else return b." z = (a > b) ? a : b; // => 10 "if a > b return a, else return b."
//Increment and decrement operators: //Increment and decrement operators:
char *s = "iLoveC"; char *s = "iLoveC";
int j = 0; int j = 0;
s[j++]; // => "i". Returns the j-th item of s THEN increments value of j. s[j++]; // => "i". Returns the j-th item of s THEN increments value of j.
j = 0; j = 0;
s[++j]; // => "L". Increments value of j THEN returns j-th value of s. s[++j]; // => "L". Increments value of j THEN returns j-th value of s.
// same with j-- and --j // same with j-- and --j
// Bitwise operators! // Bitwise operators!
~0x0F; // => 0xF0 (bitwise negation, "1's complement") ~0x0F; // => 0xF0 (bitwise negation, "1's complement")
0x0F & 0xF0; // => 0x00 (bitwise AND) 0x0F & 0xF0; // => 0x00 (bitwise AND)
0x0F | 0xF0; // => 0xFF (bitwise OR) 0x0F | 0xF0; // => 0xFF (bitwise OR)
0x04 ^ 0x0F; // => 0x0B (bitwise XOR) 0x04 ^ 0x0F; // => 0x0B (bitwise XOR)
0x01 << 1; // => 0x02 (bitwise left shift (by 1)) 0x01 << 1; // => 0x02 (bitwise left shift (by 1))
0x02 >> 1; // => 0x01 (bitwise right shift (by 1)) 0x02 >> 1; // => 0x01 (bitwise right shift (by 1))
// Be careful when shifting signed integers - the following are undefined: // Be careful when shifting signed integers - the following are undefined:
// - shifting into the sign bit of a signed integer (int a = 1 << 32) // - shifting into the sign bit of a signed integer (int a = 1 << 32)
// - left-shifting a negative number (int a = -1 << 2) // - left-shifting a negative number (int a = -1 << 2)
// - shifting by an offset which is >= the width of the type of the LHS: // - shifting by an offset which is >= the width of the type of the LHS:
// int a = 1 << 32; // UB if int is 32 bits wide // int a = 1 << 32; // UB if int is 32 bits wide
/////////////////////////////////////// ///////////////////////////////////////
// Control Structures // Control Structures
/////////////////////////////////////// ///////////////////////////////////////
if (0) { if (0) {
printf("I am never run\n"); printf("I am never run\n");
} else if (0) { } else if (0) {
printf("I am also never run\n"); printf("I am also never run\n");
} else { } else {
printf("I print\n"); printf("I print\n");
} }
// While loops exist // While loops exist
int ii = 0; int ii = 0;
while (ii < 10) { //ANY value not zero is true. while (ii < 10) { //ANY value not zero is true.
printf("%d, ", ii++); // ii++ increments ii AFTER using its current value. printf("%d, ", ii++); // ii++ increments ii AFTER using its current value.
} // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " } // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
printf("\n"); printf("\n");
int kk = 0; int kk = 0;
do { do {
printf("%d, ", kk); printf("%d, ", kk);
} while (++kk < 10); // ++kk increments kk BEFORE using its current value. } while (++kk < 10); // ++kk increments kk BEFORE using its current value.
// => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
printf("\n"); printf("\n");
// For loops too // For loops too
int jj; int jj;
for (jj=0; jj < 10; jj++) { for (jj=0; jj < 10; jj++) {
printf("%d, ", jj); printf("%d, ", jj);
} // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " } // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
printf("\n"); printf("\n");
// *****NOTES*****: // *****NOTES*****:
// Loops and Functions MUST have a body. If no body is needed: // Loops and Functions MUST have a body. If no body is needed:
int i; int i;
for (i = 0; i <= 5; i++) { for (i = 0; i <= 5; i++) {
; // use semicolon to act as the body (null statement) ; // use semicolon to act as the body (null statement)
} }
// branching with multiple choices: switch() // branching with multiple choices: switch()
switch (some_integral_expression) { switch (a) {
case 0: // labels need to be integral *constant* expressions case 0: // labels need to be integral *constant* expressions
do_stuff(); do_stuff();
break; // if you don't break, control flow falls over labels break; // if you don't break, control flow falls over labels
case 1: case 1:
do_something_else(); do_something_else();
break; break;
default: default:
// if `some_integral_expression` didn't match any of the labels // if `some_integral_expression` didn't match any of the labels
fputs("error!\n", stderr); fputs("error!\n", stderr);
exit(-1); exit(-1);
break; break;
} }
/////////////////////////////////////// ///////////////////////////////////////
// Typecasting // Typecasting
/////////////////////////////////////// ///////////////////////////////////////
// Every value in C has a type, but you can cast one value into another type // Every value in C has a type, but you can cast one value into another type
// if you want (with some constraints). // if you want (with some constraints).
int x_hex = 0x01; // You can assign vars with hex literals int x_hex = 0x01; // You can assign vars with hex literals
// Casting between types will attempt to preserve their numeric values // Casting between types will attempt to preserve their numeric values
printf("%d\n", x_hex); // => Prints 1 printf("%d\n", x_hex); // => Prints 1
printf("%d\n", (short) x_hex); // => Prints 1 printf("%d\n", (short) x_hex); // => Prints 1
printf("%d\n", (char) x_hex); // => Prints 1 printf("%d\n", (char) x_hex); // => Prints 1
// Types will overflow without warning // Types will overflow without warning
printf("%d\n", (unsigned char) 257); // => 1 (Max char = 255 if char is 8 bits long) printf("%d\n", (unsigned char) 257); // => 1 (Max char = 255 if char is 8 bits long)
// For determining the max value of a `char`, a `signed char` and an `unsigned char`, // For determining the max value of a `char`, a `signed char` and an `unsigned char`,
// respectively, use the CHAR_MAX, SCHAR_MAX and UCHAR_MAX macros from <limits.h> // respectively, use the CHAR_MAX, SCHAR_MAX and UCHAR_MAX macros from <limits.h>
// Integral types can be cast to floating-point types, and vice-versa. // Integral types can be cast to floating-point types, and vice-versa.
printf("%f\n", (float)100); // %f formats a float printf("%f\n", (float)100); // %f formats a float
printf("%lf\n", (double)100); // %lf formats a double printf("%lf\n", (double)100); // %lf formats a double
printf("%d\n", (char)100.0); printf("%d\n", (char)100.0);
/////////////////////////////////////// ///////////////////////////////////////
// Pointers // Pointers
/////////////////////////////////////// ///////////////////////////////////////
// A pointer is a variable declared to store a memory address. Its declaration will // A pointer is a variable declared to store a memory address. Its declaration will
// also tell you the type of data it points to. You can retrieve the memory address // also tell you the type of data it points to. You can retrieve the memory address
// of your variables, then mess with them. // of your variables, then mess with them.
int x = 0; int x = 0;
printf("%p\n", (void *)&x); // Use & to retrieve the address of a variable printf("%p\n", (void *)&x); // Use & to retrieve the address of a variable
// (%p formats an object pointer of type void *) // (%p formats an object pointer of type void *)
// => Prints some address in memory; // => Prints some address in memory;
// Pointers start with * in their declaration // Pointers start with * in their declaration
int *px, not_a_pointer; // px is a pointer to an int int *px, not_a_pointer; // px is a pointer to an int
px = &x; // Stores the address of x in px px = &x; // Stores the address of x in px
printf("%p\n", (void *)px); // => Prints some address in memory printf("%p\n", (void *)px); // => Prints some address in memory
printf("%zu, %zu\n", sizeof(px), sizeof(not_a_pointer)); printf("%zu, %zu\n", sizeof(px), sizeof(not_a_pointer));
// => Prints "8, 4" on a typical 64-bit system // => Prints "8, 4" on a typical 64-bit system
// To retrieve the value at the address a pointer is pointing to, // To retrieve the value at the address a pointer is pointing to,
// put * in front to dereference it. // put * in front to dereference it.
// Note: yes, it may be confusing that '*' is used for _both_ declaring a // Note: yes, it may be confusing that '*' is used for _both_ declaring a
// pointer and dereferencing it. // pointer and dereferencing it.
printf("%d\n", *px); // => Prints 0, the value of x printf("%d\n", *px); // => Prints 0, the value of x
// You can also change the value the pointer is pointing to. // You can also change the value the pointer is pointing to.
// We'll have to wrap the dereference in parenthesis because // We'll have to wrap the dereference in parenthesis because
// ++ has a higher precedence than *. // ++ has a higher precedence than *.
(*px)++; // Increment the value px is pointing to by 1 (*px)++; // Increment the value px is pointing to by 1
printf("%d\n", *px); // => Prints 1 printf("%d\n", *px); // => Prints 1
printf("%d\n", x); // => Prints 1 printf("%d\n", x); // => Prints 1
// Arrays are a good way to allocate a contiguous block of memory // Arrays are a good way to allocate a contiguous block of memory
int x_array[20]; //declares array of size 20 (cannot change size) int x_array[20]; //declares array of size 20 (cannot change size)
int xx; int xx;
for (xx = 0; xx < 20; xx++) { for (xx = 0; xx < 20; xx++) {
x_array[xx] = 20 - xx; x_array[xx] = 20 - xx;
} // Initialize x_array to 20, 19, 18,... 2, 1 } // Initialize x_array to 20, 19, 18,... 2, 1
// Declare a pointer of type int and initialize it to point to x_array // Declare a pointer of type int and initialize it to point to x_array
int* x_ptr = x_array; int* x_ptr = x_array;
// x_ptr now points to the first element in the array (the integer 20). // x_ptr now points to the first element in the array (the integer 20).
// This works because arrays often decay into pointers to their first element. // This works because arrays often decay into pointers to their first element.
// For example, when an array is passed to a function or is assigned to a pointer, // For example, when an array is passed to a function or is assigned to a pointer,
// it decays into (implicitly converted to) a pointer. // it decays into (implicitly converted to) a pointer.
// Exceptions: when the array is the argument of the `&` (address-of) operator: // Exceptions: when the array is the argument of the `&` (address-of) operator:
int arr[10]; int arr[10];
int (*ptr_to_arr)[10] = &arr; // &arr is NOT of type `int *`! int (*ptr_to_arr)[10] = &arr; // &arr is NOT of type `int *`!
// It's of type "pointer to array" (of ten `int`s). // It's of type "pointer to array" (of ten `int`s).
// or when the array is a string literal used for initializing a char array: // or when the array is a string literal used for initializing a char array:
char arr[] = "foobarbazquirk"; char otherarr[] = "foobarbazquirk";
// or when it's the argument of the `sizeof` or `alignof` operator: // or when it's the argument of the `sizeof` or `alignof` operator:
int arr[10]; int arraythethird[10];
int *ptr = arr; // equivalent with int *ptr = &arr[0]; int *ptr = arraythethird; // equivalent with int *ptr = &arr[0];
printf("%zu, %zu\n", sizeof arr, sizeof ptr); // probably prints "40, 4" or "40, 8" printf("%zu, %zu\n", sizeof arraythethird, sizeof ptr); // probably prints "40, 4" or "40, 8"
// Pointers are incremented and decremented based on their type // Pointers are incremented and decremented based on their type
// (this is called pointer arithmetic) // (this is called pointer arithmetic)
printf("%d\n", *(x_ptr + 1)); // => Prints 19 printf("%d\n", *(x_ptr + 1)); // => Prints 19
printf("%d\n", x_array[1]); // => Prints 19 printf("%d\n", x_array[1]); // => Prints 19
// You can also dynamically allocate contiguous blocks of memory with the // You can also dynamically allocate contiguous blocks of memory with the
// standard library function malloc, which takes one argument of type size_t // standard library function malloc, which takes one argument of type size_t
// representing the number of bytes to allocate (usually from the heap, although this // representing the number of bytes to allocate (usually from the heap, although this
// may not be true on e.g. embedded systems - the C standard says nothing about it). // may not be true on e.g. embedded systems - the C standard says nothing about it).
int *my_ptr = malloc(sizeof(*my_ptr) * 20); int *my_ptr = malloc(sizeof(*my_ptr) * 20);
for (xx = 0; xx < 20; xx++) { for (xx = 0; xx < 20; xx++) {
*(my_ptr + xx) = 20 - xx; // my_ptr[xx] = 20-xx *(my_ptr + xx) = 20 - xx; // my_ptr[xx] = 20-xx
} // Initialize memory to 20, 19, 18, 17... 2, 1 (as ints) } // Initialize memory to 20, 19, 18, 17... 2, 1 (as ints)
// Dereferencing memory that you haven't allocated gives // Dereferencing memory that you haven't allocated gives
// "unpredictable results" - the program is said to invoke "undefined behavior" // "unpredictable results" - the program is said to invoke "undefined behavior"
printf("%d\n", *(my_ptr + 21)); // => Prints who-knows-what? It may even crash. printf("%d\n", *(my_ptr + 21)); // => Prints who-knows-what? It may even crash.
// When you're done with a malloc'd block of memory, you need to free it, // When you're done with a malloc'd block of memory, you need to free it,
// or else no one else can use it until your program terminates // or else no one else can use it until your program terminates
// (this is called a "memory leak"): // (this is called a "memory leak"):
free(my_ptr); free(my_ptr);
// Strings are arrays of char, but they are usually represented as a // Strings are arrays of char, but they are usually represented as a
// pointer-to-char (which is a pointer to the first element of the array). // pointer-to-char (which is a pointer to the first element of the array).
// It's good practice to use `const char *' when referring to a string literal, // It's good practice to use `const char *' when referring to a string literal,
// since string literals shall not be modified (i.e. "foo"[0] = 'a' is ILLEGAL.) // since string literals shall not be modified (i.e. "foo"[0] = 'a' is ILLEGAL.)
const char *my_str = "This is my very own string literal"; const char *my_str = "This is my very own string literal";
printf("%c\n", *my_str); // => 'T' printf("%c\n", *my_str); // => 'T'
// This is not the case if the string is an array // This is not the case if the string is an array
// (potentially initialized with a string literal) // (potentially initialized with a string literal)
// that resides in writable memory, as in: // that resides in writable memory, as in:
char foo[] = "foo"; char foo[] = "foo";
foo[0] = 'a'; // this is legal, foo now contains "aoo" foo[0] = 'a'; // this is legal, foo now contains "aoo"
function_1(); function_1();
} // end main function } // end main function
/////////////////////////////////////// ///////////////////////////////////////
@ -427,12 +426,12 @@ int main() {
int add_two_ints(int x1, int x2) int add_two_ints(int x1, int x2)
{ {
return x1 + x2; // Use return to return a value return x1 + x2; // Use return to return a value
} }
/* /*
Functions are call by value. When a function is called, the arguments passed to Functions are call by value. When a function is called, the arguments passed to
the function are copies of the original arguments (except arrays). Anything you the function are copies of the original arguments (except arrays). Anything you
do to the arguments in the function do not change the value of the original do to the arguments in the function do not change the value of the original
argument where the function was called. argument where the function was called.
@ -444,14 +443,14 @@ Example: in-place string reversal
// A void function returns no value // A void function returns no value
void str_reverse(char *str_in) void str_reverse(char *str_in)
{ {
char tmp; char tmp;
int ii = 0; int ii = 0;
size_t len = strlen(str_in); // `strlen()` is part of the c standard library size_t len = strlen(str_in); // `strlen()` is part of the c standard library
for (ii = 0; ii < len / 2; ii++) { for (ii = 0; ii < len / 2; ii++) {
tmp = str_in[ii]; tmp = str_in[ii];
str_in[ii] = str_in[len - ii - 1]; // ii-th char from end str_in[ii] = str_in[len - ii - 1]; // ii-th char from end
str_in[len - ii - 1] = tmp; str_in[len - ii - 1] = tmp;
} }
} }
/* /*
@ -463,13 +462,13 @@ printf("%s\n", c); // => ".tset a si sihT"
//if referring to external variables outside function, must use extern keyword. //if referring to external variables outside function, must use extern keyword.
int i = 0; int i = 0;
void testFunc() { void testFunc() {
extern int i; //i here is now using external variable i extern int i; //i here is now using external variable i
} }
//make external variables private to source file with static: //make external variables private to source file with static:
static int i = 0; //other files using testFunc() cannot access variable i static int j = 0; //other files using testFunc() cannot access variable i
void testFunc() { void testFunc2() {
extern int i; extern int j;
} }
//**You may also declare functions as static to make them private** //**You may also declare functions as static to make them private**
@ -486,8 +485,8 @@ my_type my_type_var = 0;
// Structs are just collections of data, the members are allocated sequentially, // Structs are just collections of data, the members are allocated sequentially,
// in the order they are written: // in the order they are written:
struct rectangle { struct rectangle {
int width; int width;
int height; int height;
}; };
// It's not generally true that // It's not generally true that
@ -497,20 +496,20 @@ struct rectangle {
void function_1() void function_1()
{ {
struct rectangle my_rec; struct rectangle my_rec;
// Access struct members with . // Access struct members with .
my_rec.width = 10; my_rec.width = 10;
my_rec.height = 20; my_rec.height = 20;
// You can declare pointers to structs // You can declare pointers to structs
struct rectangle *my_rec_ptr = &my_rec; struct rectangle *my_rec_ptr = &my_rec;
// Use dereferencing to set struct pointer members... // Use dereferencing to set struct pointer members...
(*my_rec_ptr).width = 30; (*my_rec_ptr).width = 30;
// ... or even better: prefer the -> shorthand for the sake of readability // ... or even better: prefer the -> shorthand for the sake of readability
my_rec_ptr->height = 10; // Same as (*my_rec_ptr).height = 10; my_rec_ptr->height = 10; // Same as (*my_rec_ptr).height = 10;
} }
// You can apply a typedef to a struct for convenience // You can apply a typedef to a struct for convenience
@ -518,14 +517,14 @@ typedef struct rectangle rect;
int area(rect r) int area(rect r)
{ {
return r.width * r.height; return r.width * r.height;
} }
// if you have large structs, you can pass them "by pointer" to avoid copying // if you have large structs, you can pass them "by pointer" to avoid copying
// the whole struct: // the whole struct:
int area(const rect *r) int areaptr(const rect *r)
{ {
return r->width * r->height; return r->width * r->height;
} }
/////////////////////////////////////// ///////////////////////////////////////
@ -540,12 +539,12 @@ However, definition syntax may be initially confusing.
Example: use str_reverse from a pointer Example: use str_reverse from a pointer
*/ */
void str_reverse_through_pointer(char *str_in) { void str_reverse_through_pointer(char *str_in) {
// Define a function pointer variable, named f. // Define a function pointer variable, named f.
void (*f)(char *); // Signature should exactly match the target function. void (*f)(char *); // Signature should exactly match the target function.
f = &str_reverse; // Assign the address for the actual function (determined at run time) f = &str_reverse; // Assign the address for the actual function (determined at run time)
// f = str_reverse; would work as well - functions decay into pointers, similar to arrays // f = str_reverse; would work as well - functions decay into pointers, similar to arrays
(*f)(str_in); // Just calling the function through the pointer (*f)(str_in); // Just calling the function through the pointer
// f(str_in); // That's an alternative but equally valid syntax for calling it. // f(str_in); // That's an alternative but equally valid syntax for calling it.
} }
/* /*
@ -560,36 +559,37 @@ typedef void (*my_fnp_type)(char *);
// my_fnp_type f; // my_fnp_type f;
//Special characters: //Special characters:
'\a' // alert (bell) character /*
'\n' // newline character '\a'; // alert (bell) character
'\t' // tab character (left justifies text) '\n'; // newline character
'\v' // vertical tab '\t'; // tab character (left justifies text)
'\f' // new page (form feed) '\v'; // vertical tab
'\r' // carriage return '\f'; // new page (form feed)
'\b' // backspace character '\r'; // carriage return
'\0' // NULL character. Usually put at end of strings in C. '\b'; // backspace character
// hello\n\0. \0 used by convention to mark end of string. '\0'; // NULL character. Usually put at end of strings in C.
'\\' // backslash // hello\n\0. \0 used by convention to mark end of string.
'\?' // question mark '\\'; // backslash
'\'' // single quote '\?'; // question mark
'\"' // double quote '\''; // single quote
'\xhh' // hexadecimal number. Example: '\xb' = vertical tab character '\"'; // double quote
'\ooo' // octal number. Example: '\013' = vertical tab character '\xhh'; // hexadecimal number. Example: '\xb' = vertical tab character
'\ooo'; // octal number. Example: '\013' = vertical tab character
//print formatting: //print formatting:
"%d" // integer "%d"; // integer
"%3d" // integer with minimum of length 3 digits (right justifies text) "%3d"; // integer with minimum of length 3 digits (right justifies text)
"%s" // string "%s"; // string
"%f" // float "%f"; // float
"%ld" // long "%ld"; // long
"%3.2f" // minimum 3 digits left and 2 digits right decimal float "%3.2f"; // minimum 3 digits left and 2 digits right decimal float
"%7.4s" // (can do with strings too) "%7.4s"; // (can do with strings too)
"%c" // char "%c"; // char
"%p" // pointer "%p"; // pointer
"%x" // hexadecimal "%x"; // hexadecimal
"%o" // octal "%o"; // octal
"%%" // prints % "%%"; // prints %
*/
/////////////////////////////////////// ///////////////////////////////////////
// Order of Evaluation // Order of Evaluation
/////////////////////////////////////// ///////////////////////////////////////