Cheat Sheet
Cheat Sheet
printf, “print formatted”, is a dependently-typed function: The number and type Enforce a particular precedence order by enclosing expressions in parentheses.
of its arguments depends on its first argument, a string. == equals != differs from ! not
◦ The number of occurrences of ‘%’ in the string argument is the number of >= at least <= at most && and
additional arguments the printf takes. > greater than < less than || or
1
Assignments That is, numerically % yields remainders; but logically, in C, it expresses the is-
multiple-of relationship.
When x is a number, the shift operations correspond to multiplication and division by
/* Abbreviations */ 2n , respectively.
x ⊕= y ≈ x = x ⊕ y Left Shift x « n append the bit representation of x with n-many 0s
x++ ≈ x += 1 Right Shift x » n throw away n bits from the end of the bit representation of x
--x ≈ x -= 1
The bitwise operators and &, or |, not !, and xor ^ operate at the bit representation of
The increment and decrement, ++/--, operators may precede or follow a name: an item. For example, the ASCII code of a character consists of 7 bits where
If they follow a name, then their behaviour is executed after the smallest context Bit Function
—e.g., braces or conditional parentheses— in which they occur. 7 0 digit, 1 letter
When they precede a name, their behaviour is executed before the context in which 6 0 upper case, 1 digit or lower case
they appear. 5 0 for a-o, 1 for digit or p-z
The order of evaluation is not specified inside a function call and so behaviour Whence, to convert a character to uppercase it suffices to change bit 5 to be a 0 and
varies between compilers. leave the other bits alone. That is, to perform a bitwise and with the binary number
11011111, which corresponds to the decimal number 223.
Avoid using these in complex expressions, unless you know what you’re doing.
// Mask, or hide, bit 6 to be a ‘1’.
#define toLower(c) (c | (1 << 6))
Loops
#define toUpper(c) (c & 223)
Here’s the general form.
#define times10(x) ( (x << 1) + (x << 3)) // Parens matter!
while (condition) // x ⇒ 2·x + 8·x ⇒ 10 · x
statementBlock
How did we know is was 223?
/* Abbreviations */ 0. Ninth bit is on 100000000 1 « 8
/* for loop */ for(A; B; C;) S ≈ A; while(B) S 1. Negate it: Eight ones 11111111 ~(1 « 8)
/* do-while loop */ do S while B ≈ S; while(B) S 2. Sixth bit is on 100000 1 « 5
3. Xor them 11011111 ··· ^ ···
do/while: The conditional is evaluated after the statement has been executed and so the 4. See it as a decimal 223
statement is obeyed at least once, regardless of the truth or falsity of the condition. This
is useful for do once, and possible more operations. Ironically, C has no primitive binary printing utility.
int i = 0;
do printf("%d \n", i++); Floats & Other Types
while (i != 10); //Note the ending semicolon.
2
short age; Variables may be used without having values declared! This is akin to buying the land
float height; —computer memory— but not building the house —assigning a value— thereby leaving
}; // Note that this is a statement us with a vacant lot that contains trees, garbage, and whatever was there before you get
there —as is the case in C.
// Alias for readability.
typedef struct entry Person; char c;
int i;
int main() long x;
{ // Exponent form for numbers. float f;
struct entry qasim = {"qasim", 0, 613e-2}; // 613e-2 ⇒ 613 × 10− ² ⇒ 6.13double d;
Person q = qasim;
q.age = 23; // Uninitalised ⇒ have random values.
printf("Hello! My name is %s and I’m %d years old, being %f ft tall", printf("char %c\n",c);
q.name, q.age, q.height); printf("int %i\n",i);
return 0; printf("long %l\n",x);
} printf("float %f\n",f);
printf("double %d\n",d);
Hello! My name is qasim and I’m 23 years old being 6.130000 ft tall
#RESULTS:
char
Forming Numbers using Octal & Hexadecimal
int 32766
long
Numbers that begin with a 0 are interpreted as octal and those beginning with 0x or
float 0.0
0X are hexadecimal. These forms are obtained from binary by grouping using 3 bits and
double 73896
4 bits, respectively. E.g., 129 ≈ 0201 ≈ 0x81 since:
129 →hbinaryi 10 000 001 →hoctali 2 0 1 Rather than being set to 0, variables automatically obtain the random values in memory
129 →hbinaryi 1000 0001 →hhexadecimali 8 1 and one should always initialise variables upon declaration.
The amount of space a variable needs is its size in memory.
Indeed:
The actual location of the variable in memory is called its address —just like the
int x = 129, y = 0201, z = 0x81, zz = 0X81, b = (x == y) && (x == zz); address on your house or on an ugly vacant lot.
printf("%d ≈ %d ≈ %d; %d", x, y, z, b); In your computer, memory space is measured in bytes.
129 ≈ 129 ≈ 129; 1 ◦ Not every computer uses the same storage space for its variables.
◦ The sizeof keyword tells us how many bytes a variable, or structure, uses
up.
The Preprocessor ? Both sizeof(x) and sizeof(int) are valid calls.
The preprocessor performs alterations to a program before it is compiled; e.g., the ◦ This keyword is used primarily for manual allocation of memory for new
following ensures the replacement of every occurrence of this(· · · , · · · ) after it with variables we create.
whatever that is. char c;
#define this(arg1, arg2) that int i;
long x;
All preprocessor commands are preceded by the # symbol. float f;
One generally defines useful constants or abbreviations this way, and if they have double d;
a collection of such parameters or utilities in some file, then they can copy-paste
them into the current program file by using #include as mentioned before. // Uninitalised ⇒ have random values.
printf("char %i\n",sizeof(c));
Pointers printf("int %i\n",sizeof(i));
printf("Z %i\n",sizeof(int));
printf("long %i\n",sizeof(x));
A fly is a bug that flies; so a fly flies. printf("float %i\n",sizeof(f));
Likewise, a pointer points. printf("double %i\n",sizeof(d));
3
assert( &arr == arr );
#define Length 123 assert( &arr[0] == arr );
#define Type char 1. Here’s some useful conversion laws
Type arr[Length];
*&-inverses p = &x ≡ *p = x
assert( sizeof(arr) == sizeof(Type) * Length ); The lhs lives in the land of addresses and locations, whereas the rhs
Memory is set aside for variables, even if they’re not used. lives in the land of values.
◦ To avoid hogging up excess memory, programs that need a lot of memory []&-array arr + i = &arr[i]
usually request it a little at a time using malloc —‘m’emory ‘alloc’ation.
C is a mid-level language that has the capability to examine memory and the From these we obtain:
variables stored there. []*-array *(arr + i) = arr[i] —immediate from the above two laws.
Why care about where variables are located in memory? *-array *arr = arr[0] —immediate from the above two laws.
Consider sorting an array of complex data, rather than moving the data itself *-array **arr = arr[0][0] —the previous law iterated twice.
around, it is much cheaper to simply sort an array corresponding to their numeric
locations in memory. Tips:
◦ The variables that hold addresses are called pointers.
X Pointers must be declared with the asterisk
A variable consists of 4 pieces: Name, type, size, and location in memory. X When a pointer is used without its asterisk, it refers to a memory location
◦ The first two we know, since we write them down. X When a pointer is used with its asterisk, it indirectly refers to a value
◦ The third is obtained via the sizeof operator. X To assign a pointer a memory location, prefix the other variable with &
◦ The fourth is obtained by using the “address of” operator &, resulting in a X Pointers are memory addresses and can be displayed as unsigned integers using %u
pointer. ( One may think of ‘&’ as simply projecting the fourth component
from a variable structure. ) int a[30], *p;
A pointer is a variable that holds a memory address. // Make p point to the beginning of the array a, since a is itself a pointer to the
◦ Pointers are represented as numeric locations, but they are not number val- // beginning of the array.
ues. p = a;
T* is denotes pointers of type T.
// Determine the actual machine address of a variable by predicing it with &.
◦ Pointers are declared like any other variable. int fred;
◦ Conventionally a declaration looks like T *t; —the whitespace around the p = &fred;
‘*’ is completely irrelevant— as a reminder that the expression *t denotes a
value of type T. Huh? // If we need to know exactly where in the machine C had decidede to allocate space
? Without ‘*’, pointers consume and represent memory locations. // to fred, we need only to print out p; whence & is read “address of”.
? With ‘*’, they denote a value of type T. printf("%p", p);
Unless you’re working with pointers to pointers, you do not want to write 0x7ffee36348ac
down *p = &x, which sets the value at the location of p to refer to an address.
Since the name of an array is itself a pointer to the beginning of an array, we have
// Valid declarations arr ≈ &arr[0]
int*p;
int* q; // Find furthest points of ‘int a[N-1]’ that are identical.
int *r; #define N 12
int * s; int a[] = {9,8,1,2,3,4,4,3,2,1,7,6};
4
f() {return 9; } char surprise [] = "surprise! No more, my friend";
g() {return ’c’; } // Remember that chars are actually numbers. assert(strlen(surprise) == 28);
5
◦ It is pointers to strings, rather than strings themselves, that’re passed around Input –getting things into the machine
in a C program.
C strings like s = "this" actually, under the hood, are null-terminated arrays
of characters: The name s refers to the address of the first character, the ’t’, getchar returns the next character input from the keyboard.
with the array being ’t’ → ’h’ → ’i’ → ’s’ → 0, where 0 is not ASCII zero
—whose value is 48— but ASCII null —i.e., all bits set to 0. #include <stdio.h>
◦ Note that null 0 is denoted by ’\0’ and has value 0, i.e., false, so conditions
of the form p != ’\0’ are the same as p. int main()
{
An array name is a pointer to the beginning of the array.
printf("Please enter a character: ");
◦ Yet, an array name is a constant and you can’t do arithmetic with it. char c = getchar();
int length(char c[]) // A string is a character array printf("You entered: %c", c);
{ printf("\nBye\n");
// While c[l] is not an ASCII null, keep counting until.
int l = 0; return 0;
while( c[l] ) l++; }
return l;
Let’s extend getchar to work on buffers of length, say, 20:
}
#include <stdio.h>
int main()
{ #define BUFFER_LENGTH 10
char str[] = "hello world 0123";
printf("length(“%s”) = %d", str, length(str)); int main()
return 0; {
} printf("Please enter a string, only first %d chars or newline will be read: \n\t\t
length(“hello world 0123”) = 16 BUFFER_LENGTH);
T *p; ⇒ declare p to be a pointer to elements of type T. char cs[BUFFER_LENGTH];
int keep_reading = 1;
*p = v ⇒ “put the value of v in the location which p points to” char * p = cs;
We can now rewrite the length function with even less square brackets. while(keep_reading && p < cs + BUFFER_LENGTH)
{*p = getchar(); if (*p == ’\n’) keep_reading = 0; else p++;}
int length(char* c)
{ *p = ’\0’; // Strings are a null-terminated arrays of chars.
char* start = c;
while( *c ) c++; // Local copy of c is affected. printf("\nYou entered: %s", cs);
return c - start; printf("\nBye\n");
}
return 0;
assert(length("hello world") == 11); }