From 162b5bb60dea93dac591005a73956be663313761 Mon Sep 17 00:00:00 2001 From: Sergey Avseyev Date: Wed, 14 Aug 2013 23:15:17 +0300 Subject: Fix typo about how pointers are declared Of course it is completely valid that star can go right after type name, but it might be not that obvious that during declaration the star is associated to the variable, not with the type name. --- c.html.markdown | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'c.html.markdown') diff --git a/c.html.markdown b/c.html.markdown index d243b19d..2b50efa0 100644 --- a/c.html.markdown +++ b/c.html.markdown @@ -230,10 +230,13 @@ printf("%p\n", &x); // Use & to retrieve the address of a variable // (%p formats a pointer) // => Prints some address in memory; -// Pointer types end with * in their declaration -int* px; // px is a pointer to an int + +// Pointers start with * in their declaration +int *px, not_a_pointer; // px is a pointer to an int px = &x; // Stores the address of x in px printf("%p\n", px); // => Prints some address in memory +printf("%d, %d\n", (int)sizeof(px), (int)sizeof(not_a_pointer)); +// => Prints "8, 4" on 64-bit system // To retreive the value at the address a pointer is pointing to, // put * in front to de-reference it. -- cgit v1.2.3 From 7cb78e94d24d7c768868fa4bd575a7572126cbe5 Mon Sep 17 00:00:00 2001 From: dacechavez Date: Thu, 15 Aug 2013 12:12:19 +0200 Subject: Fixed 2 typos around function pointers --- c.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'c.html.markdown') diff --git a/c.html.markdown b/c.html.markdown index 2b50efa0..e8a26056 100644 --- a/c.html.markdown +++ b/c.html.markdown @@ -373,7 +373,7 @@ int area(rect r){ /////////////////////////////////////// /* At runtime, functions are located at known memory addresses. Function pointers are -much likely any other pointer (they just store a memory address), but can be used +much like any other pointer (they just store a memory address), but can be used to invoke functions directly, and to pass handlers (or callback functions) around. However, definition syntax may be initially confusing. @@ -394,7 +394,7 @@ Function pointers are usually typedef'd for simplicity and readability, as follo typedef void (*my_fnp_type)(char *); -// The used when declaring the actual pointer variable: +// Then used when declaring the actual pointer variable: // ... // my_fnp_type f; -- cgit v1.2.3 From 24fd870589d863618e6ba2571b6a0c8339d35edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81rp=C3=A1d=20Goretity=20=EF=A3=BF?= Date: Thu, 15 Aug 2013 12:30:22 +0200 Subject: Update c.html.markdown --- c.html.markdown | 629 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 356 insertions(+), 273 deletions(-) (limited to 'c.html.markdown') diff --git a/c.html.markdown b/c.html.markdown index 2b50efa0..ac6c7ab4 100644 --- a/c.html.markdown +++ b/c.html.markdown @@ -5,19 +5,20 @@ language: c filename: learnc.c contributors: - ["Adam Bard", "http://adambard.com/"] + - ["Árpád Goretity", "http://twitter.com/h2co3_ios"] --- -Ah, C. Still the language of modern high-performance computing. +Ah, C. Still **the** language of modern high-performance computing. C is the lowest-level language most programmers will ever use, but it more than makes up for it with raw speed. Just be aware of its manual memory management and C will take you as far as you need to go. ```c -// Single-line comments start with // +// Single-line comments start with // - only available in C99 and later. /* -Multi-line comments look like this. +Multi-line comments look like this. They work in C89 as well. */ // Import headers with #include @@ -25,6 +26,11 @@ Multi-line comments look like this. #include #include +// file names between are headers from the C standard library. +// They are searched for by the preprocessor in the system include paths +// (usually /usr/lib on Unices, can be controlled with the -I option if you are using GCC or clang.) +// For your + // Declare function signatures in advance in a .h file, or at the top of // your .c file. void function_1(); @@ -33,264 +39,317 @@ void function_2(); // Your program's entry point is a function called // main with an integer return type. int main() { - -// print output using printf, for "print formatted" -// %d is an integer, \n is a newline -printf("%d\n", 0); // => Prints 0 -// All statements must end with a semicolon - -/////////////////////////////////////// -// Types -/////////////////////////////////////// - -// 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. - -// ints are usually 4 bytes -int x_int = 0; - -// shorts are usually 2 bytes -short x_short = 0; - -// chars are guaranteed to be 1 byte -char x_char = 0; -char y_char = 'y'; // Char literals are quoted with '' - -// 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; - -// floats are usually 32-bit floating point numbers -float x_float = 0.0; - -// doubles are usually 64-bit floating-point numbers -double x_double = 0.0; - -// 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 -// signed 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("%lu\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: -char my_array[20] = {0}; - -// Indexing an array is like other languages -- or, -// rather, other languages are like C -my_array[0]; // => 0 - -// Arrays are mutable; it's just memory! -my_array[1] = 2; -printf("%d\n", my_array[1]); // => 2 - -// 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 the NUL byte. -Chars #18, 19 and 20 have undefined values. -*/ - -printf("%d\n", a_string[16]); // => 0 - -/////////////////////////////////////// -// Operators -/////////////////////////////////////// - -int i1 = 1, i2 = 2; // Shorthand for multiple declaration -float f1 = 1.0, f2 = 2.0; - -// Arithmetic is straightforward -i1 + i2; // => 3 -i2 - i1; // => 1 -i2 * i1; // => 2 -i1 / i2; // => 0 (0.5, but truncated towards 0) - -f1 / f2; // => 0.5, plus or minus epsilon - -// Modulo is there as well -11 % 3; // => 2 - -// Comparison operators are probably familiar, but -// there is no boolean type in c. We use ints instead. -// 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 -3 < 2; // => 0 -2 <= 2; // => 1 -2 >= 2; // => 1 - -// Logic works on ints -!3; // => 0 (Logical not) -!0; // => 1 -1 && 1; // => 1 (Logical and) -0 && 1; // => 0 -0 || 1; // => 1 (Logical or) -0 || 0; // => 0 - -// Bitwise operators! -~0x0F; // => 0xF0 (bitwise negation) -0x0F & 0xF0; // => 0x00 (bitwise AND) -0x0F | 0xF0; // => 0xFF (bitwise OR) -0x04 ^ 0x0F; // => 0x0B (bitwise XOR) -0x01 << 1; // => 0x02 (bitwise left shift (by 1)) -0x02 >> 1; // => 0x01 (bitwise right shift (by 1)) - -/////////////////////////////////////// -// Control Structures -/////////////////////////////////////// - -if (0) { - printf("I am never run\n"); -} else if (0) { - printf("I am also never run\n"); -} else { - printf("I print\n"); -} - -// While loops exist -int ii = 0; -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 { - printf("%d, ", kk); -} 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++) { - printf("%d, ", jj); -} // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " - -printf("\n"); - -/////////////////////////////////////// -// Typecasting -/////////////////////////////////////// - -// 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 - -// Casting between types will attempt to preserve their numeric values -printf("%d\n", x_hex); // => Prints 1 -printf("%d\n", (short) x_hex); // => Prints 1 -printf("%d\n", (char) x_hex); // => Prints 1 - -// Types will overflow without warning -printf("%d\n", (char) 257); // => 1 (Max char = 255) - -// 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 -/////////////////////////////////////// - -// 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 -// of your variables, then mess with 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; - - -// Pointers start with * in their declaration -int *px, not_a_pointer; // px is a pointer to an int -px = &x; // Stores the address of x in px -printf("%p\n", px); // => Prints some address in memory -printf("%d, %d\n", (int)sizeof(px), (int)sizeof(not_a_pointer)); -// => Prints "8, 4" on 64-bit system - -// To retreive the value at the address a pointer is pointing to, -// put * in front to de-reference it. -printf("%d\n", *px); // => Prints 0, the value of x, which is what px is pointing to the address of - -// You can also change the value the pointer is pointing to. -// We'll have to wrap the de-reference in parenthesis because -// ++ has a higher precedence than *. -(*px)++; // Increment the value px is pointing to by 1 -printf("%d\n", *px); // => Prints 1 -printf("%d\n", x); // => Prints 1 - -int x_array[20]; // Arrays are a good way to allocate a contiguous block of memory -int xx; -for (xx=0; xx<20; xx++) { - x_array[xx] = 20 - xx; -} // Initialize x_array to 20, 19, 18,... 2, 1 - -// Declare a pointer of type int and initialize it to point to x_array -int* x_ptr = x_array; -// 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. - -// Arrays are pointers to their first element -printf("%d\n", *(x_ptr)); // => Prints 20 -printf("%d\n", x_array[0]); // => Prints 20 - -// Pointers are incremented and decremented based on their type -printf("%d\n", *(x_ptr + 1)); // => Prints 19 -printf("%d\n", x_array[1]); // => Prints 19 - -// 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; // 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 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("%c\n", *my_str); // => 'T' - -function_1(); + // print output using printf, for "print formatted" + // %d is an integer, \n is a newline + printf("%d\n", 0); // => Prints 0 + // All statements must end with a semicolon + + /////////////////////////////////////// + // Types + /////////////////////////////////////// + + // 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. + + // ints are usually 4 bytes + int x_int = 0; + + // shorts are usually 2 bytes + short x_short = 0; + + // chars are guaranteed to be 1 byte + char x_char = 0; + char y_char = 'y'; // Char literals are quoted with '' + + // 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; + + // floats are usually 32-bit floating point numbers + float x_float = 0.0; + + // doubles are usually 64-bit floating-point numbers + double x_double = 0.0; + + // 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 + // signed 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 (but not necessarily 8 bits!), + // these types vary in size depending on your machine and compiler. + // 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. + // sizeof(obj) yields the size of an actual expression (variable, literal, etc.). + // For example, + printf("%zu\n", sizeof(int)); // => 4 (on most machines with 4-byte words) + + + // It's worth noting that if the argument of the `sizeof` operator is not a type but an expression, + // then its argument is not evaluated except VLAs (see below). Also, `sizeof()` is an operator, not a function, + // furthermore, the value it yields is a compile-time constant (except when used on VLAs, again.) + int a = 1; + size_t size = sizeof(a++); // a++ is not evaluated + printf("sizeof(a++) = %zu where a = %d\n", size, a); + // the above code prints "sizeof(a++) = 4 where a = 1" (on a usual 32-bit architecture) + + // 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: + char my_array[20] = {0}; + + // Indexing an array is like other languages -- or, + // rather, other languages are like C + my_array[0]; // => 0 + + // Arrays are mutable; it's just memory! + my_array[1] = 2; + printf("%d\n", my_array[1]); // => 2 + + // 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 time constant: + printf("Enter the array size: "); // ask the user for an array size + char buf[0x100]; + fgets(buf, sizeof buf, stdin); + size_t size = strtoul(buf, NULL, 10); // strtoul parses a string to an unsigned integer + int var_length_array[size]; // declare the VLA + printf("sizeof array = %zu\n", sizeof var_length_array); + + // A possible outcome of this program may be: + Enter the array size: 10 + sizeof array = 40 + + // 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 the NUL byte. + Chars #18, 19 and 20 are 0 as well - if an initializer list (in this case, the string literal) + has less elements than the array it is initializing, then excess array elements are implicitly + initialized to zero. This is why int ar[10] = { 0 } works as expected intuitively. + */ + + printf("%d\n", a_string[16]); // => 0 + + // So string literals are strings enclosed within double quotes, but if we have characters + // between single quotes, that's a character literal. + // It's of type `int`, and *not* `char` (for hystorical reasons). + int cha = 'a'; // fine + char chb = 'a'; // fine too (implicit conversion from int to char - truncation) + + /////////////////////////////////////// + // Operators + /////////////////////////////////////// + + int i1 = 1, i2 = 2; // Shorthand for multiple declaration + float f1 = 1.0, f2 = 2.0; + + // Arithmetic is straightforward + i1 + i2; // => 3 + i2 - i1; // => 1 + i2 * i1; // => 2 + i1 / i2; // => 0 (0.5, but truncated towards 0) + + f1 / f2; // => 0.5, plus or minus epsilon - floating-point numbers and calculations are not exact + + // Modulo is there as well + 11 % 3; // => 2 + + // Comparison operators are probably familiar, but + // there is no boolean type in c. We use ints instead. + // 0 is false, anything else is true. (The comparison + // operators always yield 0 or 1.) + 3 == 2; // => 0 (false) + 3 != 2; // => 1 (true) + 3 > 2; // => 1 + 3 < 2; // => 0 + 2 <= 2; // => 1 + 2 >= 2; // => 1 + + // Logic works on ints + !3; // => 0 (Logical not) + !0; // => 1 + 1 && 1; // => 1 (Logical and) + 0 && 1; // => 0 + 0 || 1; // => 1 (Logical or) + 0 || 0; // => 0 + + // Bitwise operators! + ~0x0F; // => 0xF0 (bitwise negation, "1's complement") + 0x0F & 0xF0; // => 0x00 (bitwise AND) + 0x0F | 0xF0; // => 0xFF (bitwise OR) + 0x04 ^ 0x0F; // => 0x0B (bitwise XOR) + 0x01 << 1; // => 0x02 (bitwise left shift (by 1)) + 0x02 >> 1; // => 0x01 (bitwise right shift (by 1)) + + /////////////////////////////////////// + // Control Structures + /////////////////////////////////////// + + if (0) { + printf("I am never run\n"); + } else if (0) { + printf("I am also never run\n"); + } else { + printf("I print\n"); + } + + // While loops exist + int ii = 0; + while (ii < 10) { + printf("%d, ", ii++); // ii++ increments ii in-place, after yielding its value ("postincrement"). + } // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " + + printf("\n"); + + int kk = 0; + do { + printf("%d, ", kk); + } while (++kk < 10); // ++kk increments kk in-place, and yields the already incremented value ("preincrement") + // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " + + printf("\n"); + + // For loops too + int jj; + for (jj=0; jj < 10; jj++) { + printf("%d, ", jj); + } // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " + + printf("\n"); + + /////////////////////////////////////// + // Typecasting + /////////////////////////////////////// + + // Every value in C has a type, but you can cast one value into another type + // if you want (with some constraints). + + int x_hex = 0x01; // You can assign vars with hex literals + + // Casting between types will attempt to preserve their numeric values + printf("%d\n", x_hex); // => Prints 1 + printf("%d\n", (short) x_hex); // => Prints 1 + printf("%d\n", (char) x_hex); // => Prints 1 + + // 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); would be undefined behavior - `char' is usually signed + // on most modern systems, and signed integer overflow invokes UB. + // Also, for determining the maximal value of a `char`, a `signed char` and an `unisigned char`, + // respectively, use the CHAR_MAX, SCHAR_MAX and UCHAR_MAX macros from + + // 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 + /////////////////////////////////////// + + // 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 + // of your variables, then mess with them. + + int x = 0; + printf("%p\n", (void *)&x); // Use & to retrieve the address of a variable + // (%p formats an object pointer of type void *) + // => Prints some address in memory; + + + // Pointers start with * in their declaration + int *px, not_a_pointer; // px is a pointer to an int + px = &x; // Stores the address of x in px + printf("%p\n", (void *)px); // => Prints some address in memory + printf("%zu, %zu\n", sizeof(px), sizeof(not_a_pointer)); + // => Prints "8, 4" on a typical 64-bit system + + // To retreive the value at the address a pointer is pointing to, + // put * in front to de-reference it. + // Note: yes, it may be confusing that '*' is used for _both_ declaring a pointer and dereferencing it. + printf("%d\n", *px); // => Prints 0, the value of x, which is what px is pointing to the address of + + // You can also change the value the pointer is pointing to. + // We'll have to wrap the de-reference in parenthesis because + // ++ has a higher precedence than *. + (*px)++; // Increment the value px is pointing to by 1 + printf("%d\n", *px); // => Prints 1 + printf("%d\n", x); // => Prints 1 + + int x_array[20]; // Arrays are a good way to allocate a contiguous block of memory + int xx; + for (xx = 0; xx < 20; xx++) { + x_array[xx] = 20 - xx; + } // Initialize x_array to 20, 19, 18,... 2, 1 + + // Declare a pointer of type int and initialize it to point to x_array + int* x_ptr = x_array; + // 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. + // For example, when an array is passed to a function or is assigned to a pointer, + // it decays into (implicitly converted to) a pointer. + // Exceptions: when the array is the argument of the `&` (address-od) operator: + int arr[10]; + int (*ptr_to_arr)[10] = &arr; // &arr is NOT of type `int *`! 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: + char arr[] = "foobarbazquirk"; + // or when it's the argument of the `sizeof` or `alignof` operator: + int arr[10]; + int *ptr = arr; // equivalent with int *ptr = &arr[0]; + printf("%zu %zu\n", sizeof arr, sizeof ptr); // probably prints "40, 4" or "40, 8" + + + // Pointers are incremented and decremented based on their type + // (this is called pointer arithmetic) + printf("%d\n", *(x_ptr + 1)); // => Prints 19 + printf("%d\n", x_array[1]); // => Prints 19 + + // You can also dynamically allocate contiguous blocks of memory with the + // 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 + // may not be true on e. g. embedded systems - the C standard says nothing about it). + int *my_ptr = malloc(sizeof(*my_ptr) * 20); + for (xx = 0; xx < 20; xx++) { + *(my_ptr + xx) = 20 - xx; // my_ptr[xx] = 20-xx would also work here, and it's also more readable + } // Initialize memory to 20, 19, 18, 17... 2, 1 (as ints) + + // Dereferencing memory that you haven't allocated gives + // "unpredictable results" - the program is said to invoke "undefined behavior" + 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, + // or else no one else can use it until your program terminates + // (this is called a "memory leak"): + free(my_ptr); + + // 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). + // 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.) + const char *my_str = "This is my very own string literal"; + printf("%c\n", *my_str); // => 'T' + + // This is not the case if the string is an array (potentially initialized with a string literal) + // that resides in writable memory, as in: + char foo[] = "foo"; + foo[0] = 'a'; // this is legal, foo now contains "aoo" + + function_1(); } // end main function /////////////////////////////////////// @@ -300,7 +359,8 @@ function_1(); // Function declaration syntax: // () -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 } @@ -312,10 +372,12 @@ Example: in-place string reversal */ // A void function returns no value -void str_reverse(char* str_in){ +void str_reverse(char *str_in) +{ char tmp; - int ii=0, len = strlen(str_in); // Strlen is part of the c standard library - for(ii=0; ii ".tset a si sihT" typedef int my_type; my_type my_type_var = 0; -// Structs are just collections of data +// Structs are just collections of data, the members are allocated sequentially, in the order they are written: struct rectangle { int width; int height; }; +// it's generally not true that sizeof(struct rectangle) == sizeof(int) + sizeof(int) due to +// potential padding between the structure members (this is for alignment reasons. Probably won't +// happen if all members are of the same type, but watch out! +// See http://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member +// for further information. -void function_1(){ - +void function_1() +{ struct rectangle my_rec; // Access struct members with . @@ -352,22 +419,29 @@ void function_1(){ my_rec.height = 20; // 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... (*my_rec_ptr).width = 30; - // ... or use the -> shorthand + // ... or even better: prefer the -> shorthand for the sake of readability my_rec_ptr->height = 10; // Same as (*my_rec_ptr).height = 10; } // You can apply a typedef to a struct for convenience typedef struct rectangle rect; -int area(rect r){ +int area(rect r) +{ return r.width * r.height; } +// if you have large structs, you can pass them "by pointer" to avoid copying the whole struct: +int area(const rect *r) +{ + return r->width * r->height; +} + /////////////////////////////////////// // Function pointers /////////////////////////////////////// @@ -379,10 +453,11 @@ However, definition syntax may be initially confusing. 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. void (*f)(char *); // Signature should exactly match the target function. f = &str_reverse; // Assign the address for the actual function (determined at runtime) + // 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); // That's an alternative but equally valid syntax for calling it. } @@ -403,7 +478,15 @@ typedef void (*my_fnp_type)(char *); ## Further Reading Best to find yourself a copy of [K&R, aka "The C Programming Language"](https://en.wikipedia.org/wiki/The_C_Programming_Language) +It is *the* book about C, written by the creators of C. Be careful, though - it's ancient and it contains some +inaccuracies (well, ideas that are not considered good anymore) or now-changed practices. + +Another good resource is [Learn C the hard way](http://c.learncodethehardway.org/book/). + +If you have a question, read the [compl.lang.c Frequently Asked Questions](http://c-faq.com). -Another good resource is [Learn C the hard way](http://c.learncodethehardway.org/book/) +It's very important to use proper spacing, indentation and to be consistent with your coding style in general. +Readable code is better than clever code and fast code. For a good, sane coding style to adopt, see the +[Linux kernel coding stlye](https://www.kernel.org/doc/Documentation/CodingStyle). Other than that, Google is your friend. -- cgit v1.2.3 From f2c41a8a9915606ed801a87f153d91fd347069a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81rp=C3=A1d=20Goretity=20=EF=A3=BF?= Date: Thu, 15 Aug 2013 12:36:29 +0200 Subject: whoops, fixed typos and added missing info --- c.html.markdown | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'c.html.markdown') diff --git a/c.html.markdown b/c.html.markdown index ac6c7ab4..ec5c5e0d 100644 --- a/c.html.markdown +++ b/c.html.markdown @@ -5,7 +5,6 @@ language: c filename: learnc.c contributors: - ["Adam Bard", "http://adambard.com/"] - - ["Árpád Goretity", "http://twitter.com/h2co3_ios"] --- Ah, C. Still **the** language of modern high-performance computing. @@ -29,7 +28,13 @@ Multi-line comments look like this. They work in C89 as well. // file names between are headers from the C standard library. // They are searched for by the preprocessor in the system include paths // (usually /usr/lib on Unices, can be controlled with the -I option if you are using GCC or clang.) -// For your +// For your own headers, use double quotes instead of angle brackets: +#include "my_header.h" + +// The C preprocessor introduces an almost fully-featured macro language. It's useful, but +// it can be confusing (and what's even worse, it can be misused). Read the +// Wikipedia article on the C preprocessor for further information: +// http://en.wikipedia.org/wiki/C_preprocessor // Declare function signatures in advance in a .h file, or at the top of // your .c file. -- cgit v1.2.3 From 273c08d1fe2ebe362826fd0707d55b728538c33d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81rp=C3=A1d=20Goretity=20=EF=A3=BF?= Date: Thu, 15 Aug 2013 13:08:52 +0200 Subject: fixed header, added switch statement --- c.html.markdown | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) (limited to 'c.html.markdown') diff --git a/c.html.markdown b/c.html.markdown index ec5c5e0d..8e16837c 100644 --- a/c.html.markdown +++ b/c.html.markdown @@ -1,10 +1,12 @@ --- -name: c -category: language -language: c -filename: learnc.c -contributors: - - ["Adam Bard", "http://adambard.com/"] +- name: c +- category: language +- language: c +- filename: learnc.c +- contributors: + - [Adam Bard](http://adambard.com/) + - [Árpád Goretity](http://twitter.com/H2CO3_iOS) + --- Ah, C. Still **the** language of modern high-performance computing. @@ -152,7 +154,7 @@ int main() { // So string literals are strings enclosed within double quotes, but if we have characters // between single quotes, that's a character literal. - // It's of type `int`, and *not* `char` (for hystorical reasons). + // It's of type `int`, and *not* `char` (for historical reasons). int cha = 'a'; // fine char chb = 'a'; // fine too (implicit conversion from int to char - truncation) @@ -176,6 +178,7 @@ int main() { // Comparison operators are probably familiar, but // there is no boolean type in c. We use ints instead. + // (Or _Bool or bool in C99.) // 0 is false, anything else is true. (The comparison // operators always yield 0 or 1.) 3 == 2; // => 0 (false) @@ -185,6 +188,13 @@ int main() { 2 <= 2; // => 1 2 >= 2; // => 1 + // C is not Python - comparisons don't chain. + int a = 1; + // WRONG: + int between_0_and_2 = 0 < a < 2; + // Correct: + int between_0_and_2 = 0 < a && a < 2; + // Logic works on ints !3; // => 0 (Logical not) !0; // => 1 @@ -201,6 +211,12 @@ int main() { 0x01 << 1; // => 0x02 (bitwise left shift (by 1)) 0x02 >> 1; // => 0x01 (bitwise right shift (by 1)) + // Be careful when shifting signed integers - the following are all undefined behavior: + // - shifting into the sign bit of a signed integer (int a = 1 << 32) + // - left-shifting a negative number (int a = -1 << 2) + // - shifting by an offset which is more than or equal to the width of the type of the LHS: + // int a = 1 << 32; // UB if int is 32 bits wide + /////////////////////////////////////// // Control Structures /////////////////////////////////////// @@ -237,6 +253,22 @@ int main() { printf("\n"); + // branching with multiple choices: switch() + switch (some_integral_expression) { + case 0: // labels need to be integral *constant* epxressions + do_stuff(); + break; // if you don't break, control flow falls over labels - you usually don't want that. + case 1: + do_something_else(); + break; + default: + // if `some_integral_expression` didn't match any of the labels + fputs("error!\n", stderr); + exit(-1); + break; + } + + /////////////////////////////////////// // Typecasting /////////////////////////////////////// -- cgit v1.2.3 From 8aa0ab6068a13491ec9cba59bce32911ab9f8061 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 16 Aug 2013 09:44:22 -0700 Subject: Edits --- c.html.markdown | 250 ++++++++++++++++++++++++++------------------------------ 1 file changed, 117 insertions(+), 133 deletions(-) (limited to 'c.html.markdown') diff --git a/c.html.markdown b/c.html.markdown index 00b13cb0..24a96463 100644 --- a/c.html.markdown +++ b/c.html.markdown @@ -1,11 +1,9 @@ --- -- name: c -- category: language -- language: c -- filename: learnc.c -- contributors: - - [Adam Bard](http://adambard.com/) - - [Árpád Goretity](http://twitter.com/H2CO3_iOS) +language: c +filename: learnc.c +contributors: + - ["Adam Bard", "http://adambard.com/"] + - ["Árpád Goretity", "http://twitter.com/H2CO3_iOS"] --- @@ -27,17 +25,10 @@ Multi-line comments look like this. They work in C89 as well. #include #include -// file names between are headers from the C standard library. -// They are searched for by the preprocessor in the system include paths -// (usually /usr/lib on Unices, can be controlled with the -I option if you are using GCC or clang.) +// (File names between are headers from the C standard library.) // For your own headers, use double quotes instead of angle brackets: #include "my_header.h" -// The C preprocessor introduces an almost fully-featured macro language. It's useful, but -// it can be confusing (and what's even worse, it can be misused). Read the -// Wikipedia article on the C preprocessor for further information: -// http://en.wikipedia.org/wiki/C_preprocessor - // Declare function signatures in advance in a .h file, or at the top of // your .c file. void function_1(); @@ -50,132 +41,117 @@ int main() { // %d is an integer, \n is a newline printf("%d\n", 0); // => Prints 0 // All statements must end with a semicolon - + /////////////////////////////////////// // Types /////////////////////////////////////// - - // 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. - + // ints are usually 4 bytes int x_int = 0; - + // shorts are usually 2 bytes short x_short = 0; - + // chars are guaranteed to be 1 byte char x_char = 0; char y_char = 'y'; // Char literals are quoted with '' - + // 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; - + // floats are usually 32-bit floating point numbers float x_float = 0.0; - + // doubles are usually 64-bit floating-point numbers double x_double = 0.0; - - // 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 - // signed value of the same size. - unsigned char ux_char; + + // Integral types may be unsigned. unsigned short ux_short; unsigned int ux_int; unsigned long long ux_long_long; - - // Other than char, which is always 1 byte (but not necessarily 8 bits!), - // these types vary in size depending on your machine and compiler. - // 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. - // sizeof(obj) yields the size of an actual expression (variable, literal, etc.). - // For example, + + // 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.). printf("%zu\n", sizeof(int)); // => 4 (on most machines with 4-byte words) - - - // It's worth noting that if the argument of the `sizeof` operator is not a type but an expression, - // then its argument is not evaluated except VLAs (see below). Also, `sizeof()` is an operator, not a function, - // furthermore, the value it yields is a compile-time constant (except when used on VLAs, again.) + + + // If the argument of the `sizeof` operator an expression, then its argument + // is not evaluated (except VLAs (see below)). + // The value it yields in this case is a compile-time constant. int a = 1; size_t size = sizeof(a++); // a++ is not evaluated printf("sizeof(a++) = %zu where a = %d\n", size, a); - // the above code prints "sizeof(a++) = 4 where a = 1" (on a usual 32-bit architecture) - + // prints "sizeof(a++) = 4 where a = 1" (on a 32-bit architecture) + // 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: char my_array[20] = {0}; - + // Indexing an array is like other languages -- or, // rather, other languages are like C my_array[0]; // => 0 - + // Arrays are mutable; it's just memory! my_array[1] = 2; printf("%d\n", my_array[1]); // => 2 - - // 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 time constant: + + // 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 + // time constant: printf("Enter the array size: "); // ask the user for an array size char buf[0x100]; fgets(buf, sizeof buf, stdin); - size_t size = strtoul(buf, NULL, 10); // strtoul parses a string to an unsigned integer + + // strtoul parses a string to an unsigned integer + size_t size = strtoul(buf, NULL, 10); int var_length_array[size]; // declare the VLA printf("sizeof array = %zu\n", sizeof var_length_array); - + // A possible outcome of this program may be: - Enter the array size: 10 - sizeof array = 40 - + // > Enter the array size: 10 + // > sizeof array = 40 + // 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 the NUL byte. - Chars #18, 19 and 20 are 0 as well - if an initializer list (in this case, the string literal) - has less elements than the array it is initializing, then excess array elements are implicitly - initialized to zero. This is why int ar[10] = { 0 } works as expected intuitively. - */ - + printf("%d\n", a_string[16]); // => 0 - - // So string literals are strings enclosed within double quotes, but if we have characters - // between single quotes, that's a character literal. + // i.e., byte #17 is 0 (as are 18, 19, and 20) + + // If we have characters between single quotes, that's a character literal. // It's of type `int`, and *not* `char` (for historical reasons). int cha = 'a'; // fine - char chb = 'a'; // fine too (implicit conversion from int to char - truncation) - + char chb = 'a'; // fine too (implicit conversion from int to char) + /////////////////////////////////////// // Operators /////////////////////////////////////// - + int i1 = 1, i2 = 2; // Shorthand for multiple declaration float f1 = 1.0, f2 = 2.0; - + // Arithmetic is straightforward i1 + i2; // => 3 i2 - i1; // => 1 i2 * i1; // => 2 i1 / i2; // => 0 (0.5, but truncated towards 0) - - f1 / f2; // => 0.5, plus or minus epsilon - floating-point numbers and calculations are not exact - + + f1 / f2; // => 0.5, plus or minus epsilon + // Floating-point numbers and calculations are not exact + // Modulo is there as well 11 % 3; // => 2 - + // Comparison operators are probably familiar, but // there is no boolean type in c. We use ints instead. // (Or _Bool or bool in C99.) @@ -187,14 +163,14 @@ int main() { 3 < 2; // => 0 2 <= 2; // => 1 2 >= 2; // => 1 - + // C is not Python - comparisons don't chain. int a = 1; // WRONG: int between_0_and_2 = 0 < a < 2; // Correct: int between_0_and_2 = 0 < a && a < 2; - + // Logic works on ints !3; // => 0 (Logical not) !0; // => 1 @@ -202,7 +178,7 @@ int main() { 0 && 1; // => 0 0 || 1; // => 1 (Logical or) 0 || 0; // => 0 - + // Bitwise operators! ~0x0F; // => 0xF0 (bitwise negation, "1's complement") 0x0F & 0xF0; // => 0x00 (bitwise AND) @@ -210,17 +186,17 @@ int main() { 0x04 ^ 0x0F; // => 0x0B (bitwise XOR) 0x01 << 1; // => 0x02 (bitwise left shift (by 1)) 0x02 >> 1; // => 0x01 (bitwise right shift (by 1)) - - // Be careful when shifting signed integers - the following are all undefined behavior: + + // Be careful when shifting signed integers - the following are undefined: // - shifting into the sign bit of a signed integer (int a = 1 << 32) // - left-shifting a negative number (int a = -1 << 2) - // - shifting by an offset which is more than or equal to 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 - + /////////////////////////////////////// // Control Structures /////////////////////////////////////// - + if (0) { printf("I am never run\n"); } else if (0) { @@ -228,36 +204,38 @@ int main() { } else { printf("I print\n"); } - + // While loops exist int ii = 0; while (ii < 10) { - printf("%d, ", ii++); // ii++ increments ii in-place, after yielding its value ("postincrement"). + printf("%d, ", ii++); // ii++ increments ii in-place + // after yielding its value ("postincrement"). } // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " - + printf("\n"); - + int kk = 0; do { printf("%d, ", kk); - } while (++kk < 10); // ++kk increments kk in-place, and yields the already incremented value ("preincrement") + } while (++kk < 10); // ++kk increments kk in-place, and yields + // the already incremented value ("preincrement") // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " - + printf("\n"); - + // For loops too int jj; for (jj=0; jj < 10; jj++) { printf("%d, ", jj); } // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " - + printf("\n"); - + // branching with multiple choices: switch() switch (some_integral_expression) { case 0: // labels need to be integral *constant* epxressions do_stuff(); - break; // if you don't break, control flow falls over labels - you usually don't want that. + break; // if you don't break, control flow falls over labels case 1: do_something_else(); break; @@ -267,73 +245,74 @@ int main() { exit(-1); break; } - + /////////////////////////////////////// // Typecasting /////////////////////////////////////// - + // Every value in C has a type, but you can cast one value into another type // if you want (with some constraints). - + int x_hex = 0x01; // You can assign vars with hex literals - + // Casting between types will attempt to preserve their numeric values printf("%d\n", x_hex); // => Prints 1 printf("%d\n", (short) x_hex); // => Prints 1 printf("%d\n", (char) x_hex); // => Prints 1 - + // 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); would be undefined behavior - `char' is usually signed - // on most modern systems, and signed integer overflow invokes UB. - // Also, for determining the maximal value of a `char`, a `signed char` and an `unisigned char`, + + // For determining the max value of a `char`, a `signed char` and an `unisigned char`, // respectively, use the CHAR_MAX, SCHAR_MAX and UCHAR_MAX macros from - + // 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 /////////////////////////////////////// - + // 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 // of your variables, then mess with them. - + int x = 0; printf("%p\n", (void *)&x); // Use & to retrieve the address of a variable // (%p formats an object pointer of type void *) // => Prints some address in memory; - - + + // Pointers start with * in their declaration int *px, not_a_pointer; // px is a pointer to an int px = &x; // Stores the address of x in px printf("%p\n", (void *)px); // => Prints some address in memory printf("%zu, %zu\n", sizeof(px), sizeof(not_a_pointer)); // => Prints "8, 4" on a typical 64-bit system - + // To retreive the value at the address a pointer is pointing to, // put * in front to de-reference it. - // Note: yes, it may be confusing that '*' is used for _both_ declaring a pointer and dereferencing it. - printf("%d\n", *px); // => Prints 0, the value of x, which is what px is pointing to the address of - + // Note: yes, it may be confusing that '*' is used for _both_ declaring a + // pointer and dereferencing it. + printf("%d\n", *px); // => Prints 0, the value of x + // You can also change the value the pointer is pointing to. // We'll have to wrap the de-reference in parenthesis because // ++ has a higher precedence than *. (*px)++; // Increment the value px is pointing to by 1 printf("%d\n", *px); // => Prints 1 printf("%d\n", x); // => Prints 1 - - int x_array[20]; // 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]; int xx; for (xx = 0; xx < 20; xx++) { x_array[xx] = 20 - xx; } // Initialize x_array to 20, 19, 18,... 2, 1 - + // Declare a pointer of type int and initialize it to point to x_array int* x_ptr = x_array; // x_ptr now points to the first element in the array (the integer 20). @@ -342,50 +321,52 @@ int main() { // it decays into (implicitly converted to) a pointer. // Exceptions: when the array is the argument of the `&` (address-od) operator: int arr[10]; - int (*ptr_to_arr)[10] = &arr; // &arr is NOT of type `int *`! It's of type "pointer to array" (of ten `int`s). + int (*ptr_to_arr)[10] = &arr; // &arr is NOT of type `int *`! + // 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: char arr[] = "foobarbazquirk"; // or when it's the argument of the `sizeof` or `alignof` operator: int arr[10]; int *ptr = arr; // equivalent with int *ptr = &arr[0]; printf("%zu %zu\n", sizeof arr, sizeof ptr); // probably prints "40, 4" or "40, 8" - + // Pointers are incremented and decremented based on their type // (this is called pointer arithmetic) printf("%d\n", *(x_ptr + 1)); // => Prints 19 printf("%d\n", x_array[1]); // => Prints 19 - + // You can also dynamically allocate contiguous blocks of memory with the // 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 // may not be true on e. g. embedded systems - the C standard says nothing about it). int *my_ptr = malloc(sizeof(*my_ptr) * 20); for (xx = 0; xx < 20; xx++) { - *(my_ptr + xx) = 20 - xx; // my_ptr[xx] = 20-xx would also work here, and it's also more readable + *(my_ptr + xx) = 20 - xx; // my_ptr[xx] = 20-xx } // Initialize memory to 20, 19, 18, 17... 2, 1 (as ints) - + // Dereferencing memory that you haven't allocated gives // "unpredictable results" - the program is said to invoke "undefined behavior" 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, // or else no one else can use it until your program terminates // (this is called a "memory leak"): free(my_ptr); - + // 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). // 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.) const char *my_str = "This is my very own string literal"; printf("%c\n", *my_str); // => 'T' - - // This is not the case if the string is an array (potentially initialized with a string literal) + + // This is not the case if the string is an array + // (potentially initialized with a string literal) // that resides in writable memory, as in: char foo[] = "foo"; foo[0] = 'a'; // this is legal, foo now contains "aoo" - + function_1(); } // end main function @@ -435,17 +416,17 @@ printf("%s\n", c); // => ".tset a si sihT" typedef int my_type; my_type my_type_var = 0; -// Structs are just collections of data, the members are allocated sequentially, in the order they are written: +// Structs are just collections of data, the members are allocated sequentially, +// in the order they are written: struct rectangle { int width; int height; }; -// it's generally not true that sizeof(struct rectangle) == sizeof(int) + sizeof(int) due to -// potential padding between the structure members (this is for alignment reasons. Probably won't -// happen if all members are of the same type, but watch out! -// See http://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member -// for further information. +// It's not generally true that +// sizeof(struct rectangle) == sizeof(int) + sizeof(int) +// due to potential padding between the structure members (this is for alignment +// reasons). [1] void function_1() { @@ -473,7 +454,8 @@ int area(rect r) return r.width * r.height; } -// if you have large structs, you can pass them "by pointer" to avoid copying the whole struct: +// if you have large structs, you can pass them "by pointer" to avoid copying +// the whole struct: int area(const rect *r) { return r->width * r->height; @@ -527,3 +509,5 @@ Readable code is better than clever code and fast code. For a good, sane coding [Linux kernel coding stlye](https://www.kernel.org/doc/Documentation/CodingStyle). Other than that, Google is your friend. + +[1] http://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member -- cgit v1.2.3