The C Programming Language - Data-types, operators and expressions

short and long applies to integers only and hence can be omitted from the following declarations:short int a;long int b;

Compiler is free to choose sizes for data-types subject to following conditions:short and int are atleast 16 bits longlong is atleast 32 bit longshort is not longer than intint is not longer than longsigned/unsigned applies to char and int both.

signed/unsigned applies to char and int both.

Enum Vs # Defines

values can be generated for enums.

enums can be printed in symbolic form by debuggers

enums offer the chance of compiler checks

by default constant 2.3 will be treated as double until it is specified as - 2.3f (float)

use-case is in printf if you want to split a long string across multiple lines:

printf("hello," "world");

is equivalent to:

printf("hello,\world");

Variables must be declared before their use.

non-automatic variables are initialized only once(before program starts executing) and the initializer has to be a constant. Automatic variables are initialized everytime the function or the block in which they are declared is hit. The initializer in this case can be any expression.

External and static variables are initialized to 0, while automatic variable has garbage value.

The result is implementation dependent if one tries to modify a const variable.

The direction of truncation for / and the sign of the result for % are machine-dependent for negative operands, as is the action taken on overflow or underflow.

expressions connected by logical operators - && or || are evaluated from left to right and evaluation stops as soon as the truth or falsehood of the result is known. e.g.

if (ptr && ptr->value == val)

ptr needs to be checked before de-referencing its value. So, if ptr is null, the evaluation stops there only and no de-referencing of pointer takes place.

&& has higher precedence over ||

automatic type conversion can convert a "narrow"(int) operand to a "wider"(float) one without losing information. However, the reverse operation loses information and may draw a compiler warning, but is not illegal.

float cannot be used as array index

Automatic type conversion:

If either operand is long double, convert the other to long double

Otherwise, if either operand is double, convert the other to double

Otherwise, if either operand is float, convert the other to float

Otherwise, convert char and short to int

Then, if either operand is long, convert the other to long.

Floats arent automatically converted to double. In general, mathematical functions like those in <math.h> use double precision. float is only used to save space or make code portable on machines on which double precision arithmetic is very expensive.

char is just a small integer, so can be freely used in arithmetic expressions.

definition of C gurantees that standard printable character set will never be negative, but arbitrary bit-sequence stored in character variable may be negative or positive depending on the machine. For portability, specify signed/unsigned in such cases.

Comparison between signed and unsigned operands is machine dependent because they depend on the size of various integer types.

If integer is 16 bits and long is 32 bits then -1L < 1U (unsigned integer gets promoted to signed long)-1L > 1UL (both are long, -1L gets promoted to unsigned long)

If arguments are declared by a function prototype, it causes automatic coercion of any arguments when the function is called. In other words, there is no need to explicitly do a cast to double in the following case where int is passed as an argument:

double sqrt(double);

root = sqrt(2);

increment(++) and decrement(--) operators can only be applied to variables; expression like (i+j)++ is illegal.

Right shifting(>>) an unsigned quantity will fill the vacated bits with zero, for signed quantity it will fill with bit signs(arithmetic shift) or with 0(logical shift) - what actually happens is machine dependent.

use ~0 to get implementation independent all set of 1s. e.g. on 32 bit system it will return 32 SET bits, on 16 bits system it will 16 SET bits.

(n > 0) ? f : n

if f is float and n is int, the above conditional expression will be of type float irrespective of whether n is positive or not. (int gets promoted to float if other operand is float in an expression)

Operator Precedence table:

Operator

Associativity

() [] -> .

left to right

! ~ ++ -- +(unary) -(unary) * (type) sizeof

right to left

* / %

left to right

+ -

left to right

<< >>

left to right

< <= > >=

left to right

== !=

left to right

&

left to right

^

left to right

|

left to right

&&

left to right

||

left to right

?:

right to left

= += -= *= /= %= &= ^= |= <<= >>=

right to left

,

left to right

C doesnt specify the order in which operands are evaluated (except &&, ||, ?:, ','). So, there is no gurantee which function will be evaluated first out of f() and g(). So, if one function depends on variables modified in other - it might not work as expected.x = f() + g()

There is no order of evaluation of expressions passed as function arguments. So, following is not correct:printf("%d %d\n", ++n, power(2,n)); - there is no way to make sure ++n gets executed first and we pass the updated value in function power()

C standard leaves the handling of side-effects to the implementation of the compiler. e.g. the result of following is compiler and machine dependent:a[i] = i++;

Only thing the standard specifies is that all the side-effects on arguments take effect before a function is called.