For a 4-D array, I'm trying to average the values using compact pointer notation. Using examples from my text, it says I can use something like this:
void DisplayAverage(double (*set)[DIM1][DIM2][DIM3])
double *ptr;
double subTotal2 = 0;
for (ptr = (double *)set; ptr < (double *)set + DIM0 * DIM1 * DIM2 * DIM3; ptr++) {
subTotal2 += *ptr;
subTotal2 /= (DIM0 * DIM1 * DIM2 * DIM3);
cout << "Using compact pointer operations, total: " << subTotal2 << "\n";
}
}
That code works. However, if I try to use another notation from the text:
for (ptr = (double *)set; ptr < (double *)(&set + 1); ptr++) {
to access the array, I get no output. Any thoughts? Thanks.
You have one address-of too much:
// notice: "set" instead of "&set"
for (ptr = (double *)set; ptr < (double *)(set + DIM0); ptr++) {
You were adding one to the address of your parameter (and thus were pointing to nowhereland), instead of DIM0 to the value of your parameter (which will bring you to after the array data, which is your goal).
Notice that the parameter is a pointer to an array of dimensions [DIM1][DIM2][DIM3]. In other words, the argument you pass to the function can be an array of type double[DIM0][DIM1][DIM2][DIM3], which will decay to the pointer type of that parameter. You have DIM0 rows, so you add DIM0 to that pointer to reach the position after the last cell.
What you were probably having in mind was adding one to the pointer to the whole array. This will work if you have the following declaration, instead.
void DisplayAverage(double (*set)[DIM0][DIM1][DIM2][DIM3]);
You now need to pass the argument using &arg instead of just arg, to actually pass the address of the array, instead of letting it decay to its inner dimension type. The loop can then be written as
for (ptr = (double *)set; ptr < (double *)(set + 1); ptr++) {
Your expression (&set + 1) would point to one-past the array if set was an array, but it's not. The variable set is a pointer in disguise (not an array), as are all arrays that look like they were passed by value.
Better example of this:
void g(int a[3]);
// exactly the same as:
void g(int* a);
// note the 3 is ignored by the compiler here! it serves as documentation when
// reading the code, but nothing else, and for this reason you should pretty much
// always drop it, preferring:
void g(int a[]); /*or*/ void g(int* a); // (which are also identical)
void f() {
int a[3] = {0, 1, 2};
int* end = (int*)(&a + 1); // &a has type int (*)[3] (that's pointer to array
// of 3 ints so &a + 1 has the same value as &a[2] + 1 ("one past" the last
// valid item in a)
int* p = a; // this is how a is passed "by value" to a function such as g
end = (int*)(&p + 1); // even though this "looks" the same, &p has type int**
// and adding 1 to that has no correlation with what p points to.
// to make matters worse, the cast (and C++-style casts have the same problem
// here) hides this type error and makes the compiler believe we know what
// we're doing
// pointers are not arrays, even though they mostly behave similarly:
std::cout << sizeof(a) << ", " << sizeof(p) << ", " << sizeof(void*) << '\n';
// compare the int* size to void* size
}
And example applying it to pointers-to-arrays:
typedef int T[3];
void g(T a[3]);
// same as:
void g(T a[]); /*and*/ void g(T* a);
// that T happens to be an array type doesn't change anything, these are
// also declaring the same function:
void g(int a[][3]); /*and*/ void g(int (*a)[3]);
void f() {
int a[3][3] = {};
int* end = (int*)(&a + 1);
// note that end - &a[0][0] is 9
int* p = &a[0][0];
std::cout << sizeof(a) << ", " << sizeof(p) << ", " << sizeof(void*) << '\n';
}
I would recommend against using static multidimensional arrays in C++. See my response in How to initialize 3D array in C++
Related
So i have a function and a MD array
int arrayMD[2][2] = { 0,3,6,8 };
void display(int *ptr) {
cout << *(ptr + 1);
}
display(*arrayMD, 2, 2); // Invoke
When I pass the it to the function it will point to a 1-D Array
so the *ptr would point to {0,3} (CMIIW)
When I call a function / invoke
I pass a dereferenced pointer which is : An Address
cout << *arrayMD == Address to the first one which is {0,3}
so the function should be *ptr = Address from dereferenced Argument passed
So when i try to double bracket on the array: ptr[0][1] it doesn't work because it has become 1 dimensional array which consist of {0,3} but when i type ptr[3] it printed out 8. I know how it stored in the memory but is there no limit? so i could just use one dimensional array and print out the value. Then what's the point of 2D array?
I have just started learning C++ so please CMIIW
Let's put it this way first:
void display(int (*ptr)[2]) { // Takes a pointer to an array of 2 ints
std::cout << **(ptr); // Display [0][0]
std::cout << *(*(ptr)+1); // Display [0][1]
std::cout << **(ptr+1); // Display [1][0]
std::cout << *(*(ptr+1)+1); // Display [1][1]
}
int main() {
int arrayMD[2][2] = { 0,3,6,8 };
display(arrayMD); // Here, we pass the pointer to the first element of arrayMD
}
This gives us the following output:
0368
As you know, when we use the name of an array, it is automatically converts to the pointer to the first element. But when we want a pointer to an array, we need involvement of parenthesis:
int *p[100]; // is an array of 100 pointers to int
int (*p)[100]; // is a pointer to an array of 100 int
And yes, in general, consider using Standard Library, in particular the std::array.
I know that the following is not correct:
int arr[2][3] = {}; //some array initialization here
int** ptr;
ptr = arr;
But I am quite surprised that the following lines actually work
int arr[2][3] = {}; //some array initialization here
auto ptr = arr;
int another_arr[2][3] = {}; //some array initialization here
ptr = another_arr;
Can anyone possibly explain what is the type assigned to ptr in the second block of code, and what happened underneath?
Well, arrays decay to pointers when used practically everywhere. So naturally there's decay going on in your code snippet too.
But it's only the "outer-most" array dimension that decays to a pointer. Since arrays are row-major, you end up with int (*)[3] as the pointer type, which is a pointer to a one-dimensional array, not a two dimensional array. It points to the first "row".
If you want ptr's deduction to be a pointer to the array instead, then use the address-of operator:
auto ptr = &arr;
Now ptr is int(*)[2][3].
In
auto ptr = arr;
arr decays into a pointer to its first element in the normal way; it's equivalent to
auto ptr = &arr[0];
Since arr[0] is an array of three ints, that makes ptr a int (*)[3] - a pointer to int[3].
another_arr decays in exactly the same way, so in
ptr = another_arr;
both sides of the assignment have the type int (*)[3], and you can assign a T* to a T* for any type T.
A pointer to arr itself has type int(*)[2][3].
If you want a pointer to the array rather than a pointer to the array's first element, you need to use &:
auto ptr = &arr;
First, let's look at why you can't assign int arr[2][3] to int **. To make it easier to visualise, we'll initialise your array with a sequence, and consider what it looks like in memory:
int arr[2][3] = {{1,2,3},{4,5,6}};
In memory, the array data is stored as a single block, just like a regular, 1D array:
arr: [ 1, 2, 3, 4, 5, 6 ]
The variable arr contains the address of the start of this block, and from its type (int[2][3]) the compiler knows to interpret an index like arr[1][0] as meaning "take the value that is at position (1*2 + 0) in the array".
However for a pointer-to-pointer (int**), it is expected that the pointer-to-pointer contains either a single memory address or an array of memory addresses, and this/these adress(es) point to (an)other single int value or array of ints. Let's say we copied the array arr into int **ptrptr. In memory, it would look like this:
ptrptr: [0x203F0B20, 0x203F17D4]
0x203F0B20: [ 1, 2, 3 ]
0x203F17D4: [ 4, 5, 6 ]
So in addition to the actual int data, an extra pointer must be stored for each row of the array. Rather than converting the two indexes into a single array lookup, access must be performed by making a first array lookup ("take the second value in ptrptr to get an int*"), then nother array lookup ("take the first value in the array at the address held by the previously obtained int*").
Here's a program that illustrates this:
#include <iostream>
int main()
{
int arr[2][3] = {{1,2,3},{4,5,6}};
std::cout << "Memory addresses for int arr[2][3]:" << std::endl;
for (int i=0; i<2; i++)
{
for (int j=0; j<3; j++)
{
std::cout << reinterpret_cast<void*>(&arr[i][j]) << ": " << arr[i][j] << std::endl;
}
}
std::cout << std::endl << "Memory addresses for int **ptrptr:" << std::endl;
int **ptrptr = new int*[2];
for (int i=0; i<2; i++)
{
ptrptr[i] = new int[3];
for (int j=0; j<3; j++)
{
ptrptr[i][j] = arr[i][j];
std::cout << reinterpret_cast<void*>(&ptrptr[i][j]) << ": " << ptrptr[i][j] << std::endl;
}
}
// Cleanup
for (int i=0; i<2; i++)
{
delete[] ptrptr[i];
ptrptr[i] = nullptr;
}
delete[] ptrptr;
ptrptr = nullptr;
return 0;
}
Output:
Memory addresses for int arr[2][3]:
0x7ecd3ccc0260: 1
0x7ecd3ccc0264: 2
0x7ecd3ccc0268: 3
0x7ecd3ccc026c: 4
0x7ecd3ccc0270: 5
0x7ecd3ccc0274: 6
Memory addresses for int **ptrptr:
0x38a1a70: 1
0x38a1a74: 2
0x38a1a78: 3
0x38a1a90: 4
0x38a1a94: 5
0x38a1a98: 6
Notice how the memory addresses always increase by 4 bytes for arr, but for ptrptr there is a jump of 24 bytes between values 3 and 4.
A simple assignment can't create the pointer-to-pointer structure needed for type int **, which is why the loops were necessary in the above program. The best it can do is to decay the int[2][3] type into a pointer to a row of that array, i.e. int (*)[3]. That's what your auto ptr = arr; ends up as.
What is the type of [...]
Did you already try to ask the compiler to tell you the type of an expression?
int main()
{
int arr[2][3] = {{0,1,2}, {3,4,5}}; // <-- direct complete initialized here
auto ptr = arr; // <-- address assignment only
cout << "arr: " << typeid(arr).name() << endl;
cout << "ptr: " << typeid(ptr).name() << endl;
return 0;
}
I've to confess that the output
arr: A2_A3_i
ptr: PA3_i
seems to be not very readable at first glance (compared to some other languages), but when in doubt it may help. It's very compact, but one may get used to it soon. The encoding is compiler-dependent, in case you are using gcc, you may read Chapter 29. Demangling to understand how.
Edit:
some experimentation with some simple_cpp_name function like this rudimentary hack
#include <typeinfo>
#include <cxxabi.h>
#include <stdlib.h>
#include <string>
std::string simple_cpp_name(const std::type_info& ti)
{
/// simplified code extracted from "Chapter 29. Demangling"
/// https://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html
char* realname = abi::__cxa_demangle(ti.name(), 0, 0, 0);
std::string name = realname;
free(realname);
return name;
}
will show you that auto &rfa = arr; makes rfa having the same type as arr which is int [2][3].
I want to say first - I'm as interested in what's happening "behind the scenes" to cause this problem as I am in a blind fix for the code. Perhaps the C++ standard or something I'm unfamiliar with dictates what's causing it :-)
Anyways...
I am trying to pass 3 arrays, A, B, C into a function, which will merge A and B, storing the result in C.
Function declaration: void merge(int* a, int* b, int* c)
Passed from main() by:
int A[] = {1,2,3,4};
int B[] = {6,7,8,9};
int* C; //this could be wrong, btw!
merge(A, B, C);
Issue 1.
What's strange is that in main(), if I print the result of sizeof(A)/sizeof(A[0]), it gives me the proper result for the "length" of the array - 4 - and the same thing for B. But when I pass the arrays to the function, I try to calculate the size again, by the same method, but I get the result 2 for both arrays. The first lines of merge():
void merge(int* a, int* b, int* c)
{
int sizeA = sizeof(a)/sizeof(a[0]);
int sizeB = sizeof(b)/sizeof(b[0]);
int totalsize = sizeA + sizeB;
std::cout << "size A = " << sizeA << std::endl; //prints 2
std::cout << "size B = " << sizeB << std::endl; //prints 2
std::cout << "total = " << totalsize << std::endl;
...
Issue 2.
Just for fun, I tried iterating through a and b passed to merge() (within the merge function):
for (int i = 0; i < 4; ++i)
std::cout << a[i]; //prints "1234" as expected
All cool. But when I increase the index limit to 8...
for (int i = 0; i < 8; ++i)
std::cout << a[i]; //prints "12346789" - A and B concatenated!
Raising the max index a couple more times because why not:
for (int i = 0; i < 10; ++i)
std::cout << a[i]; //prints "1234678900"
...
for (int i = 0; i < 11; ++i)
std::cout << a[i]; //prints "1234678900-444896443"
Undefined behavior from out of bounds indexing and accessing other memory, I guess.
Printing b in the same manner does similar:
looping to i = 4 prints the array - 6789
to 6 adds two zeroes - 678900
to 8 adds the other stuff - 678900-126926969126613
Printing C, of course, results in nothing.
Are these oddities the result of
the fact I'm using C++ Shell (with options -std=c++14 -Wpedantic -O2) ?
incorrect passing of arrays to merge() ?
incorrect initializations in main() ?
the arrays needing to have a terminator, like char arrays?
all of the above?
In the first case, you have an array, which is not the same as a pointer. So the sizeof is correctly computed. Whereas int* is a pointer, and an array passed to a function always decays to a pointer (except when passing by reference). Also sizeof(int*)/sizeof(int) is the size of the pointer on your machine divided by the size of the int, so if your system is a 64 bit (8 bytes) and the int has a typical size of 4, you get 8/4=2.
Typical undefined behaviour by indexing out of bounds.
Advice: use std::vector<int> instead and you'll avoid allocating memory for C and using sizeof. You can simply use the std::vector::size() member function to obtain the size of the vector.
When you are passing your array to void merge(int* a, int* b, int* c)
The a and b here are no longer arrays, but are pointers. So when you are calculating it's size using
int sizeA = sizeof(a)/sizeof(a[0]);here sizeof(a)will give you the size of pointer, and sizeof(a[0]is giving you the size of int. Hence the result.
And for your second issue, when you are increasing the indexes, and getting both arrays connected, it is just because both arrays have been allocated continuous memory block, it is not necessary though that they will always be assigned continuous memory blocks, and other outputs are because of Undefined behavior only.
you need to remember that sizeof is a keyword that expands in compile time to the relevant size. sizeof(T) will expand to the byte size of (T).
inside the main function, sizeof(a) will give you the number of bytes of int[4], inside different function, the array decays into a pointer. sizeof(T*) is not sizeof(T[4])! you are calculating the size of the pointer (after the decay) and not the size of the real array. now let say my object weight 1 MB, does sizeof(OBJ) == sizeof(OBJ*)? of course not.
you are having Undefined beaviour. in this specific example, A and B sit right after the another on the stack. so in this super specific case when iterating over the stack you actually print both of the arrays because they sit one after the another. but again , this is Undefined beaviour. different compilers can padd the area between the array, or other OS may even kill your program.
You cannot compute a size of an array with a simple pointer passed to a function.
sizeof(integers) / sizeof(integers[0])
will be simply replaced by
sizeof(pointer on integer) / sizeof(integer).
But you can compute it earlier and then pass the size to a function like below.
#include <iostream>
using namespace std;
void doSomeWork(int* integers, const size_t size)
{
for (size_t i = 0; i < size; ++i)
{
std::cout << integers[i] << std::endl;
}
}
int main()
{
int integers[] { 0, 1, 2, 3 };
const size_t size = sizeof(integers) / sizeof(integers[0]);
doSomeWork(integers, size);
return 0;
}
As said below, std::vector<int> is much better than playing with C arrays.
There are already plenty of good answers here. In short, C++ arrays are of fixed size. If you want to change it, you have to use pointers and dynamic allocation. That can be tedious. the reason for most people here to advise you to use vectors instead.
Vectors are build for being of dynamic and adjustable size. You can use them almost like arrays. Here your code adapted:
void merge(vector<int> &a, vector<int>& b, vector<int> &c) // pass by reference
{
int sizeA = a.size();
int sizeB = b.size();
int totalsize = sizeA + sizeB;
c.resize(totalsize); // easy !
// ...
}
int main() {
vector<int> A{1,2,3,4};
vector<int> B {6,7,8,9};
vector<int> C;
cout <<A[2]<<endl; // access elements exacly like arrays
merge(A, B, C);
}
I having some issue when it comes to initializing pointers.
void findMM (int *PMM, int *theG)
{
// code I haven't written yet. It will essentially take two variables from //theG and store it in MM
}
int main()
{
int size;
int MM [2] = {1000, 0};
int *theG = NULL;
cout << "\nPlease insert size of array:" << endl;
cin >> size;
theG = new int [size];
findMM(&MM, &theG); //Get error with &MM
delete [] theG;
return 0;
}
The complier says that argument of type int (*)[2] is incompatible with parameter of type int ** So obviously that I have issue with the code in particular my (reference?) of array MM. Or perhaps there is other obvious faults that I am missing?
Edit attempt 2
void findMM (int *PMM, int *theG)
{
PMM [1] = 5;
theG [0] = 7;
}
int main()
{
int size;
int MM [2] = {1000, 0};
int *theG = NULL;
cout << "\nPlease insert size of array:" << endl;
cin >> size;
theG = new int [size];
findMM(MM, theG);
cout << MM [1] << endl << theG[0];
delete [] theG;
return 0;
}
The output would be 5 and 7 correct?
Since MM is an array, &MM is a pointer to an array (that's the type int (*)[2] that you see in the error). Instead, you seem to want to pass a pointer to the first element of the array. There are two ways to do that. Firstly, you can explicitly get the first element and then take the address of it: &MM[0]. Secondly, you can rely on array-to-pointer conversion to do it for you and just pass MM. Array-to-pointer conversion converts an array to a pointer to its first element.
I know this question has already been answered but I believe I can contribute to the asker's understanding.
Let's start with the basics:
void main()
{
int a = 2; // a is an int
cout << a << endl; // print 2
int *b; // b is a pointer-to-int
b = &a; // store the address of a in b
cout << *b << endl;// print the value that b points to, which is 2
int my_array = new int[3]; // allocate an array with 3 integers
my_array[0] = 50; // store 50 in the first element of the array
my_array[1] = 51; // store 51 in the second element of the array
my_array[2] = 52; // store 52 in the third element of the array
cout << c[0] << endl; // print 50
some_function(my_array, 3); // explained below
}
Now let's see how to pass arrays into functions. Assume we want to have a function called some_function that receives an array.
void some_function(int *some_array, int size_of_the_array)
{
// use the array however you like here
}
The function some_function receives a pointer to an int (also known as "pointer-to-int"). The name of an array is always the address of its first element, so if a function expects a pointer to an int and you give it the name of an array of ints, you are actually giving it the address of the first element in the array (this is just C++ syntax rules). So the function now has the address of the first element in the array, it can do stuff like *some_array to access the first element in the array, but what if it wants to access the other elements? It adds 1 to the pointer it already has and then applies the * operator to it: *(some_array + 1). Let's say an int is 4 bytes, if you add 1 to a pointer-to-int, the result of this addition is a new pointer that points to a location in memory 4 bytes ahead, so *(some_array + 93) is the value in the 94th element of the array some_array (array elements are stored sequentially in memory). A shorthand notation for this is some_array[93]. So if you have int *some_array = new int[100];, then some_array is a pointer and some_array[93] is the same as *(some_array + 93), which is the 94th element in the array.
The address itself though is not enough, you also need to know the number of entries in the array so that you don't try to access an element past the end of the array. In this example, assume that some_function simply prints the contents of the array, so if you don't provide 3 as the second argument to the function then it will have no way of knowing when to stop adding 1 to the pointer it received in the first argument. Beware, however, that by passing an array to a function this way, you are not passing the function a copy of the array, you are simply telling it where to find its contents in memory.
I'm sorry if I didn't pick a descriptive or concise name. A lot of questions sound similar, but I haven't been able to find what I'm looking for. What I want to do is store a 2D array of pointers somewhere and assign a variable in some object to that array to be able to access it.
Here's some example code that has the same compile error I'm getting with a bigger project.
#include <iostream>
using namespace std;
struct X{
float n, * b[8][8];
X(){
n = 1;
b[1][5] = &n;
cout << *(b[1][5]) << endl;
}
void Set(float * c[8][8]){
b = c;
cout << *(b[1][5]) << endl;
}
};
main(){
float m, * a[8][8];
m = 2;
a[1][5] = &m;
X obj;
obj.Set(a);
}
What I want to happen in this code is that an X object starts with its own 2D array, whose value pointed to by b[1][5] should be printed as "1". Then the main method's 2D array, a, is passed to the object's Set() method and assigned to its array variable. The value pointed to by b[1][5] should then be printed as "2".
However, I can't figure out what type the Set() parameter, c, should be. I get
error: incompatible types in assignment of ‘float* (*)[8]’ to ‘float* [8][8]’
when I try to compile. As for why I want to do this, I'm trying to use an array of pointers to objects, not floats, but it's the same error.
Try this:
#include <iostream>
using namespace std;
struct X{
float n;
float* (*b)[8];
X(){
n = 1;
b = new float*[8][8];
b[1][5] = &n;
cout << *(b[1][5]) << endl;
}
void Set(float * c[8][8]){
delete[] b;
b = c;
cout << *(b[1][5]) << endl;
}
};
main(){
float m, * a[8][8];
m = 2;
a[1][5] = &m;
X obj;
obj.Set(a);
}
Here, X stores a pointer to a 1D array, which we are treating as a pointer to the first element of a 2D array - i.e. as just a 2D array.
In X's constructor, X allocates its own array with new and sets its pointer to point to that. When calling Set(), X deletes its own array, and sets its pointer to point to the array provided by the caller.
The only thing to watch out for is, if you call Set() again, that array will in turn be deleted (which will blow up if that array is a stack array, like in this case). So, it might be advisable to separate the line that does delete[] b into its own member function, and call it only when necessary.
Your problem with set is that you need to copy the array contents - just doing b=c can't do what you want.
void Set(float * c[8][8]){
for(unsigned int i=0; i<8; ++i) {
for(unsigned int j=0; j<8; ++j) {
b[i][j] = c[i][j];
}
}
cout << *(b[1][5]) << endl;
}
There are several bugs I think..
Firstly, in Set() function you have assigned an array name. But array name should not be used as a variable. You can solve this by making 2D array of pointer b as a pointer to 1D array of pointer like float * (*b)[8]...
Secondly, when you send the 2D array of pointer as argument of Set() function it is decaying into a pointer to 1D array of pointer i.e something like this float *(*a)[8]. so you have to make the formal argument of Set() function a pointer to 1D array of pointers like void Set(float * (*c)[8])...
And finally there is a thing your float variable m inside main is a local to main , so when control pass to Set() function I think the compiler cant find the value(may be deallocated) in the m...so it outputs undefined or give run time error..you can solve this by making m a static version. i.e by declaring static float m...
In total make your code like following :
#include <iostream>
using namespace std;
struct X{
float n, *(*b)[8];
X(){
n = 1;
b[1][5] = &n;
cout << *(b[1][5]) << endl;
}
void Set(float * (*c)[8]){
b = c;
cout << *(b[1][5]) << endl;
}
};
main(){
float * a[8][8];
static float m;
m = 2;
a[1][5] = &m;
X obj;
obj.Set(a);
}
This will output correctly :)