Created
January 31, 2020 13:30
-
-
Save neverhoodboy/55d7ec8910053560fd79ebfb45f1f180 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some things every C programmer should know about C | |
PMK 10-16-2002 | |
Types | |
----- | |
Every constant, object, function, and expression in C has a type. | |
Most generally, a type is either an unqualified type or such a type | |
qualified with "const", "volatile", or both qualifiers. Unqualified | |
types comprise three categories: | |
Object types | |
Scalar | |
Arithmetic | |
Integral | |
signed/unsigned/plain | |
character types | |
short, int, long | |
bitfield | |
enumeration | |
Floating-point | |
Pointer to general type | |
Aggregate | |
struct/union of object types and bitfields | |
known-size array of objects | |
Incomplete types | |
void | |
undefined struct/union | |
array of unknown size of objects | |
array of incomplete type (except void) | |
Function returning void or unqualified object type (except array) | |
(with no arguments, with unknown or old-style arguments, or | |
with prototyped arguments of general types) | |
Bitfields may appear only as struct/union members, so | |
there are no pointers to bitfields, arrays of bitfields, or | |
functions taking or returning bitfields. | |
Some types are silently replaced. A qualified array type becomes | |
an unqualified array of the qualified type, and function arguments | |
that are arrays or functions become pointers. | |
Declarator Syntax | |
----------------- | |
Binding is just like expressions: postfix before prefix. So | |
parentheses are necessary in declarators only for function arguments | |
and for pointers to functions and arrays! | |
In qualified pointer types, the pointer qualifiers appear after the *. | |
How to easily read a declaration from left to right: | |
transform function argument types from inside out first | |
move the base type to the end | |
add outer parentheses if there's an initial * | |
change every (*...) to ... -> | |
one -> for each * | |
move qualifiers, so * const becomes const -> | |
Example: const int *(**const x [])() | |
*(**const x [])() const int base type to end | |
(*(**const x [])()) const int add outer parens | |
(**const x [])() -> const int remove outer () | |
x [] const -> -> () -> const int remove inner () | |
array of constant pointers to pointers to functions | |
returning pointers to constant ints | |
Types of Constants | |
------------------ | |
Floating-point constants are "double" unless suffixed by 'F' or 'L'. | |
Integer constants take the first type that fits in one of these lists: | |
with 'U': unsigned int, unsigned long | |
with 'L': long, unsigned long | |
with 'UL': unsigned long | |
decimal: int, long, unsigned long | |
octal, hex: int, unsigned int, long, unsigned long | |
(So 2147483648 is long but 0x80000000-0xffffffff are unsigned int.) | |
Character constants are "int". | |
String literals are arrays of "char". | |
Null pointer constants: | |
any zero-valued integral constant expression, | |
possibly cast to (void *) | |
Operator Precedence and Associativity | |
------------------------------------- | |
These are the classes of operators in decreasing order of precedence. | |
(x) | |
postfix: x[y], x(...), x.y, x->y, x++, x-- | |
prefix: ++x, --x, (type) x, sizeof x, &x, *x, +x, -x, ~x, !x | |
multiplicative: x*y, x/y, x%y | |
additive: x+y, x-y | |
shift: x<<y, x>>y | |
relational: x<y, x<=y, x>y, x>=y | |
equality: x==y, x!=y | |
bitwise and: x&y | |
bitwise xor: x^y | |
bitwise or: x|y | |
logical and: x&&y | |
logical or: x||y | |
conditional: x?y:z | |
assignment: x=y and *= /= %= += -= <<= >>= &= ^= |= | |
sequence: x,y | |
All binary operators are left-associative where it makes a difference, | |
except of course for assignment. The conditional x?y:z operator | |
is the ONLY doubtful case that is right-associative! | |
So x ? y : a ? b : c | |
is x ? y : (a ? b : c) | |
not (x ? y : a) ? b : c | |
Some syntactic equivalences: | |
x->y means (*x).y | |
x[y] means *(x+y) | |
!x means x == 0 | |
Rules applying to x.y and *x may thus apply to x->y or x[y] as well. | |
Note that "!!x" is equivalent to "x != 0". | |
Notes on Operators | |
------------------ | |
Pointer arithmetic is always in units of the pointer's base | |
type. This means that adding or subtracting an integer to or | |
from a pointer yields a pointer to another element in the | |
same array. | |
p + 1 == &p [1] | |
Subtracting two pointers yields a distance that is also in | |
units of the pointer's base type. | |
These operators always return either 0 or 1: | |
!x | |
relations and equalities (<, <=, >, >=, ==, !=) | |
x && y | |
x || y | |
The logical operators (x && y, x || y) do not evaluate their | |
second operands if the first operand determines the result. | |
On two's-complement processors, | |
-x == ~x + 1 | |
~x == -1 - x | |
x & -(1<<y) lowers x to a multiple of a power of two | |
(1 << x) - y == y ^ ((1 << x) - 1) | |
(x&y) + (x|y) == x + y == (x^y) + ((x&y) << 1) | |
Note that sizeof (type) requires parenthese, while sizeof expression | |
does not. | |
Lvalues | |
------- | |
An Lvalue represents the location of an object or function, and | |
might be the target of assignment. An Rvalue is any other value, | |
such as an object's value or a constant or a function result. | |
Only these expressions are Lvalues: | |
identifiers of objects and functions | |
"string literal" | |
(Lvalue) | |
Lvalue.member | |
*Rvalue | |
x.y is an Lvalue if x is, and has all the qualifiers of | |
the types of both x and y. Casts are not Lvalues. | |
As a consequence of the syntactic equivalences noted above, | |
these expressions are also Lvalues: | |
Rvalue->member | |
Rvalue [Rvalue] | |
An Lvalue is modifiable if its type is none of these: | |
function | |
array | |
incomplete | |
const-qualified | |
struct/union with any unmodifiable member | |
Implicit Promotions, Conversions, and Operations | |
------------------------------------------------ | |
Lvalues (other than arrays and functions) become Rvalues of | |
unqualified type except in these contexts: | |
sizeof | |
&x | |
x++, x--, ++x, --x | |
x.member | |
left sides of assignments (x=..., x+=..., etc.) | |
Lvalues of array type are converted to Rvalues of pointer type | |
pointing to their first members except in these contexts: | |
sizeof | |
&x | |
"string literal" in a character array initializer | |
There are no Rvalues of array type in C outside sizeof. | |
Function designators are converted to Rvalues of pointer to | |
function type (except in &x which does that anyway). | |
So if "f" is the name of a function, all of these are synonyms, | |
and all have type "pointer to function": | |
f | |
*f | |
***f | |
************************************f | |
Integral promotions: Rvalues of these types (plain, signed, | |
and unsigned) become "int" or "unsigned int": | |
char | |
short | |
bitfields of type int or smaller | |
enum | |
The famous Usual Arithmetic Conversions: | |
Given two operands to a binary operator, find the first type | |
in this list that matches one of the operands, then convert | |
the other operand to that type. | |
long double | |
double | |
float | |
(apply integral promotion, then) | |
unsigned long | |
long + unsigned -> long or unsigned long | |
long | |
unsigned | |
int | |
Function argument conversions in the absence of argument types: | |
integral promotions | |
float -> double | |
There is an implicit "x != 0" in | |
if (x) | |
while (x) | |
do while (x) | |
for (; x; ) | |
x && x | |
x || x | |
x ? y : z | |
An explicit "x != 0" in these contexts serves no semantic purpose. | |
And "x == 0" in these contexts might be better written as "!x". | |
Scopes, Namespaces, and Linkage | |
------------------------------- | |
Scopes: | |
file | |
block | |
entire function body (for labels) | |
prototype | |
Beware struct/union/enum tags in prototype scopes. | |
Distinct namespaces (per scope): | |
struct/union/enum tags | |
labels | |
everything else | |
Storage classes determine linkage of names thus: | |
if "static" { | |
if file scope | |
linkage is internal | |
else | |
no linkage | |
} else if "extern" or a function { | |
if a declaration is visible at file scope | |
link to it | |
else | |
linkage is external | |
} else if file scope | |
linkage is external | |
else | |
no linkage | |
Object declarations with initializers and function declarations | |
with bodies are definitions. Object declarations without | |
initializers are tentative definitions with zero fill if | |
they are not "extern". | |
Translation Steps | |
----------------- | |
A C compiler must behave as if each of these steps were completely | |
performed before proceeding. | |
Turn end-of-line indicators into newlines and replace ??trigraphs | |
Delete all backslash-newline pairs | |
Form tokens and replace comments by single spaces | |
Preprocessing and macro expansion | |
Process \escape sequences in 'character constants' and "string literals" | |
Delete white space, including newlines | |
Concatenate adjacent "string literals" | |
Parse and generate code | |
Link |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment