Notes composed while reading C Primer Plus.
- You should use the
const
keyword when you declare an
array that’s intended to be read-only.
- Arrays declared within a function and without the
static
keyword belong to the automatic storage class.
- If you partially initialize an array the remaining elements are set
to zero.
- Omitting the size from the declaration of an array allows the
compiler to determine the array size. The length can later be calculated
by dividing the size of the array in bits by the size of the first
element (i.e.
sizeof days / sizeof days[0]
, in which
days
is an array).
- In the C99 standard, select elements of an array can be initialized
using designated initializers
(e.g.
int arr[6] = {31,28, [3] = 31,30,31, [1] = 29};
).
- Not checking array bounds allows a C program to run faster. Also,
since the value of an index may not be assigned until execution extra
code would be required to catch all index errors during runtime.
- In addition to symbolic and literal integer constants, C99 allows
the use of constant integer expressions when initializing an array.
Those expressions are used to create variable-length arrays.
- Array notation is simply a disguised use of pointers
(e.g.
arr == &arr[0];
, an array name is also the
address of the first element of the array).
- When adding a number to a pointer, C adds one storage unit, which
for arrays means adding one element of the specified type
(e.g.
arr + 2 == &arr[2]
). This is one reason why you
have to declare the type of object to which the pointer points. Applying
the *
operator to a pointer yields the value stored in the
pointed-to object (e.g. *(arr + 2) == arr[2]
). Applying the
l
operator increases its value by the size of the
pointed-to type. There is no significant technical advantage to pointer
notation over array notation.
- Rather than passing a pointer to an array and a integer parameter to
indicate the number of elements, one can also pass two pointers, which
indicate where the array begins and ends.
- C guarantees that when it allocates space for an array, a pointer to
the first location after the end of the array is a valid pointer.
- The increment operator only works with pointer notation. Also, it is
closer to machine language and will lead to more efficient code with
some compilers. However, many programmers believe that the programmer’s
main concerns should be correctness and clarity, while code optimization
should be left to the compiler.
- When subtracting an integer from a pointer, the pointer has to be
the first operand or a pointer to an integer.
- You can find how far apart two pointers are, which point to the same
array, by subtracting one from the other.
- Do not dereference an uninitialized pointer by storing a value in
the location to which it points. Creating a pointer only allocates
memory to store that pointer itself. Therefore, there is no knowing
where the assigned value will go. The function should be passed
arguments by value unless the program needs to alter the value. This
preserves the integrity of the data.
- If an array is passed as an argument then it must be passed as a
pointer in order to preserve efficiency. This increases the chance of a
programming error corrupting the original data. This is solved in ANSI C
by using the
const
keyword when declaring the formal
parameter in the prototype and the function definition.
- The
const
keyword can be used to create symbolic
constants, which can be done with the #define
directive
too, but const
can additionally create constants arrays,
constant pointers and pointers to constants.
- Declaring a pointer with the
const
keyword
(i.e. const double *ptr
) means that the value cannot
change, but where it points can be change. Conversely, declaring a
pointer like double * const ptr
, allows the value to
change, but not the address.
- It is not possible to assign the address of constant data to a non
constant pointer, and similarly use a constant variable as an argument
in a function, whose parameter was not declared to be constant.
- With a multidimensional array (e.g.
int arr[4][2]
) the
values of arr
and arr[0]
are the same, but the
value of arr[0]++
doesn’t equal arr++
, because
they are being incremented by objects of different sizes. Also, note
that arr
is the address of an address and must be
dereferenced twice to get an ordinary value, which is an example of
double indirection.
- To point to a multidimensional array
(e.g.
int arr[4][2];
) one needs to point to an array of the
correct size and type (i.e. int (*ptr)[2];
). While,
int *ptr[2];
would create an array with two pointers to
int
s.
- The rules for assigning one pointer to another are tighter than the
rules for numeric types. For example, you can assign an
int
value to a double
variable without using type conversion,
but you can’t do the same for pointers of those two types.
- Given the declaration
int arr[3][2];
, then
arr
is a pointer-to-array-of-two-ints or pointer-to-int[2].
That is the type associated with the individual pointer, even though the
address it points to is the first element of an array containing three
pointers. So when arr
is incremented, it increases by the
type associated with it.
- Function declarations including a multidimensional array as a
parameter only require the type of the pointer to be specified in either
notation (i.e.
int sum(int arr[][4], int rows);
or
int sum(int (*arr)[4], int rows);
). The declaration
int sum(int arr[][], int rows);
is invalid because the type
is not correctly specified. Conversely the 3
in
int sum(int arr[3][4], int rows);
will be ignored.
- Before the introduction of C99 it was cumbersome to write a function
to process a two dimensional array because constants were required for
the dimensions in an array declaration. Variable-length arrays allow the
use of variables when dimensioning an array. For example,
int arr[x][y];
is a valid statement if x
and
y
were previously defined.
- Variable-length arrays require the automatic storage class (see
above). The word “variable” in VLA refers to the use of a variable when
specifying the array dimensions, not that the length of the array can be
modified.
- C99 introduced compound literals that represent arrays and
structures, just as there had been for
int
,
double
, char
and strings. An example of using
a array compound literal is to pass it as an actual argument to a
function. For example,
total = sum ((int []){1,2,3,3,0}, 5);
.