diff options
Diffstat (limited to 'c.html.markdown')
-rw-r--r-- | c.html.markdown | 218 |
1 files changed, 156 insertions, 62 deletions
diff --git a/c.html.markdown b/c.html.markdown index 7975a1c2..ef341abf 100644 --- a/c.html.markdown +++ b/c.html.markdown @@ -1,15 +1,16 @@ --- -language: c +language: C filename: learnc.c contributors: - - ["Adam Bard", "http://adambard.com/"] - - ["Árpád Goretity", "http://twitter.com/H2CO3_iOS"] - - ["Jakub Trzebiatowski", "http://cbs.stgn.pl"] - - ["Marco Scannadinari", "https://marcoms.github.io"] - - ["Zachary Ferguson", "https://github.io/zfergus2"] - - ["himanshu", "https://github.com/himanshu81494"] - - ["Joshua Li", "https://github.com/JoshuaRLi"] - - ["Dragos B. Chirila", "https://github.com/dchirila"] + - ["Adam Bard", "http://adambard.com/"] + - ["Árpád Goretity", "http://twitter.com/H2CO3_iOS"] + - ["Jakub Trzebiatowski", "http://cbs.stgn.pl"] + - ["Marco Scannadinari", "https://marcoms.github.io"] + - ["Zachary Ferguson", "https://github.io/zfergus2"] + - ["himanshu", "https://github.com/himanshu81494"] + - ["Joshua Li", "https://github.com/JoshuaRLi"] + - ["Dragos B. Chirila", "https://github.com/dchirila"] + - ["Heitor P. de Bittencourt", "https://github.com/heitorPB/"] --- Ah, C. Still **the** language of modern high-performance computing. @@ -45,31 +46,47 @@ Multi-line comments don't nest /* Be careful */ // comment ends on this line... // Enumeration constants are also ways to declare constants. // All statements must end with a semicolon -enum days {SUN = 1, MON, TUE, WED, THU, FRI, SAT}; +enum days {SUN, MON, TUE, WED, THU, FRI, SAT}; +// SUN gets 0, MON gets 1, TUE gets 2, etc. + +// Enumeration values can also be specified +enum days {SUN = 1, MON, TUE, WED = 99, THU, FRI, SAT}; // MON gets 2 automatically, TUE gets 3, etc. +// WED get 99, THU gets 100, FRI gets 101, etc. // Import headers with #include #include <stdlib.h> #include <stdio.h> #include <string.h> -// (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" +// File names between <angle brackets> tell the compiler to look in your system +// libraries for the headers. +// For your own headers, use double quotes instead of angle brackets, and +// provide the path: +#include "my_header.h" // local file +#include "../my_lib/my_lib_header.h" //relative path // Declare function signatures in advance in a .h file, or at the top of // your .c file. void function_1(); int function_2(void); -// Must declare a 'function prototype' before main() when functions occur after -// your main() function. +// At a minimum, you must declare a 'function prototype' before its use in any function. +// Normally, prototypes are placed at the top of a file before any function definition. int add_two_ints(int x1, int x2); // function prototype // although `int add_two_ints(int, int);` is also valid (no need to name the args), // it is recommended to name arguments in the prototype as well for easier inspection -// Your program's entry point is a function called -// main with an integer return type. +// Function prototypes are not necessary if the function definition comes before +// any other function that calls that function. However, it's standard practice to +// always add the function prototype to a header file (*.h) and then #include that +// file at the top. This prevents any issues where a function might be called +// before the compiler knows of its existence, while also giving the developer a +// clean header file to share with the rest of the project. + +// Your program's entry point is a function called "main". The return type can +// be anything, however most operating systems expect a return type of `int` for +// error code processing. int main(void) { // your program } @@ -84,22 +101,31 @@ int main (int argc, char** argv) // %d is an integer, \n is a newline printf("%d\n", 0); // => Prints 0 + // take input using scanf + // '&' is used to define the location + // where we want to store the input value + int input; + scanf("%d", &input); + /////////////////////////////////////// // Types /////////////////////////////////////// - // All variables MUST be declared at the top of the current block scope - // we declare them dynamically along the code for the sake of the tutorial - // (however, C99-compliant compilers allow declarations near the point where - // the value is used) + // Compilers that are not C99-compliant require that variables MUST be + // declared at the top of the current block scope. + // Compilers that ARE C99-compliant allow declarations near the point where + // the value is used. + // For the sake of the tutorial, variables are declared dynamically under + // C99-compliant standards. - // ints are usually 4 bytes + // ints are usually 4 bytes (use the `sizeof` operator to check) int x_int = 0; - // shorts are usually 2 bytes + // shorts are usually 2 bytes (use the `sizeof` operator to check) short x_short = 0; - // chars are guaranteed to be 1 byte + // chars are defined as the smallest addressable unit for a processor. + // This is usually 1 byte, but for some systems it can be more (ex. for TMS320 from TI it's 2 bytes). char x_char = 0; char y_char = 'y'; // Char literals are quoted with '' @@ -142,19 +168,25 @@ int main (int argc, char** argv) 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}; + // You can initialize an array of twenty ints that all equal 0 thusly: + int my_array[20] = {0}; // where the "{0}" part is called an "array initializer". - // NOTE that you get away without explicitly declaring the size of the array, - // IF you initialize the array on the same line. So, the following declaration - // is equivalent: - char my_array[] = {0}; - // BUT, then you have to evaluate the size of the array at run-time, like this: + // All elements (if any) past the ones in the initializer are initialized to 0: + int my_array[5] = {1, 2}; + // So my_array now has five elements, all but the first two of which are 0: + // [1, 2, 0, 0, 0] + // NOTE that you get away without explicitly declaring the size + // of the array IF you initialize the array on the same line: + int my_array[] = {0}; + // NOTE that, when not declaring the size, the size of the array is the number + // of elements in the initializer. With "{0}", my_array is now of size one: [0] + // To evaluate the size of the array at run-time, divide its byte size by the + // byte size of its element type: size_t my_array_size = sizeof(my_array) / sizeof(my_array[0]); - // WARNING If you adopt this approach, you should evaluate the size *before* - // you begin passing the array to function (see later discussion), because - // arrays get "downgraded" to raw pointers when they are passed to functions - // (so the statement above will produce the wrong result inside the function). + // WARNING You should evaluate the size *before* you begin passing the array + // to functions (see later discussion) because arrays get "downgraded" to + // raw pointers when they are passed to functions (so the statement above + // will produce the wrong result inside the function). // Indexing an array is like other languages -- or, // rather, other languages are like C @@ -221,24 +253,41 @@ int main (int argc, char** argv) (float)i1 / i2; // => 0.5f i1 / (double)i2; // => 0.5 // Same with double f1 / f2; // => 0.5, plus or minus epsilon - // Floating-point numbers and calculations are not exact - // Modulo is there as well - 11 % 3; // => 2 + // Floating-point numbers are defined by IEEE 754, thus cannot store perfectly + // exact values. For instance, the following does not produce expected results + // because 0.1 might actually be 0.099999999999 inside the computer, and 0.3 + // might be stored as 0.300000000001. + (0.1 + 0.1 + 0.1) != 0.3; // => 1 (true) + // and it is NOT associative due to reasons mentioned above. + 1 + (1e123 - 1e123) != (1 + 1e123) - 1e123; // => 1 (true) + // this notation is scientific notations for numbers: 1e123 = 1*10^123 + + // It is important to note that most all systems have used IEEE 754 to + // represent floating points. Even python, used for scientific computing, + // eventually calls C which uses IEEE 754. It is mentioned this way not to + // indicate that this is a poor implementation, but instead as a warning + // that when doing floating point comparisons, a little bit of error (epsilon) + // needs to be considered. + + // Modulo is there as well, but be careful if arguments are negative + 11 % 3; // => 2 as 11 = 2 + 3*x (x=3) + (-11) % 3; // => -2, as one would expect + 11 % (-3); // => 2 and not -2, and it's quite counter intuitive // Comparison operators are probably familiar, but // there is no Boolean type in C. We use ints instead. - // (Or _Bool or bool in C99.) + // (C99 introduced the _Bool type provided in stdbool.h) // 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 + 3 > 2; // => 1 + 3 < 2; // => 0 2 <= 2; // => 1 2 >= 2; // => 1 - // C is not Python - comparisons don't chain. + // C is not Python - comparisons do NOT chain. // Warning: The line below will compile, but it means `(0 < a) < 2`. // This expression is always true, because (0 < a) could be either 1 or 0. // In this case it's 1, because (0 < 1). @@ -346,25 +395,30 @@ int main (int argc, char** argv) break; } /* - using "goto" in C + Using "goto" in C */ typedef enum { false, true } bool; // for C don't have bool as data type before C99 :( bool disaster = false; int i, j; - for(i=0;i<100;++i) - for(j=0;j<100;++j) + for(i=0; i<100; ++i) + for(j=0; j<100; ++j) { if((i + j) >= 150) disaster = true; if(disaster) - goto error; + goto error; // exit both for loops } - error : + error: // this is a label that you can "jump" to with "goto error;" printf("Error occurred at i = %d & j = %d.\n", i, j); /* - https://ideone.com/GuPhd6 - this will print out "Error occurred at i = 51 & j = 99." + https://ideone.com/GuPhd6 + this will print out "Error occurred at i = 51 & j = 99." + */ + /* + it is generally considered bad practice to do so, except if + you really know what you are doing. See + https://en.wikipedia.org/wiki/Spaghetti_code#Meaning */ /////////////////////////////////////// @@ -375,13 +429,16 @@ int main (int argc, char** argv) // if you want (with some constraints). int x_hex = 0x01; // You can assign vars with hex literals + // binary is not in the standard, but allowed by some + // compilers (x_bin = 0b0010010110) // 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 + // If you assign a value greater than a types max val, it will rollover + // without warning. 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`, @@ -527,7 +584,8 @@ 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 argument where the function was called. -Use pointers if you need to edit the original argument values. +Use pointers if you need to edit the original argument values (arrays are always +passed in as pointers). Example: in-place string reversal */ @@ -538,9 +596,11 @@ void str_reverse(char *str_in) char tmp; size_t ii = 0; size_t len = strlen(str_in); // `strlen()` is part of the c standard library - // NOTE: length returned by `strlen` DOESN'T include the - // terminating NULL byte ('\0') - for (ii = 0; ii < len / 2; ii++) { // in C99 you can directly declare type of `ii` here + // NOTE: length returned by `strlen` DOESN'T + // include the terminating NULL byte ('\0') + // in C99 and newer versions, you can directly declare loop control variables + // in the loop's parentheses. e.g., `for (size_t ii = 0; ...` + for (ii = 0; ii < len / 2; ii++) { tmp = str_in[ii]; str_in[ii] = str_in[len - ii - 1]; // ii-th char from end str_in[len - ii - 1] = tmp; @@ -572,6 +632,24 @@ printf("first: %d\nsecond: %d\n", first, second); // values will be swapped */ +// Return multiple values. +// C does not allow for returning multiple values with the return statement. If +// you would like to return multiple values, then the caller must pass in the +// variables where they would like the returned values to go. These variables must +// be passed in as pointers such that the function can modify them. +int return_multiple( int *array_of_3, int *ret1, int *ret2, int *ret3) +{ + if(array_of_3 == NULL) + return 0; //return error code (false) + + //de-reference the pointer so we modify its value + *ret1 = array_of_3[0]; + *ret2 = array_of_3[1]; + *ret3 = array_of_3[2]; + + return 1; //return error code (true) +} + /* With regards to arrays, they will always be passed to functions as pointers. Even if you statically allocate an array like `arr[10]`, @@ -637,7 +715,7 @@ struct rectangle { void function_1() { - struct rectangle my_rec; + struct rectangle my_rec = { 1, 2 }; // Fields can be initialized immediately // Access struct members with . my_rec.width = 10; @@ -661,6 +739,16 @@ int area(rect r) return r.width * r.height; } +// Typedefs can also be defined right during struct definition +typedef struct { + int width; + int height; +} rect; +// Like before, doing this means one can type +rect r; +// instead of having to type +struct rectangle r; + // if you have large structs, you can pass them "by pointer" to avoid copying // the whole struct: int areaptr(const rect *r) @@ -700,6 +788,10 @@ typedef void (*my_fnp_type)(char *); // my_fnp_type f; +///////////////////////////// +// Printing characters with printf() +///////////////////////////// + //Special characters: /* '\a'; // alert (bell) character @@ -738,11 +830,12 @@ typedef void (*my_fnp_type)(char *); // Order of Evaluation /////////////////////////////////////// +// From top to bottom, top has higher precedence //---------------------------------------------------// // Operators | Associativity // //---------------------------------------------------// // () [] -> . | left to right // -// ! ~ ++ -- + = *(type)sizeof | right to left // +// ! ~ ++ -- + = *(type) sizeof | right to left // // * / % | left to right // // + - | left to right // // << >> | left to right // @@ -780,8 +873,8 @@ as the C file. /* included into files that include this header. */ #include <string.h> -/* Like c source files macros can be defined in headers and used in files */ -/* that include this header file. */ +/* Like for c source files, macros can be defined in headers */ +/* and used in files that include this header file. */ #define EXAMPLE_NAME "Dennis Ritchie" /* Function macros can also be defined. */ @@ -808,19 +901,20 @@ enum traffic_light_state {GREEN, YELLOW, RED}; Node createLinkedList(int *vals, int len); /* Beyond the above elements, other definitions should be left to a C source */ -/* file. Excessive includes or definitions should, also not be contained in */ +/* file. Excessive includes or definitions should also not be contained in */ /* a header file but instead put into separate headers or a C file. */ #endif /* End of the if precompiler directive. */ ``` + ## 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 Dennis Ritchie, the creator of C, and Brian Kernighan. Be careful, though - it's ancient and it contains some +It is _the_ book about C, written by Dennis Ritchie, the creator of C, and Brian Kernighan. 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/). +Another good resource is [Learn C The Hard Way](http://learncodethehardway.org/c/) (not free). If you have a question, read the [compl.lang.c Frequently Asked Questions](http://c-faq.com). @@ -830,4 +924,4 @@ Readable code is better than clever code and fast code. For a good, sane coding Other than that, Google is your friend. -[1] [Why isn't sizeof for a struct equal to the sum of sizeof of each member?](http://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member) +[1] [Why isn't sizeof for a struct equal to the sum of sizeof of each member?](https://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member) |