C
A char
type is always encoded using 8 bits (single byte) \(\rightarrow\)
just an integer type with a fixed range.
An int
type usually has the same size as the processor's registers.
\[8=taille(char)\leq taille(short)\leq taille(int) \leq taille(long) \leq taille(long \ long)\]
Operations on integer types ALWAYS return integer types: 3/2 \(\rightarrow\) 1.
IEEE-764 floating point encodings (most frequently used):
type | # bits | # digits of precision |
---|---|---|
float | 4 | 6 |
double | 8 | 15 |
long double | 10 | 18 |
If a literal contains a point or exponent (e
or E
), then it is a floating type.
Otherwise, it is an integer type.
To use a different number base (ie: not decimal), prefix with 0b
for binary,
0x
for hexadecimal and 0
for octal.
In C: 'A'
is of type int
, in C++: it is of type char
.
printf("\a"); // produces a sound
By default, 2.3
is of type double
.
To specify a literals type, use a suffix (or a combination for unsigned types):
type | suffix |
---|---|
char | none |
short | none |
int | none |
long | L |
long long | LL |
float | F/f |
double | none |
long double | L |
unsigned int | U/u |
Addresses and Pointers Nonsense:
char u; // a character
char *pu; // a pointer to a character
char *ppu; // a pointer to a pointer to a character
u = 2; // assign the value of the literal 2 to u
pu = &u; // assign the address of u to pu
ppu = &pu; // assign the address of pu to ppu
// pointer dereference, lines 13, 14 and 15 have the same effect
int a = 2;
int *pa = &a;
int **ppa = &pa;
a = 0;
*pa = 0;
**ppa = 0;
The value of a pointer is an address.
Pointer to Function Example:
#include <stdio.h>
int f(int n){
return n/2;
}
int main(){
int (*fp)(int) = &f;
printf("%d\n", (*fp)(8));
return 0;
}
Always initialize a pointer.
int *p1 = 0 // the only allowed int to pointer assignment!
int *p2 = NULL // same
p1 = nullptr; // preferable in std>=C++11
Enumerated int constants:
enum {f, t}; // f: 0, t: 1
enum {f=10, t, d=2}; // f: 10, t: 11, d: 2
Constants and pointers
int const *ptr;
// ptr is a pointer to constant int -> can't modify the pointed variable with this pointer
int *const ptr = &x; // constant pointer --> needs initilization
// ptr is a constant pointer to int -> can't modify the address stored in this pointer
Prepend a variable declaration with the volatile
keyword to inform the compiler
that the variable may change without being explicitly reassigned; this disables any
optimizations.
Never use volatile
with const
.
char x = 3;
x = ~x; // The bitwise complement operator (unary)
The binary bitwise AND &
and OR |
operators.
The binary bitwise XOR ^
operator.
int a = 1<<4; //The left-shift operator; left shift by 4, pad with 0's
// k<<n is equivalent to multiplying by 2^n
int a = 1>>4; //The right-shift operator; right shift by 4, pad with 0's
// k>>n is equivalent to dividing by 2^n
If a variable x
is of type T
, the expression &x
is of type T*
.
If a variable x
is of type const T
, the expression &x
is of type const T *
.
Increasing numeric size types:
char
,short
,int
,long int
,long long int
.float
,double
,long double
.
The unsigned versions have the same sizes.
void function(const int *p){
// this function won't use p to alter the pointed variable
// (which might or might not actually be const)
int j = *p;
*p = 0; // error
}
T a[s]; // an array `a` of size s and containing s elements with type `T`.
T a[s][s2]; // an array of arrays. contains s arrays each of size s2.
Array initialization:
int a[3] = {1, 2, 3};
int b[] = {1, 2, 3, 4};
double c[4] = {3}; // rest are set to zero
int d[4] = {[0]=3, [3]=3}; // rest are set to zero
char carr[] = "bruh";
String manipulation in C
#include <string.h>
char a[12] = "bruh";
int length = strlen(a); // length = 4, due to \0 terminated value in a
char b[12];
strcpy(b, "nonsense"); // copy into b
strcpy(a, b); // a now contains nonsense
strcpy(b, "no"); // b = "no"
strcat(b, " nonsense"); // b = "no nonsense"
strcmp(t1, t2); // -> ~ t1 codes - t2 codes
char a[20];
scanf("%s", a); // NOT &a
All these functions consider the null terminator as the end of the string.
Structs
struct structure_name {
member_declarations;
};
struct Color{
int red;
int green;
int blue;
};
struct Color b;
b.red = 0;
b.green = 0;
b.blue = 255;
struct Color *pb;
pb = &b;
(*pc).red = 255;
pc->red = 255; // the two lines are equivalent
struct Color c = {128, 200, 0};
struct Color c = {.red=128, .green=200, .blue=0};
struct Color d = c; // shallow copy (d.red = c.red, ...)
Unions (synonym for Useless)
union union_name{
member_declarations;
};
Can't return an array from a function.
do {
nonsense();
}while(!working());
Switch
switch (selector){ // an int expression
case val1: // an int expression available at compile time
instructions1
break;
case val2:
instructions2
break;
...
default:
default_instructions
break;
}
A switch selects the matching case in constant time.
Program arguments
int main(int argc, char *argv[]){
// ...
}
// is equivalent to
int main(int argc, char **argv){
// ...
}
argc
is the argument count, including the program name
argv
is the argument array, containing argc
strings (char*
delimited by \0
)
Send a different exit code to the os.
#include <stdlib.h> // exit declaration
void procedure(){
exit(17); // equivalent to having an immediate return 17 in main()
}
int main(){
procedure();
// ...
return 0;
}
#define PI 3.0
// ... duh
#define STRUCTURE \
struct bruh\
{\
double x;\
double y;\
}
// <=>
#define STRUCTURE struct bruh {double x; double y;}
// macro with no value, boolean flag
#define INT32
// function like macro
#define MAX(a, b) a>b?a:b
// variadic macros
#define DEBUG(fmt, ...) fprintf(stdout, fmt, __VA_ARGS__);\
fflush(stdout)
To avoid operator precedence problems, wrap each macro formal parameter in parentheses.
NEVER USE AN OPERATION THAT PRODUCES SIDE EFFECTS AS A MACRO ARGUMENT. no idea how many times it will be called.
Macro names should ALWAYS be all uppercase.
#define bruh
#undef bruh
// undefine macro named bruh
#define LOGLEVEL 1
#if LOGLEVEL == 0
// ...
#elif LOGLEVEL == 1
// ...
// ...
#elif LOGLEVEL == 4
// ...
#else
// ...
#endif
#define M1 bruh
#define M2
#ifdef M1 // evaluates to true
// ...
#endif
#ifdef M2 // evaluates to true
// ...
#endif
#ifndef M3 // also evaluates to true
// ...
#endif
The conventional way to include header files, using header guards
// file: A.h
#ifndef A_H
#define A_H
//...
#endif
#if expression
where
The expression is a constant expression, using only literals and identifiers, defined using #define directive. Any identifier, which is not literal, non defined using #define directive, evaluates to 0.
The # operator transforms a formal macro parameter into a string.
#define·DISPLAYCALC(x)·printf(#x·"·=·%d\n",·x)
// ...
int a = 4;
DISPLAYCALC(a); // prints: a = 4\n
DISPLAYCALC(a*a); // prints: a*a = 4\n
The ## operator concatenates two formal macro parameters or a formal macro parameter and a string.
int k; // both a declaration and definition
extern int j; // is a declaration but not a definition,
// j is defined in some other compilation unit.
double square(double x) {return x*x;} // is both a declaration and definition
double root2(double); // is a declaration but not a definition
To define a type alias, declare a variable of the desired type and prefix its declaration with typedef.
// intermediate step
char chess_board[8][8]; // models an 8x8 chess board, as a declaration
typedef char chess_board[8][8]; // is now a type alias, not a variable
// can now use it to declare variables
chess_board c1, c2;
For structures, take a similar approach
struct point {
int x, y;
};
struct point p; // intermediate step, variable
typedef struct point p; // final form of type alias
// can now declare variable using p instead of struct point
p point1, point2;
we can even combine the struct definition and alias
typedef struct point{
int x, y;
} p;
struct point p1; // is ok
p p1; // is also ok
More useful version with a single identifier consumed
typedef struct {
int x, y;
} p;
struct point p1; // NOOOOOO STOOOUUUPID
p p1; // is also ok
Parsing complex types
operator | sentence | priority |
---|---|---|
[] | array of ... | highest |
() | function returning ... | mid |
* | pointer to ... | lowest |
int j = 3; // global variable is accessible to all functions
static int k = 3; // static variable is only accessible to functions in the same
// compilation unit ie same source file
char c = g() + 2; // NO, has to be a constant
char k = 'i' + 2; // Ok, know at compile time
int main(){
// ...
}
Pointer p + int k = Pointer pointing k positions after p
Pointer p - int k = Pointer pointing k positions before p
Pointer p1 - Pointer p2 = number of elements in between the pointed elements
(if homogenous, otherwise bruh)
(Pointer p1 < Pointer p2) evaluates to 0 or 1 (boolean result of numeric comparison)
int x[3];
// the following expressions are equivalent
x <==> &x[0];
x[i] <==> (&x[0])[i];
*x <==> *(&x[0]) <==> x[0];
*(x+i) <==> x[i]
int t[4];
int *pt = &t[0];
// the following expressions are equivalent
t[i] <==> pt[i] <==> *(t+i) <==> *(pt+i)
"an array identifier is a constant pointer to the first element of the array"
char *word[]; // array of pointers to char NOT pointer to array of char
dynamic memory management
// prototypes
void *malloc(size_t size);
void free(void *ptr);
void *realloc(void *ptr, size_t size);
#include <stdlib.h>
double *p;
int n = 30;
p = malloc(n*sizeof(double)); // in c void* can be assigned to a T*, in C++ NO
for(int i=0; i<n; i++) p[i] = 0.0; // can be used syntactically as an array
malloc may fail to allocate a block of memory, in which case it will return a null pointer.
ALWAYS CHECK FOR NULL POINTER AFTER A CALL TO malloc
.
int *p = malloc(20*sizeof(int));
if(p==NULL){
// fix it ... or fail catastrophically
}else{
// go ahead
}
free(p); // free the allocated block
int *p = malloc(20*sizeof(int));
// use, then find out it had an inappropriate size
p = realloc(p, 30*sizeof(int));
// yay, new size
realloc
check-list:
- allocate new block with the appropriate size (if no expansion possible)
- copy data from the old block to the new (if could not just expand old block)
- free the old block (if changed)
- return pointer to the first element of the new block (if different)
realloc
safety
double *realloc_safe(double *p, size_t newsize, int *ok){
*ok = 1; // all is good for now
int *q = realloc(p, newsize);
if(q) p = q;
else *ok = 0; // not good, inform the caller
return p;
}
these checks insure that p is always a valid pointer regardless of the success of the reallocation.
Advice:
- DO NOT TRY TO INFER THE SUCCESS OF THE OPERATION BY COMPARING THE NEW AND OLD POINTER ADDRESSES. A SUCCESSFULL REALLOCATION CAN KEEP THE SAME ADDRESS.
- ALWAYS FREE ALLOCATED BLOCKS
- NEVER USE A POINTER AFTER FREEING ITS POINTED BLOCK
- NEVER FREE MORE THAN ONCE (including realloc with 0 size)
- NEVER ACCESS MEMORY BEYOND THE ALLOCATED LIMIT (even worse than array out of bound access)
// Generate all permutations of an array
#include <stdio.h>
void swap(int *a, int *b){
int t = *a;
*a = *b;
*b = t;
}
void printarray(int n, int *a){
for(int i=0; i<n; i++)
printf("%d ", a[i]);
printf("\n");
}
void auxperm(int n, int k, int *a){
if(n==k) {printarray(n, a); return;}
for(int i=k; i<n; i++){
swap(a+i, a+k);
auxperm(n, k+1, a);
swap(a+i, a+k);
}
}
void perm(int n, int *a){
auxperm(n, 0, a);
}
int main(){
int n = 5;
int a[n];
for(int i=1; i<=n; i++) a[i-1] = i;
perm(n, a);
return 0;
}
typedef const char *(*txtptr)(double);
txtptr
is the type "pointer to function taking a double and returning a
pointer to a constant char".
assert(("message", expr)); // efficient use of the comma operator
C function name nonsense
#include <stdio.h>
void printchar(char c){
printf("%c", c);
}
int main(){
printchar('M'); // prints: M
(*printchar)('M'); // compiles, prints: M
printf("\n");
return 0;
}
Functions readily decay into pointers to themselves, see more on
reddit
.
staticrap
void foo() {
static int x = 5; // assigns value of 5 only once, at compile time
x++;
printf("%d", x);
}
int main() {
foo(); // x = 6
foo(); // x = 7
return 0;
}
See more on StackOverflow
.
#include <stdio.h>
int main() {
int nv[] = {1, 2, 3};
// common idiom to get the number of elements in an array in c
int n = sizeof(nv) / sizeof(*nv);
printf("%d\n", n); // prints: 3
return 0;
}