mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2024-12-23 17:41:41 +00:00
Merge 0c5eb762c4
into 90d544271e
This commit is contained in:
commit
a0bd453d17
165
c.html.markdown
165
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 <limits.h>
|
||||
// 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 <limits.h>
|
||||
|
||||
// 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 */
|
||||
|
Loading…
Reference in New Issue
Block a user