This repo is my (ugly) solution to exercises in K&R C book, and some (random) notes...
-
printf("%3.2f", x);
%a.bf: print a floating point number, with width a, b digits after decimal point -
assignment a = b+c has a value, which is the value of left hand side after assignment
-
EOF(end of file) is -1
-
char is just small integer, '0' is essentially an integer number
-
in C, function's argument is call by value
-
when using external variable, one should use "extern" to make variable declaration
definition: variable is created or assigned storage
declaration: no storage is allocated -
the default return value type of function is int
-
External and static variables are initialized to zero by default.
-
short, long, signed, unsigned are qualifiers
-
The standard header <ctype.h> defines a family of functions that
provide tests and conversionsthat are independent of character
set. e.g. tolower(), isdigit() -
The expression ++n increments n before its value is used, while
n++ increments n after its value has been used
1)
n = 5;
x = n++; // x = 5, n = 6
2)
n = 5;
x = ++n; // x = 6, n = 6 -
The unary operator ~ yields the one's complement of an integer.
That is, it converts each 1-bit into a O-bit and vice versa. -
Right shifting an unsigned quantity always fills vacated bits with zero.
Right shifting a signed quantity will fill with sign bits
("arithmetic shift") on some machines and with O-bits ("logical shift")
on others. -
expr1 op= expr2 is equivalent to expr1 = (expr1) op (expr2)
x *= y+1 is equivalent to x = x * (y+1) -
The conditional expression expr1 ? expr2 : expr3
Only one of expr2 and expr3 is evaluated. -
sizeof is an operator
-
C, like most languages, does not specify the order in which the operands of
an operator are evaluated. (The exceptions are &&, ||, ?:, and ',')
- else is associated with the closest previous else-less if.
if (n > 0)
if (a > b)
z = a;
else
z = b;
the else goes with the inner if.
-
The scope of an external variable or a function lasts from the point at which
it is declared to the end of the file being compile. -
extern qualifier: if an external variable is to be referred to before it is
defined, or if it is defined in a different source file from the one where it is
being used, then an extern declaration is mandatory. -
A register declaration advises the compiler that the variable in question
will be heavily used. The idea is that register variables are to be placed in
machine registers, which may result in smaller and faster programs. But com-
pilers are free to ignore the advice. -
The static declaration, applied to
1). an external variable or function, limits the scope of that object to the
rest of the source file being compiled.
2). an internal variable, the variable will remain in existence rather than coming and
going each time the function is activated. -
In the absence of explicit initialization, external and static variables are
guaranteed to be initialized to zero; automatic and register variables have unde-
fined (i.e., garbage) initial values. -
#include "filename", <filename>. If the filename is quoted, searching for the file
typically begins where the source program was found; if it is not found there, or
if the name is enclosed in < and >, searching follows an implementation-defined rule
to find the file. -
String literals can be concatenated in C directly.
char s[] = "4" " 123"; // legal, s = "4123" -
macro:
#define name replacement text
1). Normally the replacement text is the rest of the line, but a long defini-
tion may be continued onto several lines by placing a \ at the end of each line
to be continued.
#define f(a, b) replacement text
Each occurrence of a formal parameter (here a and b) will be replaced by the
corresponding actual argument.
1). #: Formal parameters are not replaced within quoted strings. Only if a
parameter name is preceded by a #, the combination will be expanded
into a quoted string with the parameter replaced by the actual
argument.
e.g. #define dprint(expr) printf(#expr "=%d\n", expr)
dprint(x) -> printf("x" "=%d\n", x);
2).##: concatenate actual arguments. If a parameter in the replacement text is adja-
cent to a ##, the parameter is replaced by the actual argument, the ## and sur-
rounding white space are removed
e.g. #define paste(front, back) front ## back
paste(name, 1) -> name1 -
conditional inclusion: include code selectively
1).
#if (constant integer expression: defined(xxx))
...
#elif ...
...
#else
...
#endif2).
#ifndef(#ifdef) xxx
...
#endif
-
An array-and-index expression is equivalent to one written
as a pointer and offset.
There is one difference between an array name(a) and a pointer(pa).
A pointer is a variable, so pa=a and pa++ are legal. But an
array name is not a variable; a=pa and a++ are illegal. -
a[-1] is syntactically legal.
-
If p and q point to elements of the same array, and p<q,
then q-p+1 is the number of elements from p to q inclusive. -
The value of *t++ is the character that t pointed to
before t was incremented. -
Multi-dimensional array as function's argument or declaration,
only the first dimension(subscript) of an array can be not specified;
all the others have to be specified. -
The format argument of printf can be an expression.
E.g., printf ((argc > 1) ? "%s " : "%s", *++argv);
-
The keyword struct introduces a structure declaration, which is a list of
declarations enclosed in braces. An optional name called a structure tag may
follow the word struct. The right brace that terminates the list of members
may be followed by a list of variables. -
Both . and -> associate from left to right.
These four expressions are equivalent:
r.pt1.x
rp->pt1.x
(r.pt1).x
(rp->pt1).x -
Addition of two pointers is illegal. Subtraction is legal.
-
A simple hash function for string:
unsigned hash(char *s) { unsigned hashval; for (hashval = 0; *s != '\0'; s++) hashval = *s + 31 * hashval; return hashval % HASHSIZE; }
-
typedef for creating new data type name,
typedef int Length
In effect, typedef is like #define, except that since it is interpreted by the compiler,
it can cope with textual substitutions that are beyond the capabilities of the
preprocessor.
main reasons for using typedef:
1). The first is to parameterize a program against portability problems.
If typedefs are used for data types that may be machine-dependent, only the
typedefs need change when the program is moved.
2). It may be easier to understand than the true type -
bit-field:
struct s { unsigned int f1 : 1; unsigned int f2 : 3; }x;
x.f1 has 1 bit, x.f2 has 2 bits. But they are not arrays, and they do
not have addresses, so the & operator cannot be applied to them.
-
printf's return value: the number of characters printed.
-
function declaration with variable-length argument list
int f(int a, double b, ...)
where the ... means that the number and types of these arguments
may vary. The declaration ... can only appear at the end of an argument list.
There must be at least one named argument.
va_list p;
va_start(p, b)
va_arg(p, /*int,double,...*/)
...
va_end(p) -
The file pointers stdin and stdout are objects of type FILE *. They are
constants, however, not variables, so it is not possible to assign to them.
- In the UNIX operating system, all input and output is done by reading or
writing file. The user program refers to the file only by the file
descriptor(a non-negative intege). The program gets file descriptor by
opening the file.
functions: open, read, write, close, lseek(change read/write position)