diff options
Diffstat (limited to 'c.html.markdown')
| -rw-r--r-- | c.html.markdown | 250 | 
1 files changed, 117 insertions, 133 deletions
| 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 <stdio.h>  #include <string.h> -// file names between <angle brackets> 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<dir> option if you are using GCC or clang.) +// (File names between <angle brackets> 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 <limits.h> -     +      // 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 | 
