diff --git a/c.html.markdown b/c.html.markdown index 15bfa05e..e5a6520f 100644 --- a/c.html.markdown +++ b/c.html.markdown @@ -27,7 +27,7 @@ void function_2(); // Your program's entry point is a function called // main with an integer return type. -int main(){ +int main() { // print output using printf, for "print formatted" // %d is an integer, \n is a newline @@ -38,36 +38,49 @@ printf("%d\n", 0); // => Prints 0 // Types /////////////////////////////////////// -// Variables must always be declared with a type. +// You have to declare variables before using them. A variable declaration +// requires you to specify its type; a variable's type determines its size +// in bytes. -// 32-bit integer +// ints are usually 4 bytes int x_int = 0; -// 16-bit integer +// shorts are usually 2 bytes short x_short = 0; -// 8-bit integer, aka 1 byte +// chars are guaranteed to be 1 byte char x_char = 0; char y_char = 'y'; // Char literals are quoted with '' -long x_long = 0; // Still 32 bytes for historical reasons -long long x_long_long = 0; // Guaranteed to be at least 64 bytes +// longs are often 4 to 8 bytes; long longs are guaranteed to be at least +// 64 bits +long x_long = 0; +long long x_long_long = 0; -// 32-bit floating-point decimal +// floats are usually 32-bit floating point numbers float x_float = 0.0; -// 64-bit floating-point decimal +// doubles are usually 64-bit floating-point numbers double x_double = 0.0; -// Integer types may be unsigned +// Integral types may be unsigned. This means they can't be negative, but +// the maximum value of an unsigned variable is greater than the maximum +// value of the same size. unsigned char ux_char; unsigned short ux_short; unsigned int ux_int; unsigned long long ux_long_long; +// Other than char, which is always 1 byte, these types vary in size depending +// on your machine. sizeof(T) gives you the size of a variable with type T in +// bytes so you can express the size of these types in a portable way. +// For example, +printf("%d\n", sizeof(int)); // => 4 (on machines with 4-byte words) + // Arrays must be initialized with a concrete size. char my_char_array[20]; // This array occupies 1 * 20 = 20 bytes int my_int_array[20]; // This array occupies 4 * 20 = 80 bytes + // (assuming 4-byte words) // You can initialize an array to 0 thusly: @@ -81,16 +94,20 @@ my_array[0]; // => 0 my_array[1] = 2; printf("%d\n", my_array[1]); // => 2 -// Strings are just lists of chars terminated by a null (0x00) byte. +// Strings are just arrays of chars terminated by a NUL (0x00) byte, +// represented in strings as the special character '\0'. +// (We don't have to include the NUL byte in string literals; the compiler +// inserts it at the end of the array for us.) char a_string[20] = "This is a string"; +printf("%s\n", a_string); // %s formats a string /* You may have noticed that a_string is only 16 chars long. -Char #17 is a null byte, 0x00 aka \0. +Char #17 is the NUL byte. Chars #18, 19 and 20 have undefined values. */ -printf("%d\n", a_string[16]); +printf("%d\n", a_string[16]); => 0 /////////////////////////////////////// // Operators @@ -112,7 +129,8 @@ f1 / f2; // => 0.5, plus or minus epsilon // Comparison operators are probably familiar, but // there is no boolean type in c. We use ints instead. -// 0 is false, anything else is true +// 0 is false, anything else is true. (The comparison +// operators always return 0 or 1.) 3 == 2; // => 0 (false) 3 != 2; // => 1 (true) 3 > 2; // => 1 @@ -140,33 +158,33 @@ f1 / f2; // => 0.5, plus or minus epsilon // Control Structures /////////////////////////////////////// -if(0){ +if (0) { printf("I am never run\n"); -}else if(0){ +} else if (0) { printf("I am also never run\n"); -}else{ +} else { printf("I print\n"); } // While loops exist int ii = 0; -while(ii < 10){ +while (ii < 10) { printf("%d, ", ii++); // ii++ increments ii in-place, after using its value. } // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " printf("\n"); int kk = 0; -do{ +do { printf("%d, ", kk); -}while(++kk < 10); // ++kk increments kk in-place, before using its value +} while (++kk < 10); // ++kk increments kk in-place, before using its value // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " printf("\n"); // For loops too int jj; -for(jj=0; jj < 10; jj++){ +for (jj=0; jj < 10; jj++) { printf("%d, ", jj); } // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " @@ -176,8 +194,8 @@ printf("\n"); // Typecasting /////////////////////////////////////// -// Everything in C is stored somewhere in memory. You can change -// the type of a variable to choose how to read its data +// Every value in C has a type, but you can cast one value into another type +// if you want. int x_hex = 0x01; // You can assign vars with hex literals @@ -188,29 +206,35 @@ printf("%d\n", (char) x_hex); // => Prints 1 // Types will overflow without warning printf("%d\n", (char) 257); // => 1 (Max char = 255) -printf("%d\n", (short) 65537); // => 1 (Max short = 65535) + +// Integral types can be cast to floating-point types, and vice-versa. +printf("%f\n", (float)100); // %f formats a float +printf("%lf\n", (double)100); // %lf formats a double +printf("%d\n", (char)100.0); /////////////////////////////////////// // Pointers /////////////////////////////////////// -// You can retrieve the memory address of your variables, -// then mess with them. +// You can retrieve the memory addresses of your variables and perform +// operations on them. int x = 0; printf("%p\n", &x); // Use & to retrieve the address of a variable // (%p formats a pointer) // => Prints some address in memory; -int x_array[20]; // Arrays are a good way to allocate a contiguous block of memory +int x_array[20]; // Arrays are a good way to allocate a contiguous block + // of memory int xx; -for(xx=0; xx<20; xx++){ +for (xx=0; xx<20; xx++) { x_array[xx] = 20 - xx; } // Initialize x_array to 20, 19, 18,... 2, 1 // Pointer types end with * int* x_ptr = x_array; -// This works because arrays are pointers to their first element. +// x_ptr now points to the first element in the array (the integer 20). +// This works because arrays are actually just pointers to their first element. // Put a * in front to de-reference a pointer and retrieve the value, // of the same type as the pointer, that the pointer is pointing at. @@ -221,33 +245,27 @@ printf("%d\n", x_array[0]); // => Prints 20 printf("%d\n", *(x_ptr + 1)); // => Prints 19 printf("%d\n", x_array[1]); // => Prints 19 -// Array indexes are such a thin wrapper around pointer -// arithmetic that the following works: -printf("%d\n", 0[x_array]); // => Prints 20; -printf("%d\n", 2[x_array]); // => Prints 18; - -// The above is equivalent to: -printf("%d\n", *(0 + x_ptr)); -printf("%d\n", *(2 + x_ptr)); - -// You can give a pointer a block of memory to use with malloc +// You can also dynamically allocate contiguous blocks of memory with the +// standard library function malloc, which takes one integer argument +// representing the number of bytes to allocate from the heap. int* my_ptr = (int*) malloc(sizeof(int) * 20); -for(xx=0; xx<20; xx++){ - *(my_ptr + xx) = 20 - xx; +for (xx=0; xx<20; xx++) { + *(my_ptr + xx) = 20 - xx; // my_ptr[xx] = 20-xx would also work here } // Initialize memory to 20, 19, 18, 17... 2, 1 (as ints) // Dereferencing memory that you haven't allocated gives // unpredictable results printf("%d\n", *(my_ptr + 21)); // => Prints who-knows-what? -// When you're done with a malloc'd block, 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 free(my_ptr); // Strings can be char arrays, but are usually represented as char // pointers: char* my_str = "This is my very own string"; -printf("%d\n", *my_str); // 84 (The ascii value of 'T') +printf("%c\n", *my_str); // => 'T' function_1(); } // end main function