See Sec: 7.4.5 Multidimensional arrays of IEEE 1800-2009
The dimensions preceding the identifier set the packed dimensions.
The dimensions following the identifier set the unpacked dimensions.
bit [3:0] [7:0] joe [1:10]; // 10 elements of 4 8-bit bytes
In a multidimensional declaration, the dimensions declared following the type and before the name
([3:0][7:0] in the preceding declaration) vary more rapidly than the dimensions following the name
([1:10] in the preceding declaration).
When referenced, the packed dimensions ([3:0], [7:0]) follow
the unpacked dimensions ([1:10]).
i.e. In a list of dimensions, the rightmost one varies most rapidly, as in C.
However, a packed dimension varies more rapidly than an unpacked one.
bit [1:10] v1 [1:5]; // 1 to 10 varies most rapidly
bit v2 [1:5] [1:10]; // 1 to 10 varies most rapidly
bit [1:5] [1:10] v3 ; // 1 to 10 varies most rapidly
bit [1:5] [1:6] v4 [1:7] [1:8]; // 1 to 6 varies most rapidly, followed by 1 to 5, then 1 to 8 and then 1 to 7
Example 1: You can view the setup like this:

Example 2:
bit [1:5][10:16] foo [21:27][31:38];
This is similar as example 1.

Example 3:
module array();
bit [1:5][10:16] foo1 [21:27][31:38],foo2 [31:27][33:38];
initial
begin
$display(" dimensions of foo1 is %d foo2 is %d",$dimensions(foo1),$dimensions(foo2) );
end
The declaration in the above module is as same as
bit [1:5][10:16] foo1 [21:27][31:38];
bit [1:5][10:16] foo2 [31:27][33:38];
As Dave has mentioned, $dimensions function gives you the total number of dimensions packed and unpacked. Sice both foo1 and foo2 are 4 dimensional the displayed value is 4.
For more on this topic please go though the following link. This would clear your all doubts. A nice representation is provided here.
http://testbench.in/SV_09_ARRAYS.html