Update c.html.markdown

This commit is contained in:
Árpád Goretity  2013-08-15 12:30:22 +02:00
parent bc614fda28
commit 24fd870589

View File

@ -5,19 +5,20 @@ language: c
filename: learnc.c filename: learnc.c
contributors: contributors:
- ["Adam Bard", "http://adambard.com/"] - ["Adam Bard", "http://adambard.com/"]
- ["Árpád Goretity", "http://twitter.com/h2co3_ios"]
--- ---
Ah, C. Still the language of modern high-performance computing. Ah, C. Still **the** language of modern high-performance computing.
C is the lowest-level language most programmers will ever use, but 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 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. memory management and C will take you as far as you need to go.
```c ```c
// Single-line comments start with // // Single-line comments start with // - only available in C99 and later.
/* /*
Multi-line comments look like this. Multi-line comments look like this. They work in C89 as well.
*/ */
// Import headers with #include // Import headers with #include
@ -25,6 +26,11 @@ Multi-line comments look like this.
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
// file names between <angle brackets> are headers from the C standard library.
// They are searched for by the preprocessor in the system include paths
// (usually /usr/lib on Unices, can be controlled with the -I<dir> option if you are using GCC or clang.)
// For your
// Declare function signatures in advance in a .h file, or at the top of // Declare function signatures in advance in a .h file, or at the top of
// your .c file. // your .c file.
void function_1(); void function_1();
@ -33,264 +39,317 @@ void function_2();
// Your program's entry point is a function called // Your program's entry point is a function called
// main with an integer return type. // main with an integer return type.
int main() { int main() {
// print output using printf, for "print formatted"
// %d is an integer, \n is a newline
printf("%d\n", 0); // => Prints 0
// All statements must end with a semicolon
// print output using printf, for "print formatted" ///////////////////////////////////////
// %d is an integer, \n is a newline // Types
printf("%d\n", 0); // => Prints 0 ///////////////////////////////////////
// All statements must end with a semicolon
/////////////////////////////////////// // You have to declare variables before using them. A variable declaration
// Types // requires you to specify its type; a variable's type determines its size
/////////////////////////////////////// // in bytes.
// You have to declare variables before using them. A variable declaration // ints are usually 4 bytes
// requires you to specify its type; a variable's type determines its size int x_int = 0;
// in bytes.
// ints are usually 4 bytes // shorts are usually 2 bytes
int x_int = 0; short x_short = 0;
// shorts are usually 2 bytes // chars are guaranteed to be 1 byte
short x_short = 0; char x_char = 0;
char y_char = 'y'; // Char literals are quoted with ''
// chars are guaranteed to be 1 byte // longs are often 4 to 8 bytes; long longs are guaranteed to be at least
char x_char = 0; // 64 bits
char y_char = 'y'; // Char literals are quoted with '' long x_long = 0;
long long x_long_long = 0;
// longs are often 4 to 8 bytes; long longs are guaranteed to be at least // floats are usually 32-bit floating point numbers
// 64 bits float x_float = 0.0;
long x_long = 0;
long long x_long_long = 0;
// floats are usually 32-bit floating point numbers // doubles are usually 64-bit floating-point numbers
float x_float = 0.0; double x_double = 0.0;
// doubles are usually 64-bit floating-point numbers // Integral types may be unsigned. This means they can't be negative, but
double x_double = 0.0; // the maximum value of an unsigned variable is greater than the maximum
// signed value of the same size.
unsigned char ux_char;
unsigned short ux_short;
unsigned int ux_int;
unsigned long long ux_long_long;
// Integral types may be unsigned. This means they can't be negative, but // Other than char, which is always 1 byte (but not necessarily 8 bits!),
// the maximum value of an unsigned variable is greater than the maximum // these types vary in size depending on your machine and compiler.
// signed value of the same size. // sizeof(T) gives you the size of a variable with type T in
unsigned char ux_char; // bytes so you can express the size of these types in a portable way.
unsigned short ux_short; // sizeof(obj) yields the size of an actual expression (variable, literal, etc.).
unsigned int ux_int; // For example,
unsigned long long ux_long_long; printf("%zu\n", sizeof(int)); // => 4 (on most machines with 4-byte words)
// Other than char, which is always 1 byte, these types vary in size depending
// on your machine. sizeof(T) gives you the size of a variable with type T in
// bytes so you can express the size of these types in a portable way.
// For example,
printf("%lu\n", sizeof(int)); // => 4 (on machines with 4-byte words)
// Arrays must be initialized with a concrete size. // It's worth noting that if the argument of the `sizeof` operator is not a type but an expression,
char my_char_array[20]; // This array occupies 1 * 20 = 20 bytes // then its argument is not evaluated except VLAs (see below). Also, `sizeof()` is an operator, not a function,
int my_int_array[20]; // This array occupies 4 * 20 = 80 bytes // furthermore, the value it yields is a compile-time constant (except when used on VLAs, again.)
int a = 1;
size_t size = sizeof(a++); // a++ is not evaluated
printf("sizeof(a++) = %zu where a = %d\n", size, a);
// the above code prints "sizeof(a++) = 4 where a = 1" (on a usual 32-bit architecture)
// Arrays must be initialized with a concrete size.
char my_char_array[20]; // This array occupies 1 * 20 = 20 bytes
int my_int_array[20]; // This array occupies 4 * 20 = 80 bytes
// (assuming 4-byte words) // (assuming 4-byte words)
// You can initialize an array to 0 thusly: // You can initialize an array to 0 thusly:
char my_array[20] = {0}; char my_array[20] = {0};
// Indexing an array is like other languages -- or, // Indexing an array is like other languages -- or,
// rather, other languages are like C // rather, other languages are like C
my_array[0]; // => 0 my_array[0]; // => 0
// Arrays are mutable; it's just memory! // Arrays are mutable; it's just memory!
my_array[1] = 2; my_array[1] = 2;
printf("%d\n", my_array[1]); // => 2 printf("%d\n", my_array[1]); // => 2
// Strings are just arrays of chars terminated by a NUL (0x00) byte, // In C99 (and as an optional feature in C11), variable-length arrays (VLAs) can be declared as well.
// represented in strings as the special character '\0'. // The size of such an array need not be a compile time constant:
// (We don't have to include the NUL byte in string literals; the compiler printf("Enter the array size: "); // ask the user for an array size
// inserts it at the end of the array for us.) char buf[0x100];
char a_string[20] = "This is a string"; fgets(buf, sizeof buf, stdin);
printf("%s\n", a_string); // %s formats a string size_t size = strtoul(buf, NULL, 10); // strtoul parses a string to an unsigned integer
int var_length_array[size]; // declare the VLA
printf("sizeof array = %zu\n", sizeof var_length_array);
/* // A possible outcome of this program may be:
You may have noticed that a_string is only 16 chars long. Enter the array size: 10
Char #17 is the NUL byte. sizeof array = 40
Chars #18, 19 and 20 have undefined values.
*/
printf("%d\n", a_string[16]); // => 0 // Strings are just arrays of chars terminated by a NUL (0x00) byte,
// represented in strings as the special character '\0'.
// (We don't have to include the NUL byte in string literals; the compiler
// inserts it at the end of the array for us.)
char a_string[20] = "This is a string";
printf("%s\n", a_string); // %s formats a string
/////////////////////////////////////// /*
// Operators You may have noticed that a_string is only 16 chars long.
/////////////////////////////////////// Char #17 is the NUL byte.
Chars #18, 19 and 20 are 0 as well - if an initializer list (in this case, the string literal)
has less elements than the array it is initializing, then excess array elements are implicitly
initialized to zero. This is why int ar[10] = { 0 } works as expected intuitively.
*/
int i1 = 1, i2 = 2; // Shorthand for multiple declaration printf("%d\n", a_string[16]); // => 0
float f1 = 1.0, f2 = 2.0;
// Arithmetic is straightforward // So string literals are strings enclosed within double quotes, but if we have characters
i1 + i2; // => 3 // between single quotes, that's a character literal.
i2 - i1; // => 1 // It's of type `int`, and *not* `char` (for hystorical reasons).
i2 * i1; // => 2 int cha = 'a'; // fine
i1 / i2; // => 0 (0.5, but truncated towards 0) char chb = 'a'; // fine too (implicit conversion from int to char - truncation)
f1 / f2; // => 0.5, plus or minus epsilon ///////////////////////////////////////
// Operators
///////////////////////////////////////
// Modulo is there as well int i1 = 1, i2 = 2; // Shorthand for multiple declaration
11 % 3; // => 2 float f1 = 1.0, f2 = 2.0;
// Comparison operators are probably familiar, but // Arithmetic is straightforward
// there is no boolean type in c. We use ints instead. i1 + i2; // => 3
// 0 is false, anything else is true. (The comparison i2 - i1; // => 1
// operators always return 0 or 1.) i2 * i1; // => 2
3 == 2; // => 0 (false) i1 / i2; // => 0 (0.5, but truncated towards 0)
3 != 2; // => 1 (true)
3 > 2; // => 1
3 < 2; // => 0
2 <= 2; // => 1
2 >= 2; // => 1
// Logic works on ints f1 / f2; // => 0.5, plus or minus epsilon - floating-point numbers and calculations are not exact
!3; // => 0 (Logical not)
!0; // => 1
1 && 1; // => 1 (Logical and)
0 && 1; // => 0
0 || 1; // => 1 (Logical or)
0 || 0; // => 0
// Bitwise operators! // Modulo is there as well
~0x0F; // => 0xF0 (bitwise negation) 11 % 3; // => 2
0x0F & 0xF0; // => 0x00 (bitwise AND)
0x0F | 0xF0; // => 0xFF (bitwise OR)
0x04 ^ 0x0F; // => 0x0B (bitwise XOR)
0x01 << 1; // => 0x02 (bitwise left shift (by 1))
0x02 >> 1; // => 0x01 (bitwise right shift (by 1))
/////////////////////////////////////// // Comparison operators are probably familiar, but
// Control Structures // there is no boolean type in c. We use ints instead.
/////////////////////////////////////// // 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
2 <= 2; // => 1
2 >= 2; // => 1
if (0) { // Logic works on ints
!3; // => 0 (Logical not)
!0; // => 1
1 && 1; // => 1 (Logical and)
0 && 1; // => 0
0 || 1; // => 1 (Logical or)
0 || 0; // => 0
// Bitwise operators!
~0x0F; // => 0xF0 (bitwise negation, "1's complement")
0x0F & 0xF0; // => 0x00 (bitwise AND)
0x0F | 0xF0; // => 0xFF (bitwise OR)
0x04 ^ 0x0F; // => 0x0B (bitwise XOR)
0x01 << 1; // => 0x02 (bitwise left shift (by 1))
0x02 >> 1; // => 0x01 (bitwise right shift (by 1))
///////////////////////////////////////
// Control Structures
///////////////////////////////////////
if (0) {
printf("I am never run\n"); printf("I am never run\n");
} else if (0) { } else if (0) {
printf("I am also never run\n"); printf("I am also never run\n");
} else { } else {
printf("I print\n"); printf("I print\n");
} }
// While loops exist // While loops exist
int ii = 0; int ii = 0;
while (ii < 10) { while (ii < 10) {
printf("%d, ", ii++); // ii++ increments ii in-place, after using its value. printf("%d, ", ii++); // ii++ increments ii in-place, after yielding its value ("postincrement").
} // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " } // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
printf("\n"); printf("\n");
int kk = 0; int kk = 0;
do { do {
printf("%d, ", kk); printf("%d, ", kk);
} while (++kk < 10); // ++kk increments kk in-place, before using its value } while (++kk < 10); // ++kk increments kk in-place, and yields the already incremented value ("preincrement")
// => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
printf("\n"); printf("\n");
// For loops too // For loops too
int jj; int jj;
for (jj=0; jj < 10; jj++) { for (jj=0; jj < 10; jj++) {
printf("%d, ", jj); printf("%d, ", jj);
} // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " } // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
printf("\n"); printf("\n");
/////////////////////////////////////// ///////////////////////////////////////
// Typecasting // Typecasting
/////////////////////////////////////// ///////////////////////////////////////
// Every value in C has a type, but you can cast one value into another type // Every value in C has a type, but you can cast one value into another type
// if you want. // if you want (with some constraints).
int x_hex = 0x01; // You can assign vars with hex literals int x_hex = 0x01; // You can assign vars with hex literals
// Casting between types will attempt to preserve their numeric values // Casting between types will attempt to preserve their numeric values
printf("%d\n", x_hex); // => Prints 1 printf("%d\n", x_hex); // => Prints 1
printf("%d\n", (short) x_hex); // => Prints 1 printf("%d\n", (short) x_hex); // => Prints 1
printf("%d\n", (char) x_hex); // => Prints 1 printf("%d\n", (char) x_hex); // => Prints 1
// Types will overflow without warning // Types will overflow without warning
printf("%d\n", (char) 257); // => 1 (Max char = 255) printf("%d\n", (unsigned char) 257); // => 1 (Max char = 255 if char is 8 bits long)
// printf("%d\n", (unsigned char) 257); would be undefined behavior - `char' is usually signed
// on most modern systems, and signed integer overflow invokes UB.
// Also, for determining the maximal value of a `char`, a `signed char` and an `unisigned 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. // Integral types can be cast to floating-point types, and vice-versa.
printf("%f\n", (float)100); // %f formats a float printf("%f\n", (float)100); // %f formats a float
printf("%lf\n", (double)100); // %lf formats a double printf("%lf\n", (double)100); // %lf formats a double
printf("%d\n", (char)100.0); printf("%d\n", (char)100.0);
/////////////////////////////////////// ///////////////////////////////////////
// Pointers // Pointers
/////////////////////////////////////// ///////////////////////////////////////
// A pointer is a variable declared to store a memory address. Its declaration will // 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 // also tell you the type of data it points to. You can retrieve the memory address
// of your variables, then mess with them. // of your variables, then mess with them.
int x = 0; int x = 0;
printf("%p\n", &x); // Use & to retrieve the address of a variable printf("%p\n", (void *)&x); // Use & to retrieve the address of a variable
// (%p formats a pointer) // (%p formats an object pointer of type void *)
// => Prints some address in memory; // => Prints some address in memory;
// Pointers start with * in their declaration // Pointers start with * in their declaration
int *px, not_a_pointer; // px is a pointer to an int int *px, not_a_pointer; // px is a pointer to an int
px = &x; // Stores the address of x in px px = &x; // Stores the address of x in px
printf("%p\n", px); // => Prints some address in memory printf("%p\n", (void *)px); // => Prints some address in memory
printf("%d, %d\n", (int)sizeof(px), (int)sizeof(not_a_pointer)); printf("%zu, %zu\n", sizeof(px), sizeof(not_a_pointer));
// => Prints "8, 4" on 64-bit system // => Prints "8, 4" on a typical 64-bit system
// To retreive the value at the address a pointer is pointing to, // To retreive the value at the address a pointer is pointing to,
// put * in front to de-reference it. // put * in front to de-reference it.
printf("%d\n", *px); // => Prints 0, the value of x, which is what px is pointing to the address of // Note: yes, it may be confusing that '*' is used for _both_ declaring a pointer and dereferencing it.
printf("%d\n", *px); // => Prints 0, the value of x, which is what px is pointing to the address of
// You can also change the value the pointer is pointing to. // You can also change the value the pointer is pointing to.
// We'll have to wrap the de-reference in parenthesis because // We'll have to wrap the de-reference in parenthesis because
// ++ has a higher precedence than *. // ++ has a higher precedence than *.
(*px)++; // Increment the value px is pointing to by 1 (*px)++; // Increment the value px is pointing to by 1
printf("%d\n", *px); // => Prints 1 printf("%d\n", *px); // => Prints 1
printf("%d\n", x); // => Prints 1 printf("%d\n", x); // => Prints 1
int x_array[20]; // Arrays are a good way to allocate a contiguous block of memory int x_array[20]; // Arrays are a good way to allocate a contiguous block of memory
int xx; int xx;
for (xx=0; xx<20; xx++) { for (xx = 0; xx < 20; xx++) {
x_array[xx] = 20 - xx; x_array[xx] = 20 - xx;
} // Initialize x_array to 20, 19, 18,... 2, 1 } // Initialize x_array to 20, 19, 18,... 2, 1
// Declare a pointer of type int and initialize it to point to x_array // Declare a pointer of type int and initialize it to point to x_array
int* x_ptr = x_array; int* x_ptr = x_array;
// x_ptr now points to the first element in the array (the integer 20). // x_ptr now points to the first element in the array (the integer 20).
// This works because arrays are actually just pointers to their first element. // 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-od) 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).
// or when the array is a string literal used for initializing a char array:
char arr[] = "foobarbazquirk";
// or when it's the argument of the `sizeof` or `alignof` operator:
int arr[10];
int *ptr = arr; // equivalent with int *ptr = &arr[0];
printf("%zu %zu\n", sizeof arr, sizeof ptr); // probably prints "40, 4" or "40, 8"
// Arrays are pointers to their first element
printf("%d\n", *(x_ptr)); // => Prints 20
printf("%d\n", x_array[0]); // => Prints 20
// Pointers are incremented and decremented based on their type // Pointers are incremented and decremented based on their type
printf("%d\n", *(x_ptr + 1)); // => Prints 19 // (this is called pointer arithmetic)
printf("%d\n", x_array[1]); // => Prints 19 printf("%d\n", *(x_ptr + 1)); // => Prints 19
printf("%d\n", x_array[1]); // => Prints 19
// You can also dynamically allocate contiguous blocks of memory with the // You can also dynamically allocate contiguous blocks of memory with the
// standard library function malloc, which takes one integer argument // standard library function malloc, which takes one argument of type size_t
// representing the number of bytes to allocate from the heap. // representing the number of bytes to allocate (usually from the heap, although this
int* my_ptr = (int*) malloc(sizeof(int) * 20); // may not be true on e. g. embedded systems - the C standard says nothing about it).
for (xx=0; xx<20; xx++) { int *my_ptr = malloc(sizeof(*my_ptr) * 20);
*(my_ptr + xx) = 20 - xx; // my_ptr[xx] = 20-xx would also work here for (xx = 0; xx < 20; xx++) {
} // Initialize memory to 20, 19, 18, 17... 2, 1 (as ints) *(my_ptr + xx) = 20 - xx; // my_ptr[xx] = 20-xx would also work here, and it's also more readable
} // Initialize memory to 20, 19, 18, 17... 2, 1 (as ints)
// Dereferencing memory that you haven't allocated gives // Dereferencing memory that you haven't allocated gives
// unpredictable results // "unpredictable results" - the program is said to invoke "undefined behavior"
printf("%d\n", *(my_ptr + 21)); // => Prints who-knows-what? 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, // 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 // or else no one else can use it until your program terminates
free(my_ptr); // (this is called a "memory leak"):
free(my_ptr);
// Strings can be char arrays, but are usually represented as char // Strings are arrays of char, but they are usually represented as a
// pointers: // pointer-to-char (which is a pointer to the first element of the array).
char* my_str = "This is my very own string"; // 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'
printf("%c\n", *my_str); // => 'T' // This is not the case if the string is an array (potentially initialized with a string literal)
// that resides in writable memory, as in:
char foo[] = "foo";
foo[0] = 'a'; // this is legal, foo now contains "aoo"
function_1(); function_1();
} // end main function } // end main function
/////////////////////////////////////// ///////////////////////////////////////
@ -300,7 +359,8 @@ function_1();
// Function declaration syntax: // Function declaration syntax:
// <return type> <function name>(<args>) // <return type> <function name>(<args>)
int add_two_ints(int x1, int x2){ int add_two_ints(int x1, int x2)
{
return x1 + x2; // Use return to return a value return x1 + x2; // Use return to return a value
} }
@ -312,10 +372,12 @@ Example: in-place string reversal
*/ */
// A void function returns no value // A void function returns no value
void str_reverse(char* str_in){ void str_reverse(char *str_in)
{
char tmp; char tmp;
int ii=0, len = strlen(str_in); // Strlen is part of the c standard library int ii = 0;
for(ii=0; ii<len/2; ii++){ size_t len = strlen(str_in); // `strlen()` is part of the c standard library
for (ii = 0; ii < len / 2; ii++) {
tmp = str_in[ii]; tmp = str_in[ii];
str_in[ii] = str_in[len - ii - 1]; // ii-th char from end str_in[ii] = str_in[len - ii - 1]; // ii-th char from end
str_in[len - ii - 1] = tmp; str_in[len - ii - 1] = tmp;
@ -336,15 +398,20 @@ printf("%s\n", c); // => ".tset a si sihT"
typedef int my_type; typedef int my_type;
my_type my_type_var = 0; my_type my_type_var = 0;
// Structs are just collections of data // Structs are just collections of data, the members are allocated sequentially, in the order they are written:
struct rectangle { struct rectangle {
int width; int width;
int height; int height;
}; };
// it's generally not true that sizeof(struct rectangle) == sizeof(int) + sizeof(int) due to
// potential padding between the structure members (this is for alignment reasons. Probably won't
// happen if all members are of the same type, but watch out!
// See http://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member
// for further information.
void function_1(){ void function_1()
{
struct rectangle my_rec; struct rectangle my_rec;
// Access struct members with . // Access struct members with .
@ -352,22 +419,29 @@ void function_1(){
my_rec.height = 20; my_rec.height = 20;
// You can declare pointers to structs // You can declare pointers to structs
struct rectangle* my_rec_ptr = &my_rec; struct rectangle *my_rec_ptr = &my_rec;
// Use dereferencing to set struct pointer members... // Use dereferencing to set struct pointer members...
(*my_rec_ptr).width = 30; (*my_rec_ptr).width = 30;
// ... or use the -> shorthand // ... or even better: prefer the -> shorthand for the sake of readability
my_rec_ptr->height = 10; // Same as (*my_rec_ptr).height = 10; my_rec_ptr->height = 10; // Same as (*my_rec_ptr).height = 10;
} }
// You can apply a typedef to a struct for convenience // You can apply a typedef to a struct for convenience
typedef struct rectangle rect; typedef struct rectangle rect;
int area(rect r){ int area(rect r)
{
return r.width * r.height; return r.width * r.height;
} }
// if you have large structs, you can pass them "by pointer" to avoid copying the whole struct:
int area(const rect *r)
{
return r->width * r->height;
}
/////////////////////////////////////// ///////////////////////////////////////
// Function pointers // Function pointers
/////////////////////////////////////// ///////////////////////////////////////
@ -379,10 +453,11 @@ However, definition syntax may be initially confusing.
Example: use str_reverse from a pointer Example: use str_reverse from a pointer
*/ */
void str_reverse_through_pointer(char * str_in) { void str_reverse_through_pointer(char *str_in) {
// Define a function pointer variable, named f. // Define a function pointer variable, named f.
void (*f)(char *); // Signature should exactly match the target function. void (*f)(char *); // Signature should exactly match the target function.
f = &str_reverse; // Assign the address for the actual function (determined at runtime) f = &str_reverse; // Assign the address for the actual function (determined at runtime)
// 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); // 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.
} }
@ -403,7 +478,15 @@ typedef void (*my_fnp_type)(char *);
## Further Reading ## 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) 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 the creators of C. 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://c.learncodethehardway.org/book/).
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 stlye](https://www.kernel.org/doc/Documentation/CodingStyle).
Other than that, Google is your friend. Other than that, Google is your friend.