Related
#include <iostream>
using namespace std;
int* flipArray(int input[], int n)
{
int output[n];
int pos = 0;
for (int i = n-1; i >= 0; i--)
{
output[pos++] = input[i];
}
int* p = output;
for (int k = 0; k < n; k++)
cout << *p-k << endl << endl;
return p;
}
int main()
{
const int SIZE = 5;
int firstArray[SIZE];
for (int n = 0; n < SIZE; n++)
{
firstArray[n] = n+1;
}
int* a;
a = flipArray(firstArray, SIZE);
for (int j = 0; j < SIZE; j++)
cout << *a-j << endl;
cout << endl;
cout << *a << '\t' << *a+1 << '\t' << *a+2;
return 0;
}
I am attempting to flip firstArray using a function that returns a pointer, but I am struggling to understand how accessing an index using a pointer works.
Here is why I am confused:
Within the function flipArray, the following for-loop:
for (int k = 0; k < n; k++)
cout << *p-k << ' ';
prints "5 4 3 2 1" to the console. It was my understanding that I should be accessing an element of a vector with *(p+k), not *(p-k). If I print *(p+k), "5 6 7 8 9" is printed to the console. If I print the array without pointers and using k as the index location, "5 4 3 2 1" is printed to the console.
Within my main function, however, the values of *a which is assigned pointer p from the flipArray function, I do not get the same results:
for (int j = 0; j < SIZE; j++)
cout << *a-j << endl;
prints 5
0
-1
-2
-3 to the console, and
for (int j = 0; j < SIZE; j++)
cout << *a+j << endl;
prints 5
2
3
4
5 to the console.
Further, I thought that the pointer location of *p and the pointer of location of *a should be the same! But when I print the address &p in the function, I get the location of 0x28fde0, and when I print the address of &a in the main, I get the location 0x28fedc. Of course, these were done during the same run.
Could someone tell me where I have gone astray? Thanks!
Thanks to everyone for the informative answers.
I have updated my solution, and it is now returning what I would expect it to. I have a new question about memory leaks and when pointers need to be deleted.
int* flipArray(int input[], int n)
{
int* output = new int[n];
int pos = 0;
for (int i = n-1; i >= 0; i--)
output[pos++] = input[i];
return output;
}
int main()
{
const int SIZE = 5;
int firstArray[SIZE];
for (int n = 0; n < SIZE; n++)
{
firstArray[n] = n+1;
}
int* a;
a = flipArray(firstArray, SIZE);
for (int j = 0; j < SIZE; j++)
cout << a[j] << " "; // can also be written as *(a+j), which is more prone to bugs
delete [] a;
return 0;
}
Will the pointer output be deleted when the function flipArray returns? If not, how should I delete output while also returning it? Is deleting the pointer a in my main function the same thing as deleting output, because they point to the same location?
It has been pointed out that your main problem is coming from the operator precedence. The * operator in *p - k is evaluated before the -. This means that k will be subtracted from the value of the int pointed at by p.
This is a huge pain, which is why the braces pointer[k] are commonly used. There are situations where using pointer arithmetic *(pointer + k) makes more sense, but it can be a source of bugs.
One point to note here: it is always better to use parenthesis even if you are not sure whether or not you need them.
You do have a second problem:
Here you are declaring output on the stack as a local variable, then you are returning output. When you return back to the previous stack frame, this pointer will be pointing to a decallocated buffer:
int* flipArray(int input[], int n)
{
int output[n]; // allocated on the stack
int pos = 0;
for (int i = n-1; i >= 0; i--)
{
output[pos++] = input[i];
}
int* p = output;
for (int k = 0; k < n; k++)
cout << *p-k << endl << endl;
return p; // this stack frame ends.
}
This means the contents of the buffer can be overwritten if the space the buffer is using is reallocated. Use new to allocate on the heap:
int* output = new int[n];
make sure to call delete on the pointer when you are done using it.
This bug can even present security vulnerabilities in your applications, so make sure you know when to allocate on the heap in C++.
Update:
Question: When this function returns, the array still exists in memory, and it's location is stored in the pointer a. Does returning the value output delete it? If not, will deleting the pointer a when I am done with it in the main function serve the same purpose?
When you delete the pointer, the memory pointed to that pointer is deallocated and the pointer is left dangling. A reference to a deleted pointer is pointing at memory that is technically free, which is bad. If the allocator library decides that it wants to reuse that space, your buffer, which is now in free space, will be reallocated. This means your buffer will lose all data integrity and the data inside of it cannot be trusted.
A common practice is to assign pointers to NULL when you are done using them. This way your program will crash and you will know where your bug is:
int* p = new int[10];
...
delete p;
p = NULL;
...
p[0] = 0; // this will now crash because you are accessing NULL.
for (int k = 0; k < n; k++)
cout << *p-k ;
It was my understanding that I should be accessing an element of a vector with *(p+k), not *(p-k).
Your understanding is right.You are not accessing the array here.p points to the first element 5 and every time k is substracted from it, which gives you 5-0 5-1 5-2 and so on which is equivalent to the filpped array.So if you want to access the array using the pointer
for (int k = 0; k < n; k++)
cout << *(p+k) ;// Note the paranthesis
for (int k = 0; k < n; k++)
cout << *p-k << endl << endl;
What this code is doing is completely different from what you think it does.
*p - k will be processed like
*p = 5 - 0 = 5
*p = 5 - 1 = 4
and so on not *(p+k) or *(p-k)
For your understanding :
int a[5] = { 1,2,6,4,5};
In order to access 3rd element in the array you do
a[2] = *(a+2)
and not
*a + 2
*(a + 2) = 6 and *a + 2 = 1 + 2 = 3
Take care of not returning the pointer to the local variable which will lead to undefined behavior
I've been trying to figure this out off and on for a week now and I keep running into problems.
My objective:
Write a function that allocates memory for an integer array. The function takes as an argument an integer pointer, the size of the array, and newSize to be allocated. The function returns a pointer to the allocated buffer. When the function is first called, the size will be zero and a new array will be created. If the function is called when the array size is greater than zero, a new array will be created and the contents of the old array will be copied into the new array. Your instructor has provided arrayBuilder.cpp as starter code for this programming challenge. In addition, Lab9_1.exe is the executable for this application which you can test.
The code:
#include <iostream>
using namespace std;
int * arrayBuilder(int * arr, int size, int newSize);
void showArray(int * arr, int size);
int main()
{
int * theArray = 0;
int i;
cout << "This program demonstrates an array builder function." << endl << endl;
// create the initial array. The initial size is zero and the requested size is 5.
theArray = arrayBuilder(theArray, 0, 5);
// show the array before values are added
cout << "theArray after first call to builder: " << endl;
showArray(theArray, 5);
// add some values to the array
for(int i = 0; i < 5; i++)
{
theArray[i] = i + 100;
}
// show the array with added values
cout << endl << "Some values stored in the array: " << endl;
showArray(theArray, 5);
// expand the size of the array. size is not the original size. newSize
// must be greater than size.
theArray = arrayBuilder(theArray, 5, 10);
// show the new array with the new size
cout << endl << "The new array: " << endl;
showArray(theArray, 10);
cout << endl;
delete [] theArray; // be sure to do this a1t the end of your program!
system("pause");
return 0;
}
/*
FUNCTION: arrayBuilder
INPUTS Pointer to an array. Size of the array. If size is zero, arr can be NULL.
Size of the new array.
OUTPUTS: Returns a pointer to allocated memory. If newSize is greater than size,
an array of newSize is allocated and the old array is copied into the new
array. Memory pointed to by the old array is deleted. All new elements
are initialized to zero.
*/
int * arrayBuilder(int * arr, int size, int newSize)
{
// TODO: Your code goes here
return NULL; // default return value. No memory allocated!
}
/*
FUNCTION: showArray
INPUTS: Pointer to an array. Size of the array. If size is zero, arr can be NULL.
OUTPUTS: Prints the contents of the array to the console.
*/
void showArray(int * arr, int size)
{
cout << "arr = ";
for(int i = 0; i < size; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
My struggles: I cannot figure out how to switch "arr" and a temporary array's values.
int * arrayBuilder(int * arr, int size, int newSize)
{
// TODO: Your code goes here
int * temp = new int [newSize];
for (int i = size; i < newSize; i++)
{
*arr = *temp;
temp++;
}
return NULL; // default return value. No memory allocated!
}
another attempt while searching for answers:
int * arrayBuilder(int * arr, int size, int newSize)
{
// TODO: Your code goes here
int * temp = new int [newSize];
memcpy (temp, arr, size *sizeof(int));
// HINT: Design the function before writing it.
delete[] arr;
for (int i = size; i < newSize; i++)
{
temp[i] = i;
}
return NULL; // default return value. No memory allocated!
}
Basically my end goal is to have the answer look like this:
This program demonstrates an array builder function.
theArray after first call to the builder:
arr = 0 0 0 0 0
some values stored in the array:
arr = 100 101 102 103 104
the new array:
arr = 100 101 102 103 104 0 0 0 0 0
PROGRESS!! Its not crashing anymore :-) This is where I'm at now:
This program demonstrates an array builder function.
theArray after first call to builder:
arr = -842150451 0 0 0 0
Some values stored in the array:
arr = 100 101 102 103 104
The new array:
arr = -842150451 -842150451 -842150451 -842150451 -842150451 -842150451 -8
42150451 -842150451 -842150451 -842150451
Press any key to continue . . .
I'll keep tinkering and let everyone know if I hit a wall! Thanks again guys!
OKAY! finally got it to display properly:
This program demonstrates an array builder function.
theArray after first call to the builder:
arr = 0 0 0 0 0
some values stored in the array:
arr = 100 101 102 103 104
the new array:
arr = 100 101 102 103 104 0 0 0 0 0
This is what I did. I feel like I may have cheated in the second part when i put 0 values in for "temp". It was my understanding that i was going to take the data from the previous array and put it into the new one, and instead I just remade it. (So it will only work with this particular set of values [only 0's]). Is there a different way I can code the second part so it works universally with whatever values are thrown at it???
int * arrayBuilder(int * arr, int size, int newSize)
{
int i = size;
int * temp = new int [newSize];
// What if the size is 0?
if (size <= 0)
{
while (i < newSize)
{
temp[i] = 0;
i++;
}
}
// Assuming the size _isn't_ 0
else
{
// "a new array will be created" (good)
for (i = 0; i < newSize; i++)
{
// The contents of the "old" array (arr) will be
// copied into the "new" array (temp)
while (i < size)
{
temp[i] = arr[i];
i++;
}
while (i >= size && i < newSize)
{
temp[i] = 0;
i++;
}
// as a hint, you can address the elements in
// both arrays using the [] operator:
// arr[i]
// temp[i]
}
}
// "The function returns a pointer to the allocated buffer."
// So, NULL is wrong, what buffer did you allocate?
return temp; // default return value. No memory allocated!
}
Since you put forth some effort.
Write a function that allocates memory for an integer array.
The prototype for this function was provided for you:
int * arrayBuilder(int * arr, int size, int newSize);
The function takes as an argument an integer pointer, the size of the
array, and newSize to be allocated. The function returns a pointer to
the allocated buffer.
This says nothing about doing anything with the "old" (passed in) array, so we should assume it needs to be left alone.
When the function is first called, the size will be zero and a new
array will be created.
The above text is meaningless given the context. Feel free to tell your instructor I said so. If the size is zero, how do you know how many elements to allocate?
If the function is called when the array size is greater than zero, a
new array will be created and the contents of the old array will be
copied into the new array.
OK, now the guts of what needs to be done (you're so close)
int * arrayBuilder(int * arr, int size, int newSize)
{
// What if the size is 0?
// Assuming the size _isn't_ 0
// "a new array will be created" (good)
int * temp = new int [newSize];
for (int i = size; i < newSize; i++)
{
// The contents of the "old" array (arr) will be
// copied into the "new" array (temp)
// as a hint, you can address the elements in
// both arrays using the [] operator:
// arr[i]
// temp[i]
// something is wrong here...
*arr = *temp;
// you definitely _don't_ want to do this
temp++;
}
// "The function returns a pointer to the allocated buffer."
// So, NULL is wrong, what buffer did you allocate?
return NULL; // default return value. No memory allocated!
}
You already got the answer here:
memcpy (temp, arr, size *sizeof(int));
but you are making several other mistakes after that. Primarily, you need to return temp ; not return NULL ;
But also you don't need the loop after the delete arr[] ;
Also don't delete arr[] if size is zero.
This is horribly complex code. Programming is all about reducing complexity.
With that in mind, here’s a proper C++ solution:
std::vector<int> arr = {1, 2, 3, 4, 5};
std::vector<int> copy = arr;
That’s it. I hope this exemplifies why you should use the standard library (or other appropriate libraries) rather than re-inventing the wheel. From the code you’ve posted I am assuming that you’ve learned (or are learning) C++ from a horrible book or course. Trash that and get a proper book. C++ is complex enough as it is, no need to add needless complexity.
Just to help you realize why the first attempt didn't work:
*arr = *temp;
This is assigning a value to the old array, from the new array. That's backwards.
But it's just targeting the first value, *arr doesn't change. You increment *temp, but you also need to increment *arr. (Also, manual pointer manipulation like that horrifying and memcopy() is a lot better. But hey, this is for learning purposes, right?)
Also, think about that loop:
for (int i = size; i < newSize; i++)
That's iterating through once for each bit that newSize is bigger than size. But you're doing two things here. 1) Copying over data and 2) initializing the new data. That for loop you have is good for going over the new data, but it's not the loop you want for copying over the data you already have. That would go from zero to size, right?
And when you're done you need to return the address of the array you built.
return NULL; // default return value. No memory allocated!
That's just some dummy mock code. It's a placeholder by the teacher. It's part of the code you're supposed to change.
Per your update:
I feel like I may have cheated in the second part when i put 0 values in for "temp"
Well what else were you going to put in there? You DO copy over the old array data. Then you EXPAND the array. What goes into the new territory? Zero values as a default is perfectly valid.
Is there a different way I can code the second part so it works universally with whatever values are thrown at it???
Well yes, but you'd have to actually have something to throw at it. Your ArrayBuilder function could take in additional arguments, possibly as a variadic function, so it knows what values to put into the new fields. But your function declaration doesn't have that. All it does is make the array bigger.
Also, in your last edit you've got those two while loops that iterate through i inside of a for loop which also iterates through i. That'll work, but just so you know it's a bit... uncouth. It's the sort of thing that'll get you in trouble when things get more complicated.
You could do this instead:
for (i = 0; i < newSize; i++)
{
if(i < size)
{
temp[i] = arr[i];
}
else // if(i >= size && i < newSize) //Wait a sec, this "if" is superfluous. It's conditions are enforced the the first if and the loop condition.
{
temp[i] = 0;
}
}
You should also probably delete the comments that make it sound like someone else wrote your code for you. Because someone else did your homework for you. It's best to
Finally, THOU SHALT INDENT THY CODE!
If I have correctly understood the assignment the function should look the following way.
First of all I would substitute the function declaration
int * arrayBuilder(int * arr, int size, int newSize);
for
int * arrayBuilder( const int *arr, size_t size, size_t newSize );
Here is its definition
int * arrayBuilder( int * arr, int size, int newSize)
{
int *tmp = 0;
if ( newSize >= 0 )
{
tmp = new int[newSize] {};
int copy_size = std::min( size, newSize );
if ( copy_size > 0 ) std::copy( arr, arr + copy_size, tmp );
}
delete []arr;
return tmp;
}
Try this:
Code:
#include <iostream>
using namespace std;
int a[3] =
{
1,
2,
3
};
int b[3];
int main ()
{
cout << endl;
cout << "Array #1 elements: " << endl;
for(int i = 0; i < 3; ++i)
{
cout << a[i] << " ";
}
for(int i = 0; i < 3; ++i)
{
b[i] = a[i];
}
cout << endl << endl;
cout << "Copying Array #1 elements to Array #2..." << endl;
cout << endl;
cout << "Array #2 elements: " << endl;
for(int i = 0; i < 3; ++i)
{
cout << b[i] << " ";
}
cout << endl << endl;
return 0;
}
I'm hoping someone can shed some light on where I am going wrong with pointers.. I've read countless web pages and tried various things but for some reason my code is returning jibberish (which I'm guessing may be the memory addresses instead of the data within my array). The purpose of the program is to create an array of 100 elements on the heap, pass this array by a pointer to a function (along with two integer variables start and end); a new array will be created on the heap (this comprises of a chunk of the original array using the start and end variables) and the pointer to this array is passed back to the main method so that the new array can be outputted. My problem is not only is the output seeming to be the location not the value, but also it seems 100 values are outputted not 20 as should be expected. I've spent hours trying to figure out where I have gone wrong and just when I think I understand the concept of pointers my faith is destroyed by red squigglies and incorrect outputs. Please HELP! My code is as follows:
#include "stdafx.h"
#include <iostream>
#include <time.h>
using namespace std;
double* getSubArray(double*, int, int);// Declare a function that will get the sub array
int _tmain(int argc, _TCHAR* argv[])
{
const int size = 100;// Declare the size of the array
double* pA;// Declare the variable to hold the pointers to the data in array
double* pB;
int start = 15;
int end = 35;
pA = new double[size];// Create space for the array
srand(clock());// Seed the program to the computers current time so that random gets a different set of random numbers everytime it is run
// Use a for loop to traverse through each element of the array (starting at index 0) placing a number defined by the random function that is no higher than 250
for (int i = 0; i < size; i++)
{
pA[i] = rand()%250;
}
cout << "An Array of 100 numbers is created and stored in the heap, these values are:" << endl;
// Output the Array for the user to see
for (int j = 0; j < size; j++)
{
// Place 10 numbers on each line
if (j % 10 == 0)
{
cout << endl;
}
cout << *(pA + j) << " ";
}
cout << endl << "The program will build a second array using the data between the indexes " << start << " & " << end << endl;
pB = getSubArray(pA, start, end);// Pass the data to the method
// Output second array for user to compare
for (int k = 0; k < size; k++)
{
// Place 10 numbers on each line
if (k % 10 == 0)
{
cout << endl;
}
cout << *(pB + k) << " ";
}
system("pause");
return 0;
}
double* getSubArray(double* pA, int start, int end)
{
double* pB = new double[end-start];// Declare space in the heap for the new array whoes size is the size of the criteria given
for (int i = 0; i < (end - start); i++)
{
for (int j = start; j < end; j++)
{
*(pB + 0) = pA[j];
}
}
return pB;
}
*(pB + 0) = pA[j];
That keeps writing to the first element of the array. Surely you want to write to each element in turn:
for (int i = start; i < end; ++i) {
pB[i-start] = pA[i];
}
or if you don't want to write your own loop
std::copy(pA+start, pA+end, pB);
Don't forget to delete[] everything you new[] or, to save mucking around with low-level memory management, use std::vector to manage the dynamic arrays for you.
I am completely new in c++ programming. I want to copy the array called distances into where pointer is pointing to and then I want to print out the resul to see if it is worked or not.
this is what I have done:
int distances[4][6]={{1,0,0,0,1,0},{1,1,0,0,1,1},{1,0,0,0,0,0},{1,1,0,1,0,0}};
int *ptr;
ptr = new int[sizeof(distances[0])];
for(int i=0; i<sizeof(distances[0]); i++){
ptr=distances[i];
ptr++;
}
I do not know how to print out the contents of the pointer to see how it works.
First of all, the size calculation of your ptr array is wrong. It only seems to work because the size of an int on your platform is 4, which is also one of the dimensions in your array. One way to get the right size is
size_t len = sizeof(distances)/sizeof(int);
Then you can instantiate your array and use std::copy to copy the elements:
int* ptr = new int[len];
int* start = &dist[0][0];
std::copy(start, start + len, ptr);
Finally, print them out:
for (size_t i = 0; i < len; ++i)
std::cout << ptr[i] << " ";
std::cout << std::endl;
....
delete [] ptr; // don't forget to delete what you new
Note that for a real application you should favour using std::vector<int> over a manually managed dynamically allocated array:
// instantiates vector with copy of data
std::vector<int> data(start, start + len);
// print
for (i : data)
std::cout << i << " ";
std::cout << std::endl;
use cout to print values like cout << *ptr;
int distances[4][6]={{1,0,0,0,1,0},{1,1,0,0,1,1},{1,0,0,0,0,0},{1,1,0,1,0,0}};
int *ptr;
ptr = new int[sizeof(distances[0])];
// sizeof(distances[0]) => 6 elements * Size of int = 6 * 4 = 24 ;
// sizeof(distances) => 24 elements * Size of int = 24 * 4 = 96 ;
// cout << sizeof(distances) << endl << sizeof(distances[0]);
for(int i=0; i<sizeof(distances[0]); i++){
ptr = &distances[0][i];
cout << *ptr << " ";
ptr++;
}
Explaination of your code
ptr = distances[i] => ptr = & distances[i][0];
// To put it simple, your assigning address of 1st column of each row
// i = 0 , assigns ptr = distances[0][0] address so o/p 1
// i = 1, assigns ptr = distances[1][0] address so o/p 1
// but when i > 3, i.e i = 4 , your pointing to some memory address because
// your array has only 4 rows and you have exceeded it so resulting in garbage values
I agree with #juanchopanza solution, calculation of your pointer size is wrong
int distances[3][6]={{1,0,0,0,1,0},{1,1,0,0,1,1},{1,0,0,0,0,0}};
// for this case it fails because
// 6 elements * 4 = 24 but total elements are just 18
Use sizeof(distances)/sizeof(int);
I think the easiest way to describe the problem is with a simple code. On each processor I have dynamically allocated '2D arrays' (achieved via the new*[rows],new[cols] formalism, see code below for clarification). Rightly or wrongly, I'm trying to use a committed MPI_Datatype to help me do MPI_Gatherv() to gather all the arrays into a single 2D array on the root processor.
Here's the code, and below it I highlight to salient points of it (it should be very easy to understand if compiled and ran - it asks for the dimensions of the array you desire):
#include <iostream>
#include <string>
#include <cmath>
#include <cstdlib>
#include <time.h>
#include "mpi.h"
using namespace std;
// A function that prints out the 2D arrays to the terminal.
void print_2Darray(int **array_in,int dim_rows, int dim_cols) {
cout << endl;
for (int i=0;i<dim_rows;i++) {
for (int j=0;j<dim_cols;j++) {
cout << array_in[i][j] << " ";
if (j==(dim_cols-1)) {
cout << endl;
}
}
}
cout << endl;
}
int main(int argc, char *argv[]) {
MPI::Init(argc, argv);
// Typical MPI incantations...
int size, rank;
size = MPI::COMM_WORLD.Get_size();
rank = MPI::COMM_WORLD.Get_rank();
cout << "size = " << size << endl;
cout << "rank = " << rank << endl;
sleep(1);
// Dynamically allocate a 2D square array of user-defined size 'dim'.
int dim;
if (rank == 0) {
cout << "Please enter dimensions of 2D array ( dim x dim array ): ";
cin >> dim;
cout << "dim = " << dim << endl;
}
MPI_Bcast(&dim,1,MPI_INT,0,MPI_COMM_WORLD);
int **array2D;
array2D = new int*[dim];
for (int i=0; i<dim; i++) {
array2D[i] = new int[dim](); // the extra '()' initializes to zero.
}
// Fill the arrays with i*j+rank where i and j are the indices.
for (int i=0;i<dim;i++) {
for (int j=0;j<dim;j++) {
array2D[i][j] = i*j + rank;
}
}
// Print out the arrays.
print_2Darray(array2D,dim,dim);
// Commit a MPI_Datatype for these arrays.
MPI_Datatype MPI_ARRAYROW;
MPI_Type_contiguous(dim, MPI_INT, &MPI_ARRAYROW);
MPI_Type_commit(&MPI_ARRAYROW);
// Declare 'all_array2D[][]' which will contain array2D[][] from all procs.
int **all_array2D;
all_array2D = new int*[size*dim];
for (int i=0; i<size*dim; i++) {
all_array2D[i] = new int[dim](); // the extra '()' initializes to zero.
}
// Print out the arrays.
print_2Darray(all_array2D,size*dim,dim);
// Displacement vector for MPI_Gatherv() call.
int *displace;
displace = (int *)calloc(size,sizeof(int));
int *dim_list;
dim_list = (int *)calloc(size,sizeof(int));
int j = 0;
for (int i=0; i<size; i++) {
displace[i] = j;
cout << "displace[" << i << "] = " << displace[i] << endl;
j += dim;
dim_list[i] = dim;
}
// MPI_Gatherv call.
MPI_Barrier(MPI_COMM_WORLD);
MPI_Gatherv(array2D,dim,MPI_ARRAYROW,all_array2D,&dim_list[rank],&displace[rank],MPI_ARRAYROW,0,MPI_COMM_WORLD);
// Print out the arrays.
print_2Darray(all_array2D,size*dim,dim);
MPI::Finalize();
return 0;
}
The code compiles, but runs into segmentation faults (I compile with 'mpic++' and used 'mpirun -np 2' to use 2 processors):
[unknown-78-ca-39-b4-09-4f:02306] *** Process received signal ***
[unknown-78-ca-39-b4-09-4f:02306] Signal: Segmentation fault (11)
[unknown-78-ca-39-b4-09-4f:02306] Signal code: Address not mapped (1)
[unknown-78-ca-39-b4-09-4f:02306] Failing at address: 0x0
[unknown-78-ca-39-b4-09-4f:02306] [ 0] 2 libSystem.B.dylib 0x00007fff844021ba _sigtramp + 26
[unknown-78-ca-39-b4-09-4f:02306] [ 1] 3 ??? 0x0000000000000001 0x0 + 1
[unknown-78-ca-39-b4-09-4f:02306] [ 2] 4 gatherv2Darrays.x 0x00000001000010c2 main + 1106
[unknown-78-ca-39-b4-09-4f:02306] [ 3] 5 gatherv2Darrays.x 0x0000000100000a98 start + 52
[unknown-78-ca-39-b4-09-4f:02306] *** End of error message ***
mpirun noticed that job rank 0 with PID 2306 on node unknown-78-ca-39-b4-09-4f.home exited on signal 11 (Segmentation fault).
1 additional process aborted (not shown)
The segmentation fault occurs upon execution of the 'print_2Darray(all_array2D,size*dim,dim)' function near the end of the code, where 'all_array2D' is 'supposed to' contain the gathered arrays. More specifically, the code seems to print the 'all_array2D' OK for the bit gathered from the master processor, but then gives the seg fault when the print_2Darray() function starts working on the bits from other processors.
Salient points of code:
I declare an MPI_Datatype that is a contiguous block of memory of sufficient size to store a single row of the 2D arrays. I then use MPI_Gatherv() to try and gathers these rows.
The code's sleep(1) call is just to help the user see the prompt for 'dims' more clearly, otherwise it get's buried between the 'size' and 'rank' couts.
The elements of the 2D array are initialized to values "i*j + rank" where i and j are the row and column indices respectively. My rationale is that the resulting numbers easily give away the rank of the processor that generated that array.
I guess it boils down to me not knowing how properly to MPI_Gatherv() dynamically allocated arrays... Should I be using MPI_Datatypes at all? It's quite important to me that the arrays are dynamically allocated.
I will be very grateful for any help/suggestions! I'm pretty much depleted of ideas!
MPI_Gatherv, MPI_Scatterv, and in fact all other MPI communication calls that take array arguments, expect that array elements are laid out consecutively in memory. This means that in the call MPI_Gatherv(array2D, dim, MPI_ARRAYROW, ...), MPI expects that the first element of type MPI_ARRAYROW starts at the memory location that array2D points to, the second element starts at (BYTE*)array2D + extent_of(MPI_ARRAYROW), the third element starts at (BYTE*)array2D + 2*extent_of(MPI_ARRAYROW), and so on. Here extent_of() is the extent of the MPI_ARRAYROW type, which can be obtained by calling MPI_Type_get_extent.
Clearly the rows of your 2D array are not consecutive in memory since each of them is allocated by a separate invocation of the new operator. Also array2D is not a pointer to the data, but rather a pointer to the vector of pointers to each row. This doesn't work in MPI and there are countless of other questions here on StackOverflow, where this fact is discussed - just search for MPI 2D and see for yourself.
The solution is to use a big chunk of singly allocated memory block with an accompanying dope vector - see this question and the arralloc() function mentioned in the answer.
This problem, involving array allocations, comes up all the time in dealing with C/C++ and MPI. This:
int **array2D;
array2D = new int*[dim];
for (int i=0; i<dim; i++) {
array2D[i] = new int[dim](); // the extra '()' initializes to zero.
}
allocates dim 1d arrays, each dim ints in length. However, there's no reason at all why these should be laid out next to each other - the dim arrays are likely scattered across memory. So even sending dim*dim ints from array2D[0] won't work. The all_array2D is the same; you are creating size*dim arrays, each of size dim, but where they are in relation to each other who knows, making your displacements likely wrong.
To make the arrays contiguous in memory, you need to do something like
int **array2D;
array2D = new int*[dim];
array2D[0] = new int[dim*dim];
for (int i=1; i<dim; i++) {
array2D[i] = &(array2D[dim*i]);
}
and similarly for all_array2D. Only then can you start reasoning about memory layouts.
I just wanted to summarise the solution which #Hristolliev and #JonathanDursi helped me get to.
MPI commands like MPI_Gatherv() work with contiguously allocated blocks of memory, hence use of 'new' to construct 2D arrays which then feed into MPI commands won't work since 'new' doesn't guarantee contiguous blocks. Use instead 'calloc' to make these arrays (see code below as an example).
An important point by #Hristolliev: The 1st and 4th arguments of MPI_Gatherv() must be pointers to the first elements of type MPI_ARRAYROW. Dereferencing the 2D arrays by one level e.g. array2D[0] will achieve this (again, see modified working code below).
The final, working code is given below:
#include <iostream>
#include <string>
#include <cmath>
#include <cstdlib>
#include <time.h>
#include "mpi.h"
using namespace std;
void print_2Darray(int **array_in,int dim_rows, int dim_cols) {
cout << endl;
for (int i=0;i<dim_rows;i++) {
for (int j=0;j<dim_cols;j++) {
cout << array_in[i][j] << " ";
if (j==(dim_cols-1)) {
cout << endl;
}
}
}
cout << endl;
}
int main(int argc, char *argv[]) {
MPI::Init(argc, argv);
// Typical MPI incantations...
int size, rank;
size = MPI::COMM_WORLD.Get_size();
rank = MPI::COMM_WORLD.Get_rank();
cout << "size = " << size << endl;
cout << "rank = " << rank << endl;
sleep(1);
// Dynamically allocate a 2D square array of user-defined size 'dim'.
int dim;
if (rank == 0) {
cout << "Please enter dimensions of 2D array ( dim x dim array ): ";
cin >> dim;
cout << "dim = " << dim << endl;
}
MPI_Bcast(&dim,1,MPI_INT,0,MPI_COMM_WORLD);
// Use another way of declaring the 2D array which ensures it is contiguous in memory.
int **array2D;
array2D = (int **) calloc(dim,sizeof(int *));
array2D[0] = (int *) calloc(dim*dim,sizeof(int));
for (int i=1;i<dim;i++) {
array2D[i] = array2D[0] + i*dim;
}
// Fill the arrays with i*j+rank where i and j are the indices.
for (int i=0;i<dim;i++) {
for (int j=0;j<dim;j++) {
array2D[i][j] = i*j + rank;
}
}
// Print out the arrays.
print_2Darray(array2D,dim,dim);
// Commit a MPI_Datatype for these arrays.
MPI_Datatype MPI_ARRAYROW;
MPI_Type_contiguous(dim, MPI_INT, &MPI_ARRAYROW);
MPI_Type_commit(&MPI_ARRAYROW);
// Use another way of declaring the 2D array which ensures it is contiguous in memory.
int **all_array2D;
all_array2D = (int **) calloc(size*dim,sizeof(int *));
all_array2D[0] = (int *) calloc(dim*dim,sizeof(int));
for (int i=1;i<size*dim;i++) {
all_array2D[i] = all_array2D[0] + i*dim;
}
// Print out the arrays.
print_2Darray(all_array2D,size*dim,dim);
// Displacement vector for MPI_Gatherv() call.
int *displace;
displace = (int *)calloc(size,sizeof(int));
int *dim_list;
dim_list = (int *)calloc(size,sizeof(int));
int j = 0;
for (int i=0; i<size; i++) {
displace[i] = j;
cout << "displace[" << i << "] = " << displace[i] << endl;
j += dim;
dim_list[i] = dim;
cout << "dim_list[" << i << "] = " << dim_list[i] << endl;
}
// MPI_Gatherv call.
MPI_Barrier(MPI_COMM_WORLD);
cout << "array2D[0] = " << array2D[0] << endl;
MPI_Gatherv(array2D[0],dim,MPI_ARRAYROW,all_array2D[0],&dim_list[rank],&displace[rank],MPI_ARRAYROW,0,MPI_COMM_WORLD);
// Print out the arrays.
print_2Darray(all_array2D,size*dim,dim);
MPI::Finalize();
return 0;
}
Compile with mpic++.