reading this question: What is the difference between int* x[n][m] and int (*x) [n][m]?
the correct reply stated this:
int arr[2][3];
int (*p1)[2][3] = &arr; // arr decays to int(*)[3], so we need to take the address
int (*p2)[2][3] = new int[42][2][3]; // allocate 42 arrays dynamically
the comment in that last line says it allocates 42 new arrays.
how does that work? can someone explain that to me?
thanks!
i would reply on that question, but i can't, so new question it is (:
new int[3] allocates 3 integers. Now new int[2][3] allocates 2 arrays, each of length 3 integers. Extending this further, new int[42][2][2] allocates 42 arrays, each of length 2 arrays, each of which in turn is, of length 2 integers. Declarations are really a recursive idea.
Allright, class time.
C++ Multidementional arrays vs. Jagged array
A multidemenional array (in C++) is a block of contiguous memory. It allocates the full number of elements, all in a line, and then access them by combining the index accessors. In essense. If I have an array defined by int x[2][3], it essentially turns x[i][j] into (&x[0][0]+i*3)[j].
sizoef(int[2][3])==24 (4bytes/int*2*3)
This type of array is often called a "static" array, because the size of the array must be allocated at compile time.
Note that the first size is irrelevent to this lookup. This makes it so that, when REFERENCING this type of array, we can exclude the smallest size from the type. This makes is so that both the functions below are valid and can work on the array x declared above:
int foo(int y[2][3]){ return y[1][1];}
int bar(int y[][3]){ return y[1][1];}
Note in this context, sizeof(y)==sizeof(void*), but that is a different problem all together.
The typeing convention for static arrays behaves differently than your used to. Part of the type information comes AFTER the variable declaration. This actually persists in typedefs as well:
typedef int a[4][20];
typedef int b[][20];
If you wanted to take the address of such a value type, then what you need to declare the pointer to this array type. That can be done with:
int (*xptr)[2][3] = &x;
The int (*var)[...] says that var is a pointer to a int[2][3].
When people say C++ arrays are pointers, they are NOT refering to this type of array: the array is a little more compilicated than a pointer here, though we could flatten it into a 1D array.
A jagged array (or dynamic array) is a single block of contiguose memory that is allocated in a linear fashion. This type of array is often called dynamic because the size does NOT need to be known at compile time.
Dynamic arrays are allocated with new or malloc (though you should only use new in C++). These types of array are strictly pointers. When I say int* a=new int[4], I allocate 4 integers: thats it.
We achieve mutlidementionality here by crating jagged arrays, which are arrays of pointers. So for example:
int** a = new int*[2];
for (int i = 0; i < 2; i++) { a[i]=new int[3];}
What your code does
int arr[2][3];//
int (*p1)[2][3] = &arr; // arr decays to int(*)[3], so we need to take the address
int (*p2)[2][3] = new int[42][2][3]; // allocate a dynamic array (42 elements long) of int[2][3] (sizeof(int[2][3])==24, so this allocates 42*24 bytes!)
It essentially allocates 43 int[2][3] in a row. So it actually comes up with all contiguous memory (though why it needs to be dynamic and not static is beyond me).
Personally, my rule is multidementional arrays "confusing as hell" and only use them in a local context now, but to each their own.
Related
I have a variable definition int (**ff)[4];, which is very bad looking. If I'm right (inferred from the fact that int (*f)[4]; is a pointer to an array of 4 int-s) this is a pointer to a pointer to an array of 4 int-s.
Now I have tried to initialize this thing but I had no success. Initializing f was simple (f = new int[5][4];), but the line ff = new int*[6][4]; is not working. Microsoft Visual Studio Community 2013 says in an error message that
a value of type "int *(*)[4]" cannot be assigned to an entity of type "int (**)[4]"
I have a very bad feeling that I really misunderstood something.
EDIT:
Sorry, that I didn't said it explicitly: What I wanted, is to allocate memory space for not only one, but for more pointers which later can point to an array of an array of 4 int-s. Of course I wanted this in one line without the help of any other definition, conversion etc.
I can (not) see this used only in this way:
int (*a)[4] = new int[6][4];
int (**ff)[4] = &a;
int (**ff)[4] means "a pointer to pointer to an array of four ints". This means we have to have int (*)[4] first - an lvalue, so we can take its address with operator&.
On the other hand, new int*[6][4]; allocates an array of pointers to an array of four pointers (the "outer" array decays). This is completely different type from the first one.
I had to help myself with cdecl.org on this one.
Edit:
I've just made this:
using PTR = int (*)[4];
int (**ff)[4] = new PTR; // hooray, a dynamically allocated pointer!
but can't figure a way without the type alias...
Actually, there is one: int (**ff)[4] = new (int (*)[4]), gathered from this Q&A. Don't forget that all you have now is a dynamically allocated uninitialized pointer.
A double pointer is a pointer to a pointer to get the obvious out of the way. That means that it will hold the memory address of a pointer, again obvious. That means you initialise your pointer and only then point your double pointer to it. Such as,
int* f[] = new int[4];
int** ff = &f;
Then to access this you can do,
(*ff)[1]
This will allow you to access the information stored in the dynamic array.
NOTE: leave the [] beside f empty if the size is not known at compile time which is the reason you should be doing it this way in the first place.
If you want to access elements of an array a[3][3] by pointer to pointer to array than you can do that by below code :
int a[3][3]={{1,2,3},{4,5,6},{7,8,9}};
int (*p)[3]=a;
int (**ff)[3]=&p;
To print it :
int i,j;
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
{
printf("%d ",ff[0][i][j]);
}
printf("\n");
}
Alan,
I think the problem here is your initial assumption is incorrect.
*f[4] (or (*f)[4])
would actually be an array of 4 pointers to int, not a pointer to an
array of 4 ints. Then,
**ff[4]
would be a pointer to an array of 4 pointers to
int. I would recommend backing up a little, with this information in hand, and
trying it again. Also, an assignment to the
**ff
would be a single pointer, and
probably not an array, depending on how you are intending to use it.
Blake Mitchell
retired free lance programmer
I have a class that has a large 2 dimensional array in it. It used to be a dynamic array allocated on the heap and now it is statically sized which I prefer.
private:
int fontTextureCoords_[100][7];
I had to add the type casting to the the accessor in order to return the array for access outside the class which is currently working okay, but I'm not sure it is safe or the correct way to handle this.
public:
inline int **getFontTextureCoords()
{
return (int**)fontTextureCoords_;
}
Is this safe / the correct way to do this or is there a more preferred method for returning a pointer to a multi-dimensional array?
That's not the correct way to do that and shouldn't compile. A 2d array is not convertible to a pointer to pointer. You'd have to return a pointer to an array, which is easiest to write using a typedef:
using T = int[7];
inline T* getFontTextureCoords() { return fontTextureCoords_; }
Although it'd be much better to just return a reference the full array:
using T = int[100][7];
inline T& getFontTextureCoords() { return fontTextureCoords_; }
You could also just std::array<std::array<int, 7>, 100>.
Maybe this diagram shows you the difference between the two types of multi-dimensional array declarations. (Sometime people don't understand this.)
The first one says a is a single block of 100 consecutive 7-int chunks, or 700 ints total, all together in one piece.
The second says a is an array of pointers, where each pointer points to a different chunk of ints, scattered all over memory.
The compiler needs to know this, because if you write a[i][j] it has to generate totally different code.
Casting an array such as int fontTextureCoords_[100][7]; to an int** is not right. It leads to undefined behavior.
If it is not too much, change getFontTextureCoords to:
inline int (*getFontTextureCoords()) [7]
{
return fontTextureCoords_;
}
and use it as:
int (*ptr)[7] = getFontTextureCoords();
If you have the option of using std::vector or std::array, it will be better to use them.
There are no multi-dimensional arrays in C/C++. There are only single dimenstional arrays. You can have a single-dimensional array, with every element of it being another single dimensional array. While there seem to be no difference, it is there and is very important.
This is exactly way transitive logic doesn not work. Everybody has gone through it. 'If single-dimensional arrays are passed as a pointer to the first elelement, 2-D arrays should be passed as a pointer to pointer to first element, etc'. But since it is not a two-dimensional array, but array of arrays, the logic can not be applied.
You can reason about it in the following way. Let's say, you have an array of types X.
X x[10];
How do you access element number 5? Easy -
x[5] = 42;
But what compiler does when it sees it? It does approximately this:
*(&x[0] + 5) = 42;
So it takes the address of the first element, and adds 5 to it to get to the address of your 5th element. But what adding 5 means? In bytes, how many bytes should be skipped from address of beginning of the array to arrive at requested memory location? Of course, 5 * sizeof(X). Now, if you have '2-D' array, declared like this:
X x[2][3];
And you try to work with it through the pointer to pointer:
px = (X**)x;
px[3][4] = 42;
Remember, to genereate the code for [][], compiler needs to express in the way of *(px + ). And something has to be the size of the array (as elements of your array are arrays). But you need to know array size for this, and as you can see, your px does not have any array size encoded in it. The only size it know is size of X, which is not enough.
Hope it makes sense, and explains why you can't use int** instead of x[][].
I have a few array-related questions. I've studied that array-size must be constant on declaration/compiler must know its value. But using the GNU GCC compiler (C++11 standard filter) and I am able to perfectly compile and run a program using a variable as array size, when declaring said array dynamically (using new)
int num;
cout << "How big an array? ";
cin >> num;
int *arr = new int [num];
Ques1) Is this considered standard? My profs are contradictory.
Ques2) If it is standard, in that case, is it possible to extend the size of the array (or any array) after creation?
Ques3) Again, if this expression is standard, then is it possible to use it within a function - eg. using a function to create such an array? (if so, how?)
(PS: Hi, I'm new here and also still a novice in C++)
Ques1) Is this considered standard? My profs are contradictory.
Yes, this is completely valid. Note that you need to explicitly delete the memory pointed by arr using operator delete[]. It is much better to use a std::vector<int> which will perform the memory management for you.
You might be mistaken with variable length arrays(VLAs), which are not allowed in C++:
// same num as the one in your example
int arr[num]; // ERROR
This is valid in C but invalid in C++(C++14 will include VLAs, although they will have some differences with C VLAs).
Ques2) If it is standard, in that case, is it possible to extend the
size of the array (or any array) after creation?
No, you can't extend it. You can allocate a bigger array, copy the elements and delete the previous one. Again, this is done automatically if you're using std::vector<int>.
Ques3) Again, if this expression is standard, then is it possible to
use it within a function - eg. using a function to create such an
array? (if so, how?)
Yes, of course:
int *allocate(size_t size) {
return new int[size];
}
But again use std::vector:
int num;
cout << "How big an array? ";
cin >> num;
std::vector<int> arr(num); // num elements, all of them initialized to 0
// insert 42 at the end. reallocations(if any) are done automatically
arr.push_back(42);
I've studied that array-size must be constant on declaration/compiler must know its value.
Well, that's true, but only for static or automatic arrays. You are allocating a dynamic array on the heap, which is different.
Static array
An array declared at global scope must have a constant size.
int arr[5];
Automatic array
An array allocated automatically within a function must have constant size (with exception, see below).
void f() {
int arr[5];
}
Dynamic array
A dynamic array allocated on the heap with new can have any size, constant or variable.
new int[5];
new int[n * 4];
GCC extension
The exception is that GCC allows one to use a variable to declare the size of an automatic array:
void f(int n) {
int arr[n];
}
However, this usage is not standard.
Question 1 - operator 'new' is used to make dynamic allocation, I mean, when you don't know previously what is the size of the array, so, there is no problem, you can do it! I think your profs are confusing with C sintax, where new neither exists and is not allowed to make things like: int p[n]; for instance.
Question 2 - No, it is not possible increase the size of an array created using operator new. You have to alocate another array and copy the data. You can consider use vector in order to do it easily.
Question 3 - I don't see why to do it, but it is possible..
int* createarray(int size)
{
return new int[size];
}
int main()
{
int *p = createarray(10);
}
Q1:Is this considered standard?
Given the definition int n = 42, new float[n][5]
is well-formed (because n is the expression of a
noptr-new-declarator), but new float[5][n] is ill-formed (because n is
not a constant expression).
--5.3.4.6,N3242
If the allocated type is an array type, the allocation function’s
name is operator new[] and the deallocation function’s name is
operator delete[].
--5.3.4.8,N3242
new T[5] results in a call of operator new[](sizeof(T)*5+x)
Here, x and y are non-negative unspecified values representing array allocation overhead;
--5.3.4.12,N3242
Q2:If it is standard, in that case, is it possible to extend the size of the array (or any array) after creation?
Partially no, or not recommended.
when the allocation function returns a value other than null, it must be a pointer to a block of storage
in which space for the object has been reserved. Mostly allocation happened in heap, and there may not have more contiguous memory left which is important for array.
If you have to do this and have a memory poll, use placement new operator you can do this partially, but what you do now is what the designer of allocator do, and have risk ruin the inner memory storage.
Q3: using a function to create such an array? (if so, how?)
Entities created by a new-expression have dynamic storage duration
(3.7.4). [Note: the lifetime of such an entity is not necessarily
restricted to the scope in which it is created. — end note ]
--5.3.4.1,N3242
The rest of things are how to design such function to meet your need, even use template.
1 template<typename T>T* foo(std::size_t size){
2 return new T[size] ;
3 }
As a complement to other answers :
Vectors
(Agreed with vector for dynamic resize) :
std::vector<int> v;
v.push_back(1);
v.push_back(1);
v.resize(v.size()+10, 5); // Greater resized
v.resize(v.size()-1); // Lower resized
If the new size is greater than the old one, additional elements will be initialized to 5 (or 0 in case of int, if the second parameter is not used) and ancient elements remain unchanged.
If the new size is lower than the old one, it is truncated.
Arrays
Side note : (about stack and heap allocation)
The way an array is handled can lead to significantly different results (See this very interesting discussion):
// Create 500 bytes on the heap
char *pBuffer = new char[500]; // or malloc in C
// Create 500 bytes on the stack
char buffer[500];
When a multidimensional array is passed to a function, why does C++ require all but the first dimension to be specified in parameter li
A better way to ask this is to ask why C++ doesn't require the first dimension to be specified.
The reason is that for all arrays, you can't pass arrays by value to a function. If you try to declare a function taking an array the compiler will adjust the declaration to the corresponding pointer type.
This means that it doesn't matter what dimension you specify as the dimension doesn't form part of the function signature.
For example, these all declare exactly the same function.
void f(int *p);
void f(int p[]);
void f(int p[10]);
void f(int p[100]);
When navigating the array pointed to by p in the function, the only information that the copmiler needs is the size of the array elements, i.e. sizeof(int) in this case.
For more complex arrays exactly the same holds. These are all the same:
void g(Type p[][10][20]);
void g(Type (*p)[10][20]);
void g(Type p[10][10][20]);
void g(Type p[99][10][20]);
But these are all different from:
void g(Type p[][5][20]);
because adjusting the dimension of anything other than the outer array dimension affects the size of (at least) the outer array's elements meaning that the pointer arithmetic for navigating the array would have to change.
For example int a[n][m] is an array whose type is an int array of length m. In other words, the length of array is part of its type. And as for all function parameters, compiler need to know its type.
There is no such thing as multidimensional array in c++. It is just a syntax which looks like it. In int a[4] and int b[5] a and b are different types.
If you refer to static allocation, this is simple.
Because the blocks of memory are contiguous which means that the memory cells are one after each other and the compiler knows where the next cell is in memory.
For unidimensional array in memory looks like this:
http://cplusplus.com/doc/tutorial/arrays/arrays3.gif
For bidimensional array in memory looks like this:
http://i.msdn.microsoft.com/dynimg/IC506192.png
In short: The compiler doesn't need the dimension, because a array decays to a pointer. But any additional dimension is needed by the compiler, to calculate the correct location in memory.
At first you need to know that an array in C/C++ is a linear continuous object in memory. Which is very efficient.
Because an array in C/C++ is a linear continuous object in memory, an array will decay to a pointer. Copying the complete array will be a waste of time and memory and is not requiered. A pointer is anything needed to go through the array. To go through the array you can take the increment-operator or any calculation which evaluates to a valid address in the array. You can set a delimiter in the array itself i.e. '\0' in a String, or pass the length to the function seperatley to tell your code where the end of the array is.
With multi-dimensional arrays the thing is a little bit more complicated. A multi-dimesional array is still only a linear continuous object in the memory! But the compiler needs the information about the additional dimensions to calculate to correct position in memory, imagine the following:
char array[10][10]; // 0 - 99
correct:
// formal argument tells your compiler, that each column is 10 elements long
funca(int array[10][10]) {
// access to element 25 (2 * 10 + 4, the 10 is known through your formal argument, remember that an array is null based)
array[2][3] = 'x';
}
wrong:
// formal argument tells your compiler, that ech colum is 5 elements long
funcb(int array[10][5]) {
// access to element 15 (2 * 5 * + 4, the 5 is known through your formal argument, remember that an array is null based)
array[2][3] = 'x';
}
A note (or warning):
Arrays in Java, especiallay (irregular) multi-dimensional arrays are completely different.
came across the code shown below in a small C++ example:
int (*arr1)[ARRAY_SIZE];
int (*arr2)[ARRAY_SIZE];
int (*arr3)[ARRAY_SIZE];
then in the constructor of the class:
ParallelMultiply::ParallelMultiply(int mat1[ARRAY_SIZE][ARRAY_SIZE],
int mat2[ARRAY_SIZE][ARRAY_SIZE],
int result_mat[ARRAY_SIZE][ARRAY_SIZE]):arr1(mat1),
arr2(mat2),
arr3(result_mat)
{
}
here, ParallelMultiply is the class, mat1, mat2, result_mat are 2-D arrays, and ARRAY_SIZE is the defined array length. But How can arr1, arr2 and arr3 can be initialized with two dimensional arrays?? Please explain.
Thank you!!
You may be familiar with the way an array can decay to a pointer and then that pointer can be used like an array (as long as its actual extent is known).
When this sort of thing is done to a multidimensional array, you get a pointer to array with one less array bound. Then that pointer can be used like the multidimensional array.
So arr1[i][j] and mat1[i][j] are the same int and have the same address.
Note that since the class is copying only pointers to the 2D arrays, the user needs to make sure the lifetime of those array arguments is long enough. And any modifications made through the class will happen to the original arrays.
arr1, arr2 and arr3 are pointers. Each pointer pointing to an array of size ARRAY_SIZE.