int ia1[5] = { 0, 1, 2 }; // ia1 = { 0, 1, 2, 0, 0 }; int ia2[4][3] = { 0, 3, 6, 9 }; // ia2 = { { 0, 3, 6 }, { 9, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }; int ia3[4][3] = { {0}, {3}, {6}, {9} }; // ia3 = { { 0, 0, 0 }, { 3, 0, 0 }, { 6, 0, 0 }, { 9, 0, 0 } };
ia[1, 2]
is a legal construct, but is equivalent to
ia[2]
because 1,2
is evaluated as a comma
expression.
The array name by itself stands for a constant
pointer to its first element,
array = &(array[0])
. So array name itself only has
rvalue: refers to address of beginning of array and cannot be
changed. Pointer also has an lvalue: it is allocated and
can be set to address of other memory content with same
data type.
rvalue is variable's data value, might think as meaning read value; lvalue is variable's memory address value, might think as meaning location value. In general, the left-hand side of an assignment operator always requires an lvalue.
sizeof array
return number of bytes of the whole
array
, but sizeof pointer
means
sizeof(pointertype*)
. Idiom for determining
number of elements in an array does not work for pointer:
arraytype array[ARRAYNUM]; size_t arraynum = sizeof array / sizeof array[0];Accesing each element of multi-dimensional array
tabletype table[ROWNUM][COLNUM]; size_t rownum = sizeof table / sizeof table[0]; size_t colnum = sizeof table[0] / sizeof table[0][0]; // This do not work when tested, will revise later // if each row is aligned with unknown padding for (tabletype **row=table, size_t i=0; i<rownum; i++, row++) { for (tabletype *elem=*row, size_t j=0; j<colnum; j++, elem++) { // access each element as *elem } } // if no padding (contiguous) for (tabletype *elem=&table[0][0], size_t i=0; i<rownum; i++) { for (size_t j=0; j<colnum; j++, elem++) { // access each element as *elem } }
An exception in C/C++, passing array is by reference by default.
Array is not pointer, but array decays to pointer when passed as
parameter to a function. No bound checking for array parameter,
returntype Function(arraytype array[ARRAYNUM])
is
equal to returntype Function(arraytype array[])
or
returntype Function(arraytype *array)
.
int GetLoginNameFromUser(char buffer[]) { int result = 0; if (fgets(buffer, sizeof buffer, stdin) == NULL) result = -1; return result; }The bug occurs because
sizeof buffer
becomes
sizeof(char*)
but not length of array as expected,
as char buffer[]
decays to char *buffer
.
This is why need to pass length of array as extra argument to
function for manipulating the array.
arraytype array[ARRAYNUM]; // ARRAYNUM is allocated size // arraynum can be ARRAYNUM, or <ARRAYNUM if array is not // fully utilized, which is effective size returntype Function(arraytype array[], size_t arraynum);To pass array to function without extra argument (but limit to fixed type and fixed size array):
typedef struct { arraytype a[ARRAYNUM]; } Array; returntype Function(Array *array) { // statements ... for (int i=0; i<(sizeof array->a / sizeof array->a[0]); i++) { // access array element as array->a[i] } // statements ... }
returntype Function(arraytype (&a)[ARRAYNUM]) { // statements ... for (int i=0; i<ARRAYNUM; i++) { // access array element as a[i] } // statements ... }Using template to eliminate predefined constant, but introduce code bloat (different definition for different type and size of array)
template <typename arraytype, int arraynum> returntype Function(arraytype (&a)[arraynum]) { // statements ... for (int i=0; i<arraynum; i++) { // access array element as a[i] } // statements ... } // usage in application int ia[] = { 10, 7, 14, 3, 25 }; returntype r = Function(ia); // arraytype=>int, arraynum=>5
template <typename arraytype, int arraynum> class Array { public: Array(arraytype* array) : arraynum_(arraynum) { for (int i=0; i<arraynum; i++) array_[i] = array[i]; } private: arraytype array_[arraynum]; int arraynum_; }; // usage in application int ia[] = { 10, 7, 14, 3, 25 }; Array<int, sizeof(ia)/sizeof(int)> iA(ia);
The best way is defining array as a class, which can also
provide bound checking, IO and many other features
(actually no need the arraynum
template
argument, example above is for illustration only).
#include <iostream> const int LineLength = 12; // elements to a line void PrintArray(const arraytype *a, size_t size) { cout << "( " << size << " ) < "; for (int i=0; i<size; i++) { if (i%LineLength == 0 && i != 0) cout << "\n"; cout << a[i]; if (i%LineLength != LineLength-1 && i != size-1) cout << ", "; } cout << " >"; }Index