summaryrefslogtreecommitdiffhomepage
path: root/c.html.markdown
diff options
context:
space:
mode:
authorDmitrii Kuznetsov <torgeek@gmail.com>2021-02-22 18:42:33 +0300
committerDmitrii Kuznetsov <torgeek@gmail.com>2021-02-22 18:42:33 +0300
commite09fefaa3e78c645c720c86391e3f96d257be8a9 (patch)
tree0ff8b235e3e707125e2b11d5268ad085832355cb /c.html.markdown
parentf4c740839d78f797e9cbcfa1eb0483ac0ea45501 (diff)
parentbc8bd2646f068cfb402850f7c0f9b1dbfe81e5a0 (diff)
Merge branch 'master' of https://github.com/torgeek/learnxinyminutes-docs
Diffstat (limited to 'c.html.markdown')
-rw-r--r--c.html.markdown104
1 files changed, 79 insertions, 25 deletions
diff --git a/c.html.markdown b/c.html.markdown
index 57ce72d2..a57be1dc 100644
--- a/c.html.markdown
+++ b/c.html.markdown
@@ -8,6 +8,9 @@ contributors:
- ["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.
@@ -16,6 +19,16 @@ C is the lowest-level language most programmers will ever use, but
it more than makes up for it with raw speed. Just be aware of its manual
memory management and C will take you as far as you need to go.
+> **About compiler flags**
+>
+> By default, gcc and clang are pretty quiet about compilation warnings and
+> errors, which can be very useful information. Explicitly using stricter
+> compiler flags is recommended. Here are some recommended defaults:
+>
+> `-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.
+
```c
// Single-line comments start with // - only available in C99 and later.
@@ -76,8 +89,12 @@ int main (int argc, char** argv)
// 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
+ // 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
int x_int = 0;
@@ -90,7 +107,7 @@ int main (int argc, char** argv)
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
+ // 8 bytes
long x_long = 0;
long long x_long_long = 0;
@@ -130,6 +147,17 @@ int main (int argc, char** argv)
// You can initialize an array to 0 thusly:
char 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:
+ 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).
// Indexing an array is like other languages -- or,
// rather, other languages are like C
@@ -269,7 +297,7 @@ int main (int argc, char** argv)
// While loops exist
int ii = 0;
- while (ii < 10) { //ANY value not zero is true.
+ while (ii < 10) { //ANY value less than ten is true.
printf("%d, ", ii++); // ii++ increments ii AFTER using its current value.
} // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
@@ -302,7 +330,7 @@ int main (int argc, char** argv)
// branching with multiple choices: switch()
switch (a) {
- case 0: // labels need to be integral *constant* expressions
+ case 0: // labels need to be integral *constant* expressions (such as enums)
printf("Hey, 'a' equals 0!\n");
break; // if you don't break, control flow falls over labels
case 1:
@@ -324,7 +352,7 @@ int main (int argc, char** argv)
using "goto" in C
*/
typedef enum { false, true } bool;
- // for C don't have bool as data type :(
+ // for C don't have bool as data type before C99 :(
bool disaster = false;
int i, j;
for(i=0;i<100;++i)
@@ -336,10 +364,10 @@ int main (int argc, char** argv)
goto error;
}
error :
- printf("Error occured at i = %d & j = %d.\n", i, j);
+ printf("Error occurred at i = %d & j = %d.\n", i, j);
/*
https://ideone.com/GuPhd6
- this will print out "Error occured at i = 52 & j = 99."
+ this will print out "Error occurred at i = 51 & j = 99."
*/
///////////////////////////////////////
@@ -363,8 +391,8 @@ int main (int argc, char** argv)
// 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("%f\n", (double) 100); // %f always formats a double...
+ printf("%f\n", (float) 100); // ...even with a float.
printf("%d\n", (char)100.0);
///////////////////////////////////////
@@ -422,7 +450,7 @@ int main (int argc, char** argv)
// or when it's the argument of the `sizeof` or `alignof` operator:
int arraythethird[10];
int *ptr = arraythethird; // equivalent with int *ptr = &arr[0];
- printf("%zu, %zu\n", sizeof arraythethird, sizeof ptr);
+ printf("%zu, %zu\n", sizeof(arraythethird), sizeof(ptr));
// probably prints "40, 4" or "40, 8"
// Pointers are incremented and decremented based on their type
@@ -439,16 +467,24 @@ int main (int argc, char** argv)
*(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)
+ 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.
- int size = 10;
- int *my_arr = malloc(sizeof(int) * size);
+ size_t size = 10;
+ int *my_arr = calloc(size, sizeof(int));
// Add an element to the array
size++;
my_arr = realloc(my_arr, sizeof(int) * size);
+ if (my_arr == NULL) {
+ //Remember to check for realloc failure!
+ return
+ }
my_arr[10] = 5;
// Dereferencing memory that you haven't allocated gives
@@ -503,9 +539,11 @@ Example: in-place string reversal
void str_reverse(char *str_in)
{
char tmp;
- int ii = 0;
+ size_t ii = 0;
size_t len = strlen(str_in); // `strlen()` is part of the c standard library
- for (ii = 0; ii < len / 2; ii++) {
+ // 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
tmp = str_in[ii];
str_in[ii] = str_in[len - ii - 1]; // ii-th char from end
str_in[len - ii - 1] = tmp;
@@ -546,7 +584,7 @@ array in C.
*/
// Size must be passed!
// Otherwise, this function has no way of knowing how big the array is.
-void printIntArray(int *arr, int size) {
+void printIntArray(int *arr, size_t size) {
int i;
for (i = 0; i < size; i++) {
printf("arr[%d] is: %d\n", i, arr[i]);
@@ -559,7 +597,7 @@ printIntArray(my_arr, size);
// will print "arr[0] is: 1" etc
*/
-// if referring to external variables outside function, must use extern keyword.
+// if referring to external variables outside function, you should use the extern keyword.
int i = 0;
void testFunc() {
extern int i; //i here is now using external variable i
@@ -570,6 +608,14 @@ static int j = 0; //other files using testFunc2() cannot access variable j
void testFunc2() {
extern int j;
}
+// The static keyword makes a variable inaccessible to code outside the
+// compilation unit. (On almost all systems, a "compilation unit" is a .c
+// file.) static can apply both to global (to the compilation unit) variables,
+// functions, and function-local variables. When using static with
+// function-local variables, the variable is effectively global and retains its
+// value across function calls, but is only accessible within the function it
+// is declared in. Additionally, static variables are initialized to 0 if not
+// declared with some other starting value.
//**You may also declare functions as static to make them private**
///////////////////////////////////////
@@ -656,6 +702,7 @@ typedef void (*my_fnp_type)(char *);
// ...
// my_fnp_type f;
+
//Special characters:
/*
'\a'; // alert (bell) character
@@ -683,7 +730,8 @@ typedef void (*my_fnp_type)(char *);
"%3.2f"; // minimum 3 digits left and 2 digits right decimal float
"%7.4s"; // (can do with strings too)
"%c"; // char
-"%p"; // pointer
+"%p"; // pointer. NOTE: need to (void *)-cast the pointer, before passing
+ // it as an argument to `printf`.
"%x"; // hexadecimal
"%o"; // octal
"%%"; // prints %
@@ -738,14 +786,20 @@ as the C file.
/* Like 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. */
-#define ADD(a, b) (a + b)
+
+/* Function macros can also be defined. */
+#define ADD(a, b) ((a) + (b))
+
+/* Notice the parenthesis surrounding the arguments -- this is important to */
+/* ensure that a and b don't get expanded in an unexpected way (e.g. consider */
+/* MUL(x, y) (x * y); MUL(1 + 2, 3) would expand to (1 + 2 * 3), yielding an */
+/* incorrect result) */
/* Structs and typedefs can be used for consistency between files. */
-typedef struct node
+typedef struct Node
{
int val;
- struct node *next;
+ struct Node *next;
} Node;
/* So can enumerations. */
@@ -769,14 +823,14 @@ Best to find yourself a copy of [K&R, aka "The C Programming Language"](https://
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/).
If you have a question, read the [compl.lang.c Frequently Asked Questions](http://c-faq.com).
It's very important to use proper spacing, indentation and to be consistent with your coding style in general.
Readable code is better than clever code and fast code. For a good, sane coding style to adopt, see the
-[Linux kernel coding style](https://www.kernel.org/doc/Documentation/CodingStyle).
+[Linux kernel coding style](https://www.kernel.org/doc/Documentation/process/coding-style.rst).
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
+[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)