diff --git a/c.html.markdown b/c.html.markdown index 4e2c2cdf..a9b65504 100644 --- a/c.html.markdown +++ b/c.html.markdown @@ -11,6 +11,7 @@ contributors: - ["Joshua Li", "https://github.com/JoshuaRLi"] - ["Dragos B. Chirila", "https://github.com/dchirila"] - ["Heitor P. de Bittencourt", "https://github.com/heitorPB/"] + - ["Chris Harding", "https://github.com/sjrct"] --- Ah, C. Still **the** language of modern high-performance computing. @@ -27,7 +28,8 @@ memory management and C will take you as far as you need to go. > > `-Wall -Wextra -Werror -O2 -std=c99 -pedantic` > -> For information on what these flags do as well as other flags, consult the man page for your C compiler (e.g. `man 1 gcc`) or just search online. +> For information on what these flags do as well as other flags, consult the man +> page for your C compiler (e.g. `man 1 gcc`) or just search online. ```c // Single-line comments start with // - only available in C99 and later. @@ -63,26 +65,28 @@ enum days {SUN = 1, MON, TUE, WED = 99, THU, FRI, SAT}; // 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_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); +void function_1(void); +int function_2(int a, float b); -// 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. +// 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 +// 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 // 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. +// 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 @@ -125,7 +129,8 @@ int main (int argc, char** argv) short x_short = 0; // 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). + // 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 '' @@ -153,8 +158,8 @@ int main (int argc, char** argv) // sizeof(obj) yields the size of the expression (variable, literal, etc.). printf("%zu\n", sizeof(int)); // => 4 (on most machines with 4-byte words) - // If the argument of the `sizeof` operator is an expression, then its argument - // is not evaluated (except VLAs (see below)). + // If the argument of the `sizeof` operator is 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 is an unsigned integer type of at least 2 bytes used to represent @@ -171,15 +176,15 @@ int main (int argc, char** argv) // 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". - // All elements (if any) past the ones in the initializer are initialized to 0: + // 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: + // 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] + // NOTE that, when not declaring the size, the array's size 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]); @@ -249,7 +254,7 @@ int main (int argc, char** argv) i2 * i1; // => 2 i1 / i2; // => 0 (0.5, but truncated towards 0) - // You need to cast at least one integer to float to get a floating-point result + // You need to cast at least one int to float to get a floating-point result (float)i1 / i2; // => 0.5f i1 / (double)i2; // => 0.5 // Same with double f1 / f2; // => 0.5, plus or minus epsilon @@ -366,13 +371,17 @@ int main (int argc, char** argv) printf("\n"); // *****NOTES*****: - // Loops and Functions MUST have a body. If no body is needed: + // If the body of control expression is only one statement then parentheses + // are optional, but recommended: + for (jj = 0; jj < 5; jj++) + printf("%d\n", jj); + + // Loops and if statements MUST have a body. If no body is needed: int i; for (i = 0; i <= 5; i++) { - ; // use semicolon to act as the body (null statement) } // Or - for (i = 0; i <= 5; i++); + for (i = 0; i <= 5; i++); // semicolon acts as the body (null statement) // branching with multiple choices: switch() switch (a) { @@ -441,8 +450,9 @@ int main (int argc, char** argv) // 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`, - // respectively, use the CHAR_MAX, SCHAR_MAX and UCHAR_MAX macros from + // For determining the max value of a `char`, a `signed char` and an + // `unsigned char`, respectively, use the CHAR_MAX, SCHAR_MAX and UCHAR_MAX + // macros from // Integral types can be cast to floating-point types, and vice-versa. printf("%f\n", (double) 100); // %f always formats a double... @@ -453,9 +463,9 @@ int main (int argc, char** argv) // 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. + // 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 @@ -493,9 +503,9 @@ int main (int argc, char** argv) 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-of) operator: + // For example, when an array is passed to a function or is assigned to a + // pointer, it decays into (implicitly converted to) a pointer. + // Exception: when the array is the argument of the `&` (address-of) 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). @@ -514,22 +524,23 @@ int main (int argc, char** argv) // 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). + // 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 } // Initialize memory to 20, 19, 18, 17... 2, 1 (as ints) - // Be careful passing user-provided values to malloc! If you want - // to be safe, you can use calloc instead (which, unlike malloc, also zeros out the memory) + // Be careful passing user-provided values to malloc! If you want to be safe, + // you can use calloc instead (which, unlike malloc, also zeros the memory) int* my_other_ptr = calloc(20, sizeof(int)); - // Note that there is no standard way to get the length of a - // dynamically allocated array in C. Because of this, if your arrays are - // going to be passed around your program a lot, you need another variable - // to keep track of the number of elements (size) of an array. See the - // functions section for more info. + // Note that there is no standard way to get the length of a dynamically + // allocated array in C. Because of this, if your arrays are going to be + // passed around your program a lot, you need another variable to keep track + // of the number of elements (size) of an array. See the functions section + // for more info. size_t size = 10; int *my_arr = calloc(size, sizeof(int)); // Add an element to the array @@ -541,9 +552,9 @@ int main (int argc, char** argv) } my_arr[10] = 5; - // 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. + // 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 @@ -552,8 +563,8 @@ int main (int argc, char** argv) // 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.) + // 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' @@ -635,11 +646,11 @@ printf("first: %d\nsecond: %d\n", first, second); // 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) +// 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) + if (array_of_3 == NULL) return 0; //return error code (false) //de-reference the pointer so we modify its value @@ -672,15 +683,15 @@ printIntArray(my_arr, size); // will print "arr[0] is: 1" etc */ -// if referring to external variables outside function, you should use the extern keyword. +// if referring to external variables outside function, use the extern keyword. int i = 0; -void testFunc() { +void testFunc(void) { extern int i; //i here is now using external variable i } // make external variables private to source file with static: static int j = 0; //other files using testFunc2() cannot access variable j -void testFunc2() { +void testFunc2(void) { extern int j; } // The static keyword makes a variable inaccessible to code outside the @@ -693,6 +704,18 @@ void testFunc2() { // declared with some other starting value. //**You may also declare functions as static to make them private** +// Note that before C23, and unlike C++, functions taking no arguments without +// an explicit `void` inside the parameter list will be treated as taking an +// unknown number of arguments rather than no arguments. +void testFunc3(void) { + // Functions can be prototyped inside other functions + void foobie(); + void bletch(void); + + foobie(1, 2, 3); // This will give a warning at most, not an error + bletch(1, 2, 3); // This will produce an error +} + /////////////////////////////////////// // User-defined types and structs /////////////////////////////////////// @@ -713,10 +736,16 @@ struct rectangle { // due to potential padding between the structure members (this is for alignment // reasons). [1] -void function_1() +void function_1(void) { struct rectangle my_rec = { 1, 2 }; // Fields can be initialized immediately + // Fields can also be initialized in an arbitrary order with the field name + struct rectangle my_rec2 = { + .height = 2, + .width = 1 + }; + // Access struct members with . my_rec.width = 10; my_rec.height = 20; @@ -760,10 +789,10 @@ int areaptr(const rect *r) // Function pointers /////////////////////////////////////// /* -At run time, functions are located at known memory addresses. Function pointers are -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. +At run time, functions are located at known memory addresses. Function pointers +are 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. Example: use str_reverse from a pointer */ @@ -773,12 +802,13 @@ void str_reverse_through_pointer(char *str_in) { f = &str_reverse; // Assign the address for the actual function (determined at run time) // f = str_reverse; would work as well - functions decay into pointers, similar to arrays (*f)(str_in); // Just calling the function through the pointer - // f(str_in); // That's an alternative but equally valid syntax for calling it. + // f(str_in); // That's an alternative but equally valid syntax for calling it } /* -As long as function signatures match, you can assign any function to the same pointer. -Function pointers are usually typedef'd for simplicity and readability, as follows: +As long as function signatures match, you can assign any function to the same +pointer. Function pointers are usually typedef'd for simplicity and readability, +as follows: */ typedef void (*my_fnp_type)(char *); @@ -864,8 +894,8 @@ as the C file. */ /* A safe guard to prevent the header from being defined too many times. This */ -/* happens in the case of circle dependency, the contents of the header is */ -/* already defined. */ +/* happens in the case of circlular dependencies, or such as when a header is */ +/* included alongside a header that includes the same header itself. */ #ifndef EXAMPLE_H /* if EXAMPLE_H is not yet defined. */ #define EXAMPLE_H /* Define the macro EXAMPLE_H. */ @@ -895,9 +925,10 @@ typedef struct Node /* So can enumerations. */ enum traffic_light_state {GREEN, YELLOW, RED}; -/* Function prototypes can also be defined here for use in multiple files, */ -/* but it is bad practice to define the function in the header. Definitions */ -/* should instead be put in a C file. */ +/* Function prototypes can also be defined here for use in multiple files, */ +/* but it is bad practice to define the function in the header. Definitions */ +/* should instead be put in a C file. An uncommon exception is when defining */ +/* static inline functions. */ Node createLinkedList(int *vals, int len); /* Beyond the above elements, other definitions should be left to a C source */