Limits
https://en.cppreference.com/w/c/types/limits
Type | Min value | Max value |
---|---|---|
char |
CHAR_MIN |
CHAR_MAX |
signed char |
SCHAR_MIN |
SCHAR_MAX |
unsigned char |
UCHAR_MIN |
UCHAR_MAX |
short |
SHRT_MIN |
SHRT_MAX |
unsigned short |
USHRT_MIN |
USHRT_MAX |
int |
INT_MIN |
INT_MAX |
unsigned int |
UINT_MIN |
UINT_MAX |
long |
LONG_MIN |
LONG_MAX |
unsigned long |
ULONG_MIN |
ULONG_MAX |
long long |
LLONG_MIN |
LLONG_MAX |
unsigned long long |
ULLONG_MIN |
ULLONG_MAX |
scanf
Use of scanf()
is usually frowned upon as it doesn't give the programmer enough control over what could happen (Eg: buffer overflow attack).
Yet, it's quite useful to get stuff done quick when you are just messing around with C while learning it.
In that sense, scanf()
is in fact, quite useful.
Format specifiers
FS | Type |
---|---|
%zu |
size_t |
%n |
Number of chars written so far |
%o |
Octal |
%% |
Literal '%' |
%c |
char |
%d |
int |
%s |
string |
%f |
int |
%n |
a special one |
%d |
int |
%u |
unsigned int |
%p |
address (pointer) |
%x |
int as hexadecimal (0x, 0X, ,) |
%X |
int as hexadecimal (0x, 0X, ,) |
%[^\n] |
Exclude (till \n ) |
Modifiers:
- l: long
- ll: long long
Format specifier | Type |
---|---|
%ld |
long int |
%lld |
long long int |
%lf |
double |
%lu |
long unsigned |
See: https://en.cppreference.com/w/c/io/fprintf
- Precision
- %.2f
- %.f // No precision. Truncate.
- Width and alignment
- Right alignment: %2f
- Left alignment: %-2f
*
- In printf(), variable as width: `%*.*f`, width, precision
- In scanf(), suppresses assignment: `%*f`
- Wide characters
- L' stuff as format specifiers.
Interesting behavior and facts
Undefined behaviour means that compiler can do whatever it likes.
No evaluation within sizeof
#include<stdio.h>
int main() {
int a=3, b;
sizeof(a=10);
b = "a=%d, b=%d\n", a, b);
printf(// a=3, b=4
// Value of b is compiler/arch dependent though.
return 0;
}
(Thanks to Kevin for telling me this.)
—
Expression inside sizeof
is not evaluated. So the value of a
never changed.
In sizeof(a=10)
, type of a=10
is looked at. It is found to be int
. So essentially, it is same as saying sizeof(int)
or sizeof(10)
, I guess.
sizeof(int)
is usually 4, but can be different depending on architecture and compiler.
Multiple sequence points
Multiple sequence points => undefined behaviour.
#include<stdio.h>
int main() {
int a=3;
"%d\n", a++ + ++a);
printf(// 8
// Needn't be so though. It's undefined behaviour.
return 0;
}
Evaluation could be like:
((a++) + ++a))
(a++ + (++a))
(Thanks to Kevin for reminding me about this.)
—
Similarly, the following are also invoke undefined behaviour due to multiple sequence points.
i = i++
i = ++i
printf("%d %d", i++, i++)
See:
- https://stackoverflow.com/questions/26894509/c-why-does-i-i-invoke-undefined-behaviour
- https://stackoverflow.com/questions/4968854/is-the-behaviour-of-i-i-really-undefined
- https://stackoverflow.com/questions/22616986/order-of-evaluation-of-arguments-in-function-calling
Order of function arg eval
Order of function argument evaluation is not defined in the C standard.
The following snippet could print "HiHello" or "HelloHi".
"Hi"), printf("Hello")); f(printf(
Calling main
from main
Looks like this is possible in C (gcc).
#include<stdio.h>
int ctr = 5;
int main() {
if(ctr>0) {
"Hi\n");
printf(
ctr--;
main();
}
}
/*
Hi
Hi
Hi
Hi
Hi
*/
Apparently, C++ standard prohibits this though gcc allows it.
Digraphs
Digraph | Equivalent |
---|---|
<: |
[ |
:> |
] |
<% |
{ |
%> |
} |
%: |
# |
%:%: |
## |
Trigraphs
- Used when old keyboards didn't have some of the keys common today.
- Silently changes the tokens, so considered unsavory.
Trigarph | Result |
---|---|
??= |
# |
??( |
[ |
??/ |
\ |
??) |
] |
??' |
^ |
??< |
{ |
??! |
│ |
??> |
} |
??- |
~ |
C pre-processor (CPP)
- Define a macro:
#define NAME body
- Undefine a macro:
#undef NAME
- Check if a macro is defined:
#ifdef
- Check if a macro is not defined:
#ifndef
- Conditions:
#if
,#elif
,#else
,#endif
- Token concatenation of
a
andb
:a##b
- Stringification of
a
:#a
// Check if a macro is defined
#ifdef NAME
"NAME is defined\n");
print(#else
"NAME is not defined\n");
print(#endif
Often ignored error checking
- Return value of malloc, printf, scanf
- strcat, sprintnf, strcpy, etc
- strtoint
More
- Bit field packing
- Div by zero gives INF for float. Segfault for int
- Original C compiler was 1-pass. That's why it mandated separate function declaration and definition
malloc
can return pointer to a smaller memory area even if the requested amount of memory isn't available.Overcommit. Kernel overcommits by default
sysctl vm.overcommitmemory
$ sudo sysctl vm.overcommit_memory vm.overcommit_memory = 0
- Heap is part of process' address space, which is part of virtual memory
- Virtual memory could be thought of RAM + Swap (not the same though??)