Below is the code
void printLoop(type?? p){
for(int i = 0; i<2;i++)
{
for(int e = 0;e<3;e++)
{
cout<<p[i][e]<<" ";
}
cout<<"\n";
}
}
void array()
{
int a[2][3] = {{1,2,3},{4,5,6}};
int (*p)[3] = a;
printLoop(p);
}
Basic idea is that I want to print out the array using a for loop in the printLoop func. However, I need to know the type of that pointer which has the address of the 2D array. What's the pointer's type? Is it int (*)[]? I'm confused.
Also what does "(*p)" mean(from int (*p)[3]) ? Thanks a lot!
what does "(*p)" mean(from int (*p)[3]) ?
p is a pointer to an array of size 3 of objects of type int.
You have multiple possibilites for your printLoop function (though with the general C-restriction that you can leave at most one -- the outermost declarator empty):
You can specify the dimensions explicitly:
void printLoop(int p[ 2 ][ 3 ]);
The only advantage with this method is that the implementation can consider that the array being passed is of the desired size (i.e. 2x3 matrix of ints) as a pre-condition.
You can leave out the [ 2 ] part entirely:
void printLoop(int p[][ 3 ]);
or,
void printLoop(int (*p)[ 3 ]);
You can use a pointer to a pointer of int
You will also need to pass the dimensions (if you skip one that is) along to make sure that you don't access out-of-bounds memory. So, your function signature should go like this:
void printLoop(int (*p)[ 3 ], int dim);
For the printLoop function, int p[2][3] as an argument should just work.
int (*p)[3] = a;
p is a pointer to an array of 3 ints, initialized to point to a.
First of all, your code is not very modern C++. It's basically "c with iostreams".
Second of all, printLoop(int p[2][3]) is the signature you're looking for even though again, it's not the best way of doing things at all.
Third of all, int (*p)[3] is analyzed as follows: Start at the name which is p and take a look around (first to the right and then to the left yet here it doesn't matter) until you "hit" braces. There's only a star at it, so you can say that p is a pointer. Now you recursively do the same analysis again, you see [3], which means that p is a pointer to an array that has 3 ints.
Now I'd like to mention the following:
Use std::array for staticly-sized arrays.
Use std::vector for dynamicaly-sized arrays.
Oh, also, I myself wouldn't use a 2D array, they are clunky and just a syntactic sugar (around the basic "array" notion which is a syntactic sugar as well).
So perhaps, something like this, brain compiled, hopefully correct, C++11 abusing:
std::array<int, 3 * 2> p = {{1, 2, 3, 4, 5, 6}};
std::for_each(std::begin(p), std::end(p), [](int elem){ std::cout<<elem; });
Nice and dandy. You could also have lambda check for some "2d array" sizes and insert newlines if you so desire.
Related
I'm solving a question where a function is defined as following:
vector <int> func(int a, vector<vector<int>> B[]){
// Some stuff
}
I'm confused about why the second parameter is not simply vector<vector<int>> B. Why the extra [] part in the parameter? Can someone clarify the meaning of this?
The vector, B, is populated by a similar code snippet:
vector<vector<int>> B[10];
while(num--){
cin>>u>>v>>w;
vector<int> t1,t2;
t1.push_back(v);
t1.push_back(w);
B[u].push_back(t1);
t2.push_back(u);
t2.push_back(w);
B[v].push_back(t2);
}
Just as int foo(char str[]) is a function that takes a (c-style) array of characters, so int foo(vector<vector<int>> B[]) takes an array of vectors of vectors ... of integers. This means that it's three-dimensional data, requiring 3 indices to access the elements (fundamental data type; in this case, int), like B[i][j][k] = 5. Without the extra [] in the API it'd be two-dimensional data: a vector of vectors.
Note that int foo(char str[]) is equivalent to int foo(char str[5]) which is equivalent to int foo(char * str).
In C we usually add the [] to a function declaration to imply that we expect to receive an array of those elements; while * is often used when we expect at most one element. Likewise, adding the number [5] is basically just a comment to the user of the code that they expect 5 elements, but the compiler won't enforce this. These conventions carry over to C++ when we use these c-style arrays ... which is rare.
With c-style arrays there's either going to be a maximum array size in the comments somewhere; or, more commonly, it's provided as an input. That may be what the first argument of the function is supposed to represent.
I agree with KungPhoo here that this API looks suspiciously bad. I'd expect bugs/bad performance just because the choices seem very amateurish. The c-style array means the function can't know where the end of the c-style array is - but the vectors mean that we give up some of the (niche) benefits of c-style simplicity (especially because they're nested!). It seems to be getting the worst of both worlds. But, perhaps, there may be a very niche justification for the API.
B ist a static array (C-style) of 10 elements [0 .. 9]. It's not safe and this code is a terrible mess.
Better use std::array<std::vector<str::vector<int>>, 10> B; instead to have index checking.
ok so suppose I have a function myFunction. Then in main i have a multi dimensional array of pointers. I want to pass a pointer to this array of pointers into myFunction. How would I do that? I know that If you want to pass an int to my function, one can write the function as
myfunct( int x) { ...}
What would that type of x be if I have to pass a pointer to an array of pointers? Thanks in advance :D
Typically you want to modify the elements of an array rather then the actual pointer. The actual pointer is given by malloc and if you change it, by writing directly to the value, it won't affect the memory allocation (except you might loose the initial pointer...).
This might be what you're looking for in a 2D array.
void myfunct(int** ptr,int items, int array_items)
{
//some code
}
int main(int argc, char *argv[])
{
const auto items = 5;
const auto array_items = 7;
int** multi_dimensional_array = reinterpret_cast<int**>(std::malloc(items * sizeof(int*)));
for (auto i = 0 ;i < items;++i)
{
multi_dimensional_array[i] = static_cast<int*>(std::malloc(sizeof(int) * array_items));
}
myfunct(multi_dimensional_array,items,array_items);
//deallocate
}
Wrap your multidimensional array inside a class. That way you can carry the data and dimensions in one block and passing it around is as simple as moving around any other class.
Remember to observe the Rules of Three, Five, and Zero, whichever best applies to how you store your array inside your class. std::vector is a personal favourite because it allows you to use the Rule of Zero.
For example:
#include <iostream>
#include <vector>
struct unspecified
{
};
template<class TYPE>
class TwoDee{
int rows;
int cols;
std::vector<TYPE> data;
public:
TwoDee(int row, int col):rows(row), cols(col), data(rows*cols)
{
// does nothing. All of the heavy lifting was in the initializer
}
// std::vector eliminates the need for destructor, assignment operators, and copy
//and move constructors. All hail the Rule of Zero!
//add a convenience method for easy access to the vector
TYPE & operator()(size_t row, size_t col)
{
return data[row*cols+col];
}
TYPE operator()(size_t row, size_t col) const
{
return data[row*cols+col];
}
};
void function(TwoDee<unspecified *> & matrix)
{
// does stuff to matrix
}
int main()
{
TwoDee<unspecified *> test(10,10);
function(test);
}
To directly answer your question, typically the type passed will be int * for a vector of int, and int ** for a 2D array of int
void myfunct( int **x)
{
x[2][1] = 25;
return;
}
If for some reason you wanted that to be an array of int pointers instead of int you need an extra *.
void myfunct( int ***x)
{
*(x[2][1]) = 25;
return;
}
Let me first try to interpret the exact type that you want to deal with. I suppose in your main function there is a "multidimensional array" which stores pointers for each element. As an example, let's say you have a 3-dimensional array of pointer to integer type.
Assume that you know the size of the array:
C style array will look like this:
int *a[4][3][2];
that means a is a 4x3x2 array, and each element in the array is a pointer to integer. So overall you now have 24 pointers to integer in total, as can be seen by testing the result of sizeof(a) / sizeof(int*) (the result should be 24). Okay, so far so good. But now I guess what you want is a pointer to the array a mentioned above, say b, so b is defined
int *(*b)[4][3][2] = &a;
Notice that although now b looks intimidating, in the end it is just a pointer which just stores an address, and sizeof(b) / sizeof(int*) gives 1 as the result. (The * inside parenthesis indicates b is pointer type, so b is a pointer to a "multidimensional array" of pointers to integer.)
Now to pass b to myFunction, just give the same type of b as argument type in the declaration:
void myFunction(int *(*x)[4][3][2]) {
// do something
}
And that's it! You can directly use myFunction(b) to invoke this function. Also, you can test that inside myFunction, x is still of the size of one pointer, and *x is of the size of 24 pointers.
*Note that since we are passing a pointer to array type into the function, the array-to-pointer decay does not apply here.
Assume you don't know the size of the array at compile time:
Say you have int N1 = 4, N2 = 3, N3 = 2; and you want to initialize a N1xN2xN3 array of pointer to integer, you cannot directly do that on the stack.
You could initialize use new or malloc as suggested in #Mikhail's answer, but that approach takes nested loops for multidimensional arrays and you need to do nested loops again when freeing the memory. So as #user4581301 suggests, std::vector provides a good wrapper for dynamic size array, which do not need us to free the memory by ourselves. Yeah!
The desired array a can be written this way (still looks kind of ugly, but without explicit loops and bother of freeing memory)
std::vector<std::vector<std::vector<int*>>> a (N1,
std::vector<std::vector<int*>> (N2,
std::vector<int*> (N3)
)
);
Now, b (the pointer to a) can be written as
auto *b = &a;
You can now pass b with
void myFunction(std::vector<std::vector<std::vector<int*>>>* x) {
// do something
}
Notice that the * before x means x is a pointer.
If I declare a 2D array
int A[sz][sz];
How can I create a pointer to this object?
I ask because I want to return an array via pointer to a pointer, int**, from a function but I want to build the array without knowing the size beforehand. The size will be passed as an argument. I want to know if there is a way to do this without using dynamic allocation.
The problem is if I do something like int** A inside the function this gives A no information about the size I want.
How can I create the array and then assign a pointer to this array, if it's a 2D array.
I should be more clear. I want return a pointer to a pointer so it wouldn't be a pointer to the 2D array but a something like int**.
Your problem is, that a 2D array in the form int** requires an array of int* for the two step dereferencing, which simply does not exist when you declare an array with int A[sz][sz];.
You can build it yourself like this:
int* pointers[sz];
for(size_t i = sz; i--; ) pointers[i] = A[i];
This might seem absurd, but is rooted in the way C handles arrays: A[i] is of type int ()[sz], which is the subarray of row i. But when you use that array in the assignment, it decays to a pointer to the first element in that subarray, which is of type int*. After the loop, A and pointers are two very different things (the type of A is int ()[sz][sz])
Sidenote: You say that you want to return this from a function. If your array is allocated on the stack, you must not return a pointer to its data, it will disappear the moment your function returns. You can only return pointers/references to objects that have either static storage or are part of another existing object. If you fail to comply with this, you are likely to get stack corruption.
Edit:
A little known fact about C is, that you can actually pass around pointers to real C arrays, not just the pointer types that an array decays to. Here is a small program to demonstrate this:
#include <stddef.h>
#include <stdio.h>
int (*foo(int size, int (*bar)[size][size], int y))[] {
return &(*bar)[y];
}
int main() {
int mySize = 30;
int baz[mySize][mySize];
int (*result)[mySize];
result = foo(mySize, &baz, 15);
printf("%ld\n", (size_t)result - (size_t)baz);
}
The expected output of this example program is 1800. The important thing is that the actual size of the array must be known, either by being a compile time constant, or by being passed along with the array pointer (and if it's passed along with the array pointer, the size argument must appear before the array pointer does).
Let me flesh out your question a little bit. You mention:
I ask because I want to return an array [...] from a function but I
want to build the array without knowing the size beforehand. The size
will be passed as an argument. I want to know if there is a way to do
this without using dynamic allocation.
For the I want to return an array from a function [...] size passed as an argument, it seems reasonable to me that you can use std::vector everywhere, and call its .data() method when you need access to the underlying array (which is guaranteed to be contiguous). For example:
std:vector<double> myfun(size_t N) {
std::vector<double> r(N);
// fill r[0], r[1], ..., r[N-1]
return r;
}
// later on:
r.data(); // gives you a pointer to the underlying double[N]
And for the I want to to do this without dynamic allocation, that is not possible unless you know the size at compile time. If that is the case, then do exactly as before but use std::array, which can implement optimizations based on known compile-time size:
std::array<double, N> myfun() {
std::array<double, N> r;
// fill r[0], r[1], ..., r[N-1]
return r;
}
// later on:
r.data(); // gives you a pointer to the underlying double[N]
And to be generic, I would actually use a template function capable of working with arbitrary containers:
template<typename T>
void myfun(T& data) {
for(int k=0; k<data.size(); k++) {
// do stuff to data[k]
}
}
// call as, for example:
std::vector<double> data(10);
myfun(data);
// or equally valid:
std::array<double, 10> data;
myfun(data);
Finally, if you are working with two-dimensional data, please remember that when you store the Matrix in row-major order that is:
Matrix [1, 2; 3 4] is stored as [1 2 3 4]
then you can refer to element (i, j) of the matrix by calling data[i * ncols + j]. For example: consider a three by four matrix:
a b c d
e f g h
i j k l
The element (2, 2) (that is: third row, third column because we assume zero-based C-type indexing) is calculated as: M[2][2] = M[2 * 4 + 2] = M[10] = k. This is the case because it was stored as:
[a b c d e f g h i j k l]
[0 1 2 3 4 5 6 7 8 9 10 11]
and k is the element with index 10.
the responses to your question are weird. Just do this:
int A[2][2];
int**p =NULL;
*p = A[0]; // **p==A[0][0] , *(*p+1)==A[0][1]
Let's say I have an array arr. When would the following not give the number of elements of the array: sizeof(arr) / sizeof(arr[0])?
I can thing of only one case: the array contains elements that are of different derived types of the type of the array.
Am I right and are there (I am almost positive there must be) other such cases?
Sorry for the trivial question, I am a Java dev and I am rather new to C++.
Thanks!
Let's say I have an array arr. When
would the following not give the
number of elements of the array:
sizeof(arr) / sizeof(arr[0])?
One thing I've often seen new programmers doing this:
void f(Sample *arr)
{
int count = sizeof(arr)/sizeof(arr[0]); //what would be count? 10?
}
Sample arr[10];
f(arr);
So new programmers think the value of count will be 10. But that's wrong.
Even this is wrong:
void g(Sample arr[]) //even more deceptive form!
{
int count = sizeof(arr)/sizeof(arr[0]); //count would not be 10
}
It's all because once you pass an array to any of these functions, it becomes pointer type, and so sizeof(arr) would give the size of pointer, not array!
EDIT:
The following is an elegant way you can pass an array to a function, without letting it to decay into pointer type:
template<size_t N>
void h(Sample (&arr)[N])
{
size_t count = N; //N is 10, so would be count!
//you can even do this now:
//size_t count = sizeof(arr)/sizeof(arr[0]); it'll return 10!
}
Sample arr[10];
h(arr); //pass : same as before!
Arrays in C++ are very different from those in Java in that they are completely unmanaged. The compiler or run-time have no idea whatsoever what size the array is.
The information is only known at compile-time if the size is defined in the declaration:
char array[256];
In this case, sizeof(array) gives you the proper size.
If you use a pointer as an array however, the "array" will just be a pointer, and sizeof will not give you any information about the actual size of the array.
STL offers a lot of templates that allow you to have arrays, some of them with size information, some of them with variable sizes, and most of them with good accessors and bounds checking.
There are no cases where, given an array arr, that the value of sizeof(arr) / sizeof(arr[0]) is not the count of elements, by the definition of array and sizeof.
In fact, it's even directly mentioned (ยง5.3.3/2):
.... When applied to an array, the result is the total number of bytes in the array. This implies that the size of an array of n elements is n times the size of an element.
Emphasis mine. Divide by the size of an element, sizeof(arr[0]), to obtain n.
Since C++17 you can also use the standardized free function:
std::size(container) which will return the amount of elements in that container.
example:
std::vector<int> vec = { 1, 2, 3, 4, 8 };
std::cout << std::size(vec) << "\n\n"; // 5
int A[] = {40,10,20};
std::cout << std::size(A) << '\n'; // 3
No that would still produce the right value because you must define the array to be either all elements of a single type or pointers to a type. In either case the array size is known at compile time so sizeof(arr) / sizeof(arr[0]) always returns the element count.
Here is an example of how to use this correctly:
int nonDynamicArray[ 4 ];
#define nonDynamicArrayElementCount ( sizeof(nonDynamicArray) / sizeof(nonDynamicArray[ 0 ]) )
I'll go one further here to show when to use this properly. You won't use it very often. It is primarily useful when you want to define an array specifically so you can add elements to it without changing a lot of code later. It is a construct that is primarily useful for maintenance. The canonical example (when I think about it anyway ;-) is building a table of commands for some program that you intend to add more commands to later. In this example to maintain/improve your program all you need to do is add another command to the array and then add the command handler:
char *commands[] = { // <--- note intentional lack of explicit array size
"open",
"close",
"abort",
"crash"
};
#define kCommandsCount ( sizeof(commands) / sizeof(commands[ 0 ]) )
void processCommand( char *command ) {
int i;
for ( i = 0; i < kCommandsCount; ++i ) {
// if command == commands[ i ] do something (be sure to compare full string)
}
}
_countof(my_array) in MSVC
I can thing of only one case: the array contains elements that are of different derived types of the type of the array.
Elements of an array in C++ are objects, not pointers, so you cannot have derived type object as an element.
And like mentioned above, sizeof(my_array) (like _countof() as well) will work just in the scope of array definition.
It seems that if you know the type of elements in the array you can also use that to your advantage with sizeof.
int numList[] = { 0, 1, 2, 3, 4 };
cout << sizeof(numList) / sizeof(int);
// => 5
First off, you can circumvent that problem by using std::vector instead of an array. Second, if you put objects of a derived class into an array of a super class, you will experience slicing, but the good news is, your formula will work. Polymorphic collections in C++ are achieved using pointers. There are three major options here:
normal pointers
a collection of boost::shared_ptr
a Boost.Pointer Container
Let's say I have an array arr. When would the following not give the number of elements of the array: sizeof(arr) / sizeof(arr[0])?
In contexts where arr is not actually the array (but instead a pointer to the initial element). Other answers explain how this happens.
I can thing of only one case: the array contains elements that are of different derived types of the type of the array.
This cannot happen (for, fundamentally, the same reason that Java arrays don't play nicely with generics). The array is statically typed; it reserves "slots" of memory that are sized for a specific type (the base type).
Sorry for the trivial question, I am a Java dev and I am rather new to C++.
C++ arrays are not first-class objects. You can use boost::array to make them behave more like Java arrays, but keep in mind that you will still have value semantics rather than reference semantics, just like with everything else. (In particular, this means that you cannot really declare a variable of type analogous to Foo[] in Java, nor replace an array with another one of a different size; the array size is a part of the type.) Use .size() with this class where you would use .length in Java. (It also supplies iterators that provide the usual interface for C++ iterators.)
Use the Microsoft "_countof(array)" Macro. This link to the Microsoft Developer Network explains it and offers an example that demonstrates the difference between "sizeof(array)" and the "_countof(array)" macro.
Microsoft and the "_countof(array)" Macro
If you can not use C++17, which allows to use std::size(container), you can easily implement your own generic sizeofArray template function as a one-liner:
#include <cstddef>
#include <cstdio>
template< typename T, std::size_t N >
inline constexpr std::size_t sizeofArray( const T(&)[N] ) noexcept { return N; }
int x[10];
void* y[100];
long z[1000];
struct {int x; char y; long z;} s[123];
static_assert( sizeofArray(x) == 10, "error" );
static_assert( sizeofArray(y) == 100, "error" );
static_assert( sizeofArray(z) == 1000, "error" );
static_assert( sizeofArray(s) == 123, "error" );
int main() {
puts( "ok" );
}
test it here: http://cpp.sh/8tio3
It will work if and only if arr is a C-Array (type[size]; except for function parameters!), a reference to a C-Array (type(&)[size]) or a pointer to a C-Array (type(*)[size]).
Note you should use std::size or std::ssize instead with current C++-Standards!
In C++17 you can use std::size:
int arr[] = {1, 2, 3};
auto count = std::size(arr); // type std::size_t, value == 3
In C++20 you can additionally get a signed value by using std::ssize:
int arr[] = {1, 2, 3};
auto count = std::ssize(arr); // type std::ptrdiff_t, value == 3
https://en.cppreference.com/w/cpp/iterator/size
Also note that C++ unfortunately inherited from C that C-Arrays are never passed by value (deep copy) to functions.
void f(int a[3]);
is the same as
void f(int* a);
so you loose the information that a is an array and with this, how much elements it had. The 3 is completely ignored by the compiler!
If you want to preserve the datatype (including the array element count), you can use a pointer or a reference to an C-Array:
void f(int (&a)[3]); // reference to C-Array with 3 elements
void f(int (*a)[3]); // pointer to C-Array with 3 elements
void f(int a[3]); // pointer to int
void f(int* a); // pointer to int
If you want to call functions with Arrays call-by-value, you can use C++-Arrays (std::array) from the C++ standard library:
f(std::array<int, 3> a);
std::array<int, 3> arr = {1, 2, 3};
f(arr); // deep copy
https://en.cppreference.com/w/cpp/container/array
Determine how many numbers are in your array.
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n[10] ;
int l = sizeof(n)/sizeof(n[0]);
cout << l;
return 0;
}
I know is old topic but what about simple solution like while loop?
int function count(array[]) {
int i = 0;
while(array[i] != NULL) {
i++;
}
return i;
}
I know that is slower than sizeof() but this is another example of array count.
Can anyone hint on how to pass by reference an array of the kind
int array[2][3][4];
so that I may save his pointer in order to use and modify the array?
Like, if I were speaking about a single integer:
// Scope 1
int a = 3;
increment(&a);
// End Scope 1
// Scope 2
int *pa;
void increment(int *tpa) {
pa = tpa; *pa++;
}
// End Scope 2
Thanks a lot and best regards.
If you really want to pass the array by reference, you can do so:
void f(int (&a)[2][3][4]) { }
In C, which doesn't have references, you can pass the array by pointer (this works in C++ too, of course):
void f(int (*a)[2][3][4]) { }
C++:
void f(int (&array)[2][3][4])
{
}
C: There are no references in C
Note that no matter how you pass the array, via reference or not, the array is not going to be copied, so you'll get the original pointer. You can pass this array also like this:
void f(int array[][3][4])
{
}
Thanks to everyone who participated in this! sskuce provided a very good solution, taking advantage of a "container". I had thought about this but didn't really like the extra stuff.
I realized after a little jumbling that James McNellis had given the answer all along. So... here's the solution I prefer with no containers and no indexes arithmetic (mind the parenthesis):
void Scope1()
{
int array[2][3][4];
Scope2(&array);
}
int (*pArray)[2][3][4];
void Scope2(int (*tpArray)[2][3][4]))
{
pArray = tpArray;
(*pArray)[0][0][0] = 3;
}
Thanks again to everyone.
Edit: I'm keeping my original answer below, as I believe it's necessary for folks to understand how arrays are actually passed to functions and how they're layed out in memory, but on further reflection I think there is a simple and correct way to get what you want done.
Encapsulate the array within a struct, e.g.
typedef struct ArrayHolderStruct{
int array[2][3][4];
} ArrayHolder
//...
//scope 1
ArrayHolder thingy;
thingy.array[0] = something;
//other initialization.
F( &thingy );
//...
//scope 2
ArrayHolder *pa;
void F ( ArrayHolder *p ){
pa = p;
p->array[0][1][2] = 42;
}
//Call F first to set pa.
void G(){
pa->array[0][1][2] = 6 * 9; // if pa = &thingy, thingy will be modified.
}
The struct will allow you to maintain layout information about the encapsulated array, and you don't have to worry about nasty index arithmetic.
-----OLD ANSWER-----
Passing a reference to an array is not useful, unless you want to change the size or layout of the array (which you can't do with statically sized arrays anyway). You'll get a reference (or pointer) to the elements of the array even if you pass the array by "value". That is to say, if you declare your function:
void f ( int a[2][3][4] ){
a[0][1][2] = 42;
}
and call it like f( array ) when f exits, array[0][2][2] will have been set to 42, even though you didn't pass a "reference" to array into the function.
If you want to save a pointer to the array for later use in a library function, etc, you could do something like:
//scope 2
int * pa;
void f ( int a[2][3][4] ){
pa = &a[0][0][0];
}
It gets tricky at this point - you have to know how pa is layed (laid?) out in memory. I think C has standardized on 'row major order', so the array should be layed out in memory like:
a[0][0][0] a[0][0][1] a[0][0][2] a[0][0][3] a[0][1][0] ... a[0][2][3] a[1][0][0] a[1][0][1]... a[1][2][3]
So, to get at an element at index [n][j][k], you have to do something like:
pa[n * 12 + j * 4 + k] = something;
Basically, multiply each index by the number of elements that can be referenced by an index of that order, e.g. each k index points to exactly one element given a fixed j and n index, each j index can point to 4 elements given a fixed n index, and each n index can point to one of 12 (because 12 = 3 * 4) elements.
Like I said, it's tricky. See the wikipedia articles on Array Data Structures and Row-major order to get a better understanding of how these things are layed out.