How to return array of doubles?

Pages: 12
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

#include <iostream>

using namespace std;
double *f();
int main()
{


    double *ptrToDouble = f();
    cout << ptrToDouble[0] << endl;
    cout << ptrToDouble[1] << endl;

    return 0;


}

double *f(){
double arrayOfDoubles[2] = {0};
arrayOfDoubles[0] = 1;
arrayOfDoubles[1] = 2.3;
cout << arrayOfDoubles[0] << endl;
cout << arrayOfDoubles[1] << endl;
return arrayOfDoubles;

}

The way I return gives false data
1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include <vector>

std::vector<double> f() { return { 0.1, 2.3, 4.5, 6.7, 8.9 } ; }

int main()
{
    std::vector<double> vec = f() ;
    for( double v : vec ) std::cout << v << '\n' ;
}

http://coliru.stacked-crooked.com/a/5175f1a07e0b3b43
The reason it didn't work is because arrayOfDoubles is a local variable that gets destroyed when the function ends. That means ptrToDouble will point to an object that no longer exists.
Last edited on
to do it the C way (arrays and pointers) you have 2 main ideas.
1) allocate the memory in the function and return it (pointers with new/delete, smart pointers, etc. vector hides the details of this for you). A situationally useful way is a static local in the function, but that depends on what you need. It would always return the same array, every call to the function, which may or may not be useful; it more or less makes the function a global variable surrogate of sorts.

2) pass the memory into the function (where the function only fills in the values, really).

The biggest problem is you are creating a temp array within the scope of the function. The locally created array goes out of scope and is deallocated when the function returns. Your program no longer has 'legal' access to the memory that was used by the local array.

When creating C++ programs it is best to use C++ stdlib containers. Let the container manage the memory for you. A std::vector would be a good choice. Another would be std::array.

With that said:

Allocating the memory manually for the array on the heap/free store is one possible solution, a very 'C++ that looks like C' solution:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>

double* f();

int main()
{
   double* ptrToDouble { f() };

   std::cout << ptrToDouble[ 0 ] << '\t';
   std::cout << ptrToDouble[ 1 ] << '\n';

   // it pays to be neat.  this delete isn't necessary
   // the memory will be deallocated when the program terminates

   // ultimate question is 'who owns this memory?'
   // f() or main()?
   delete[] ptrToDouble;
}

double* f()
{
   // create the array on the heap/free store to survive
   // deallocation of local assets when the function returns
   double* arrayOfDoubles = new double[ 2 ];

   arrayOfDoubles[ 0 ] = 1.2;
   arrayOfDoubles[ 1 ] = 2.3;

   std::cout << arrayOfDoubles[ 0 ] << '\t';
   std::cout << arrayOfDoubles[ 1 ] << '\n';

   return arrayOfDoubles;
}
1.2     2.3
1.2     2.3

http://coliru.stacked-crooked.com/a/5d9537e87735319d

Passing objects created using new leads to the "who owns the memory" dilemma. Is it the f() function or main()? Deleting manually allocated memory can present problems when changing scope. Problems that can be nigh um-possible to track down.

I prefer to leverage what C++ has to offer and not deal with manual memory management if I don't have to.

Another solution is to create the array in main and pass it to f():
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>

void f(double[], int);

int main()
{
   double ptrToDouble[2];

   f(ptrToDouble, 2);

   std::cout << ptrToDouble[ 0 ] << '\t';
   std::cout << ptrToDouble[ 1 ] << '\n';
}

void f(double arrayOfDoubles[], int size)
{
   arrayOfDoubles[ 0 ] = 1.2;
   arrayOfDoubles[ 1 ] = 2.3;

   std::cout << arrayOfDoubles[ 0 ] << '\t';
   std::cout << arrayOfDoubles[ 1 ] << '\n';

   return;
}

http://coliru.stacked-crooked.com/a/a8a2716d3a50d8cf

No manual memory management, but when passing regular arrays into a function they decay to a pointer so passing the size of the array is highly recommended.

The degradation of regular arrays to a pointer is yet another reason to prefer using C++ containers, they retain their awareness of their size when passed.
With C++ you can skip several steps when instantiating an array and assign values on creation (requires C++11 or later):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>

double* f();

int main()
{
   double* ptrToDouble { f() };

   std::cout << ptrToDouble[ 0 ] << '\t';
   std::cout << ptrToDouble[ 1 ] << '\n';

   // it pays to be neat.  this delete isn't necessary
   // the memory will be deallocated when the program terminates

   // ultimate question is 'who owns this memory?'
   // f() or main()?
   delete[] ptrToDouble;
}

double* f()
{
   // create the array on the heap/free store to survive
   // deallocation of local assets when the function returns
   double* arrayOfDoubles = new double[ 2 ] { 1.2, 2.3 };

   std::cout << arrayOfDoubles[ 0 ] << '\t';
   std::cout << arrayOfDoubles[ 1 ] << '\n';

   return arrayOfDoubles;
}

http://coliru.stacked-crooked.com/a/05110a12ccb61281

C++20 allows for parenthesized initialization, change line 24 to:
double* arrayOfDoubles = new double[ 2 ] ( 1.2, 2.3 );
If you don't want to use std::vector (??), then use managed memory:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <memory>
#include <iostream>

auto f() {
	std::unique_ptr<double[]> ptd { new double[] {1.2, 2.3} };

	std::cout << ptd[0] << '\n';
	std::cout << ptd[1] << '\n';

	return ptd;
}

int main() {
	const auto pd { f() };

	std::cout << pd[0] << '\n';
	std::cout << pd[1] << '\n';
}



1.2
2.3
1.2
2.3

Last edited on
I keep forgetting about using auto, either as a type for creating an object or as a function return type.

Ooooops!
Just remember AAA - Alcoholics Anonymous Always. Sorry, Almost Always Auto
https://herbsutter.com/2013/08/12/gotw-94-solution-aaa-style-almost-always-auto/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>

using namespace std;
double *f();
int main()
{


    double *ptrToDouble = f();
    cout << ptrToDouble[0] << endl;
    cout << ptrToDouble[1] << endl;

    return 0;


}

double *f(){
static double arrayOfDoubles[2] = {0};
arrayOfDoubles[0] = 1;
arrayOfDoubles[1] = 2.3;
cout << arrayOfDoubles[0] << endl;
cout << arrayOfDoubles[1] << endl;
return arrayOfDoubles;

}


On line 19 declare your array as static.
Last edited on
You should mention that declaring the array as static makes there be one, single array for the lifetime of the program, which might not be what OP wanted.

Just remember AAA

In the US that is "Triple A," the American Automobile Association. An organization that provides roadside assistance for an annual fee.
Where expression templates or (proxy objects in general) are involved, the rule could be:
'Almost Never Auto' or 'Quite Rarely Auto'

Common pitfalls (Eigen):
In short: do not use the auto keywords with Eigen's expressions, unless you are 100% sure about what you are doing. In particular, do not use the auto keyword as a replacement for a Matrix<> type.
https://eigen.tuxfamily.org/dox/TopicPitfalls.html


In particular, be wary about unbridled use of deduced return types for functions.
1
2
3
4
5
6
7
8
#include <valarray>

auto almost_always_auto()
{ 
    std::valarray<int> va { 0, 1, 2, 3, 4, 5 } ;
    // ...
    return va*5 ; // *** disaster ***
}


CoreGuideLines:
As of C++20, we can (and should) use concepts to be more specific about the type we are deducing
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-auto

Write a convoluted const std::signed_integral auto r = std::rand() ;
instead of the straightforward const int r = std::rand() ; ?
Thank you very much.
There's always the exception that proves the rule...

and don't mention decltype(auto)
Last edited on
There's always the exception that proves the rule...


1
2
3
4
5
6
7
8
9
10
11
12
13
#include <valarray>
#include <iostream>

auto almost_always_auto()
{ 
    std::valarray<int> va { 0, 1, 2, 3, 4, 5 } ;
    return va *= 5;
}

int main()
{
   for ( auto e : almost_always_auto() ) std::cout << e << ' ';
}
0 5 10 15 20 25
Last edited on
The auto type has its uses, but can be over-used, abused and misused.

Duh, just like all of C++ in the hands of not-even-fair programmers.
AAA is one of those things I was calling eggheadery. The reasons given for it are just arm waving to support a preference by the folks that came up with it and followers. It makes code that much more difficult to follow if you use it on everything. Coming from having had to deal with a lot of old school matlab code, where anything can be any type at any time, that was a nightmare when a novice gave you a piece of code. Now its a string, now its a matrix, now its a double, now its a matrix again... auto isn't quite that bad, but I am very against using it everywhere. If modern IDE did not have mouseover - type showing, I would be against using it most places, but with that, I can tolerate reasonable use of it esp ugly iterators or templates. To me overuse of auto is like putting in 200 typedefs and macros to make your c++ look more like pascal.
Last edited on
Actually, Python does it all the time. Except that it doesn't even bother to write "auto". Maybe we just have to learn to live with it.
Last edited on
Creating iterators is IMO one use of auto, in a for loop for example (Sprite is a custom class).

1
2
vector<Sprite*>::iterator siSprite;
for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end(); siSprite++)

vs.

for (auto siSprite = m_vSprites.begin(); siSprite != m_vSprites.end(); siSprite++)

If you decide to change the begin/end to cbegin/cend or even the reverse iterators auto changes the construction of the iterator for you.

A better for loop in the begin to end use might be a range based for loop, using auto:
for (auto& siSprite : m_vSprites)

C++20 allows for a reverse range based for loop, using a range adaptor.
https://www.fluentcpp.com/2020/02/11/reverse-for-loops-in-cpp/

auto is a useful tool, when used judiciously.
I think auto has a cost in terms of readability but sometimes it's worth it.

Personally I often use it with iterators, less often with range-based for loops (I did for a while but found that my code became much harder to understand).

 
for (auto it = sprites.begin(); it != sprites.end(); ++it)

 
for (Sprite* sprite : sprites)

I might also use it when storing the result of make_unique because I think all the information is easily available on the right hand side.

 
auto sprite = std::make_unique<Sprite>();

Obviously I also use it with lamdas if I need to store them in a variable (there is no other choice).

 
auto lambda = [](){};

Otherwise I think auto is mostly useful in templated code.

This is just my personal opinion. I'm not hating. People can use AAA if they want. My only worry is that influential people like Herb Sutter will influence the design of C++ to make it cumbersome to write new things in other ways, or that it becomes an accepted fact that writing it this way is the "only right way". I think there is already a tendency towards making changes to benefit "generic programming" that are not necessarily the best for "normal" (non-templated) code which I think is after all the most common type of code (is it not?). It might be hard to realize for people working on the standard library and other heavily templated libraries.
Last edited on
Pages: 12