Got Questions? Try office hours, email or the class newsgroup.
The terms "row-major" and "column-major" are actually insufficient
to correctly describe the generalization to 3 (or more) dimensions. With
3 dimensions there are actually (at least) 3 distinct ways of laying out
storage but real languages only use generalizations of row-major and column-major.
Remember that one way of thinking about these words is "what dimension
varies most quickly"? If you scan over the linear representation of the
array in memory and have a "speedometer" showing the i, j and k indices,
which one is spinning the quickest? With row-major, the column or "rightmost"
index varies most quickly. With column-major, the row of "leftmost" index
varies most quickly. These are the two generalizations we are interested
in. For "row-major" storage of a 3d structure, the rightmost index still
varies most quickly but now it is the 3rd dimension (k).
Think of a 2x2x2 cube facing you. Element 0,0,0 is in the upper-left corner.
If we look at the very next position in the "linearized" stored array,
which element do we find? The one to the right? The one below? Or the one
behind? For "row-major" the righmost index (k) varies most quickly, so
the next one in memory is the one BEHIND the first element. After we have
stored all the elements behind 0,0,0 where do we go next? To the right
or below? For row-major the k index varies most quickly, the j index varies
the next most quickly and the i index stays the same for the longest period
of time. That means we stay in the same row, so now we start storing elements
to the right of the first element. That is we vary the column index. Since
the leftmost index (the row) varies the least quickly, we will store the
"top slice" of the 3d array first and then the slice below it.
For column-major, the leftmost index varies most quickly, that is, the
row, then the column and the k dimension stays the same for the longest.
Thus we store the "front" slice of the cube first, then the slice behind
it and so forth.
The third (weird) storage possibility would be to store the left
slice of the cube first and then the slice to its right and so forth but
no language that I know of uses this scheme. This corresponds to the j
index varying least quickly. We would still have to decide what varies
the next most (i or k?).
Here are the access formulas for "row-major" (rightmost index varies most
quickly) and for "column-major" (leftmost index varies most quickly). Assume
that there are I elements in the i dimension , J elements in the j dimension
and K elements in the k dimension.
rightmost index varies most quickly ("row-major"):
BASE
+ i*K*J*ESIZE
// slices above
+ j*K*ESIZE
// rows to the left
+ k*ESIZE
// elements in front
leftmost index varies most quickly ("column-major"):
BASE
+ k*J*I*ESIZE
// slices in front
+ j*J*ESIZE
// columns to the left
+ i*ESIZE
// elements above
Both of these can be expressed as a recurrence for the general case. (Whew!!)
int fun( int *i ) {
*i
+= 5;
return
4;
}
void main() {
int
x = 3;
x =
x + fun(&x);
}
What is the value of x after the assignment statement in main, assuming:
1) operands are evaluated left to right.
3 + the
result of fun()
3 + 4
7
2) operands are evaluated right to left.
x + the
result of fun
x + 4 but fun adds 5 to
x so x is now 8
8 + 4
12
Here is an example of an Ada variant record:
type NODE (TAG : BOOLEAN) is
record
case TAG is
when true => COUNT : INTEGER;
when false => SUM : FLOAT;
end case;
end record;
You can think about this problem generally but one "easy" solution
would have been to simply lookup Ada's mechanism for doing this and describe
it! The basic idea is to not allow a "partial assignment". Whenever you
change the tag you must change the variant and vice-versa. Attempts to
alter the tag and a variant are always type-checked to make sure that the
tag and variant being assigned match. A very simple solution would be to
only allows assignments like this:
MY_NODE.TAG, COUNT := true, 10;
MY_NODE.TAG, SUM := false, 3.1415;
"Partial assignments" are not allowed (like this):
MY_NODE.TAG := true; // not allowed!
MY_NODE.COUNT := 10; // record is possibly inconsistent
in between
Ada really likes compile-time type checking so it does not
allow the tag value to be computed. It must a literal value, so you can't
say things like:
MY_NODE.TAG := FLAG, 3.1415;
It would be possible to allow "computed tags" but you would
need runtime type checking to ensure that the value actually assigned was
correct and consistent.
Ada goes a bit further and requires that you assign ALL FIELDS in the record
(even non-variant fields) when you change the tag. Ada has a notation for
"literal record values" so the actual notation might look like this (the
tag value, which must a literal, comes first):
MY_NODE := (true, 10);