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 */