// Primitive
int 012(octal) 0xFB(hex)
float double long double
char
bool (from <stdbool.h>)
signed unsigned //(for "char" and "int" types only)
short long long long
// Derived
arrays, enum, union, struct
// Pointers
int*
void (only pointers and function return type)
etc...
/* Literal Suffixes (Case-insensitive)
int = u, l, ll
real = f, l
*/
Systems that used 16-bits to store integer values continued using the same amount even on 32-bit systems where Memory Address Register (MAR) is larger and hence can address more locations than on a 16-bit storage, we needed a type that can be utilized depending on machine architecture, hence long
came into existence.
int
by default.static a = 7;
signed b = 8;
const c = 9;
int
and char
are always signed
by default.
No modifiers for float
are allowed.
Only modifier allowed on double
is long double
.
Explicit Cast: Higher data type to lower one. float
to int
(often leads to truncate or loss of data).
//Explicit conversion syntax
(int)a
(float) 9/5
(unsigned int) 4.3 - 2
1.141f 1.141F
Implicit Cast: Lower data type to higher one. signed
to unsigned
(performed by compiler implicitly).
//Very famous pitfall
#include <stdio.h>
int main(){
float c = 5.0;
printf ("Temperature in Fahrenheit is %.2f", (9/5)*c + 32);
return 0;
}
//Output: 37.0
//Fix: make any one of 9 or 5 as double
#include <stdio.h>
int main(){
float c = 5.0;
printf ("Temperature in Fahrenheit is %.2f", (9.0 / 5)*c + 32);
return 0;
}
//Output: 41.0
Integer Promotions: Integer data types like char
and enum
can be promoted to int
or unsigned
.
Link: https://www.geeksforgeeks.org/integer-promotions-in-c/
0
) and taking their 2s complement as negative numbers.0
(that’s good), and we also get a number 2 ^ (n)
(where n
is number of bits we are using to represent the numbers) whose both positive and negative representations are same, so we take it as negative.0
in their representaion and negative have MSB as 1
.float
is a 32-bit IEEE 754 single precision Floating Point Number: 1 bit for the sign, 8 bits for the exponent, and 23 for the value, i.e. float
has 7 decimal digits of precision
.
double
is a 64-bit IEEE 754 double precision Floating Point Number: 1 bit for the sign, 11 bits for the exponent, and 52 bits for the value, i.e. double
has 15 decimal digits of precision
.
float a = .2; //valid
float b = 2.; //valid
//By default every real constant in a program is double
float a = 0.1;
a == 0.1 //false, double is more precise than float
a == 0.1f //true (f/F)
0.1l //long double literal (l/L)
0.1 + 0.1 + 0.1 == 0.3 //false, non-terminating recurring decimal on RHS, LHS expression's result has limited precision
char
and int
are interchangeable in C as char
is internally represented as ASCII integer values from 0 to 255. The range of char
is much less than that of int
and hence care must be taken on casts.
unsigned long long
.sizeof()
operator returns thismalloc()
, memcpy()
, etc… has arguments and strlen()
returns this type.unsigned char c;
c = 255 + 255; //sets c = 254
//signed char c would set c as -2
2/3 // 0
9/5 // 1
%
) operation, sign of the numerator
always carries over to the result.-3 % 2 // -1
-3 % -2 // -1
2 % -3 // 1
-3 / -1 // 3
-2 / 1 // -2
3 / -2 // -1
int
. Either 0
or non-zero
value.&&==
or ||==
.&&
and ||
.sizeof(x && y) // 4
//Short-circuiting in logical operators
foo() && bar() // if foo() returns 0, bar() won't be called
foo() || bar() // if foo() returns a non-zero value, bar() won't be called
int
. Either 0
or non-zero
value.sizeof(x == y) // 4
-2 << 1 //undefined
1 << 33 //undefined, if int is 32-bits
n left shifts
= multiplication by 2 ^ n
and n right shifts
= division by 2 ^ n
.int a = 8;
a++; //correct
8++; //compile-time error
int a = 1, b= 2;
++(a+b); //compile-time error
++(-i); //compile-time error
(++i)++; //compile-time error
int t = -i;
++t; //correct
postfix > prefix = dereferncing
LR associative RL associative
int a = 8;
return a++; //returns 8
return ++a; //returns 9
Two uses:
//As an operator it evaluates both sides starting from left, sets sewuence point after each and returns the rightmost evaluation's result
int a = (1, 2, 5, 10); // a = 10
int b = (foo(), bar()); // a = bar()
//we can also do this:
printf("Hello"),
printf("World"),
if(a == b) { ... }
int a = 1, 2, 3; //acts as a separator as it is expected during declaration, compile-time error saying expected identifier at 2's place
int a;
a = 1, 2, 3; //acts as an separator after setting a = 1
int a = (1, 2, 3); //acts as an operator and sets value as 3
return a, b, c; //c is returned
(a, b) = 10; //compile-time error
Sequence Points: All side-effects (changes and computations on variables) have been finished before a sequence point. We have them naturally in &&
, ||
, ?:
, start of loops, etc.. or we can make them using ,
. Cases such as foo() + bar()
or printf("%d %d", foo(), bar())
don’t have any sequence points and hence we don’t have a defined order of execution among operands or parameters of a function.
//what's the return type of an assignment operation? Ans: the value it is assigning.
int a = (b = 4); //a is set as 4
if(a = 0) //false
if(b = 4) //true
if(c = 'c') //true
// it has a return type and operands must have same return type (foo and bar) else suprises
int a = (b == 1) ? foo() : bar();
a > b ? (c=10) : (d=10); //use brackets otherwise error
size_t
typeint a = 1, b = 2;
printf("%d", sizeof(a+=b)); // 4
printf("%d", a); // 1
sizeof a+b; //valid
Link: here
//no chaining in C, unlike Python
if(a < b < c){ ... } //no errors
//evaluated as: (a < b) < c
for(;;); //valid
for(foo();foo();foo()){
foo();
}
//TO-DO: sequence of execution in a 'for' loop
continue
may appear before updation.break;
after every case.default:
is optional and its position of default:
never matters.switch
but not in any case
is never executed.int
, char
, and enum
.low ... high
, low and high must only be integer literals. Errors will occur if overlapping case values or high < low.switch (var) {
case 1:
break;
case 'a':
break;
case 'b' ... 'c':
break;
case 1+2:
break;
case 'a'||1:
break;
default:
break;
}
goto label;
label:
continue;
//skips current iteration in any loop as soon as it's encountered, used only with loops
break;
//goes out of loop as soon as it is encountered, to be used only with switch and loops