0

I want to make a simple function in C++ to get some practise. It should do the same thing as the range() function in python but for now a lot simpler. I ran into a problem that the array isn't returned properly from the function to the main function. I used to code below and got a weird error. Does someone know what the problem might be?

#include "iostream"

int * range(int max){
    int n = 0;
    int ran [max] ;
    while (n < max){
        ran[n] = n;
        n += 1;
    }
    return ran;
}

int main(){
    int * ranger;
    ranger = range(5);
    for(int k  = 0; k < 5; k++){
        std::cout << ranger[k] << " ";
    }
    return 0;
}

I didn't get an error message from the comiler it just did nothing for a few seconds and then printed out: Process finished with exit code -1073741819 (0xC0000005)

kamelfanger83
  • 97
  • 1
  • 7
  • 3
    `int ran [max];` is not correct C++, it use VLA extension. use `std::vector` instead. – Jarod42 Mar 23 '21 at 15:22
  • 5
    return a `std::vector` instead – Cory Kramer Mar 23 '21 at 15:23
  • 1
    Because it's declared inside the function scope. You need to pass it by reference. You can also swap your while loop with a for loop. Change `int ran [max]` to `int* ran = new int[max];` – thethiny Mar 23 '21 at 15:23
  • Although it wasn't necessary for this particular question - in general, if you're going to ask about a compile error, please include the error message. I don't care if you think the message is weird, it's information from the compiler that you're choosing to hide. – Useless Mar 23 '21 at 15:27
  • Does this answer your question? [Returning an array using C](https://stackoverflow.com/questions/11656532/returning-an-array-using-c) – Stephen Newell Mar 23 '21 at 15:32

4 Answers4

4

Try to pass the array as a reference instead of returning it.

#include "iostream"

void range(int max, int* ranger){
    int n = 0;
    while (n < max){
        ranger[n] = n;
        n += 1;
    }
 
}

int main(){
    int ranger[5];
    range(5, ranger);
    for(int k  = 0; k < 5; k++){
        std::cout << ranger[k] << " ";
    }
    return 0;
}

Edit: As @Jarod42 pointed out, I've changed the memory allocation from int * to int[5].

thethiny
  • 1,125
  • 1
  • 10
  • 26
  • By convention it is usually it's *pointer*,*size*, in that order, but that's a C-ism, not a C++ thing which uses `std::vector` instead. – tadman Mar 23 '21 at 15:26
  • 2
    and if you really must pass a pointer + size, it's best to use `std::span` or some polyfill thereof instead if you can. but `vector` is superior unless you know you need something else (or `std::array` if the bound is static). – underscore_d Mar 23 '21 at 15:26
  • 1
    @tadman I never knew there's a convention for that, I just thought it's a preferred way. Thanks for letting me know. – thethiny Mar 23 '21 at 15:47
  • @underscore_d yes but OP mentioned that they're new to C++ so they're probably studying it or something, and that's the textbook way of doing it. – thethiny Mar 23 '21 at 15:47
  • 1
    @thethiny only in bad textbooks. A textbook attempting to teach *idiomatic* C++ will use `std::vector` / `std::array` – Caleth Mar 23 '21 at 15:50
  • @Caleth my textbook said `int x[size]` and `int *x = new int [size]` and I graduated 2018. – thethiny Mar 23 '21 at 18:57
2

got a weird error. Does someone know what the problem might be?

The first step of finding out the reason for a weird error is to read the error message. It often explains the reason.

How can you return an array from a function in c++

You cannot return an array from a function in C++.

You can however return a class object, and class can contain an array as a member. As such by wrapping an array in a class and returning instance of that class, you can get around the restriction. There is a generic template for such array wrapper in the standard library. It is called std::array. std::array is limited to compile time constant size, just like array variables (see below).


int * range(/*...*/){
    // ...
    int ran [max] ;
    // ...
    return ran;
}

The problem with this is that the array is an automatic variable. The lifetime of automatic variables end when they go out of scope. Thus, the array doesn't exist after the function returns.

Returning a pointer to an automatic variable is pointless, because the pointed object never exists outside the function. Attempting to access the object through the pointer will result in undefined behaviour. You should always avoid undefined behaviour. It is bad.

Don't forget to use compiler warnings. This is what my compiler says:

warning: address of local variable 'ran' returned [-Wreturn-local-addr]
   10 |     return ran;
      |            ^~~

int * range(int max){
    // ...
    int ran [max] ;

The size of an array variable must be a compile time constant in C++. A function parameter is not compile time constant. Thus, this program is ill-formed.

Don't forget to use compiler warnings. This is what my compiler says:

warning: ISO C++ forbids variable length array 'ran' [-Wvla]
    5 |     int ran [max] ;
      |         ^~~

If you need to create a dynamic array, then you must allocate it dynamically. The simplest and the most convenient way to create a dynamic array is to use std:vector.


It should do the same thing as the range() function in python

Note that python range doesn't return a list (since python 3). It returns a lazy generator range. This would also be the recommended approach in C++, instead of returning a vector.

Lazy ranges are a bit too advanced to implement for beginners, but instead of implementing it yourself, I recommend using the standard library:

for (int i : std::views::iota(0, 5))
    std::cout << i << " ";

#include "iostream"

Minor issue: #include <iostream> should conventionally be used with standard headers. Otherwise you may get surprising results if there happens to be a file named iostream in your include path. That said, try to avoid having a file named iostream in your include path as well (besides than the standard one).

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • well the thing about the error mesasge is that i never got one: it just didn't print anything for like 2 seconds and then said: Process finished with exit code -1073741819 (0xC0000005) but i didn't get an error message – kamelfanger83 Mar 23 '21 at 15:41
  • @kamelfanger83 Did you try to find out what exit code 0xC0000005 means? – eerorika Mar 23 '21 at 15:42
  • @kamelfanger83 All of the code was syntactically legal so you won't get an error message. You may get warnings. Errors mean the code cannot be transformed into a program. The grammar is bad. Warnings mean that even though the program can be turned into program, that program probably doesn't do what you want it to do. It is logically incorrect. If you find you don't get warnings, turn up the warning level. How to do that depends on the compiler. In GCC and similar you want `-Wall -Wextra` at an absolute minimum. I add on `-pedantic` and often `-Werror` so that the warnings cannot be ignored. – user4581301 Mar 23 '21 at 16:03
  • @user4581301 That's not quite accurate. Compilers *can* produce semantic errors as well, not just syntactic ones. – eerorika Mar 23 '21 at 16:12
  • True enough. And some compilers promote warnings to errors, demote errors to warnings, or outright allow non-standard behaviour. The comment character limit is a real drag sometimes. – user4581301 Mar 23 '21 at 16:27
0

As @eerorika says, you can't pass raw arrays in C++ by value. And while you could get around this restriction, in your specific example you can simply avoid the problem altogether. You write:

int * range(int max){
    int n = 0;
    int ran [max] ;
    while (n < max){
        ran[n] = n;
        n += 1;
    }
    return ran;
}

this is a sort of a named constructor idiom for a range of consecutive integers, from 0 to max-1, in a raw array. But you don't actually need this array explicitly, seeing how you know what's in it. Instead, consider using std::views::iota(0,max).

When you want to use it, you'll probably want to iterate over the range, like so:

#include <ranges>
#include <iostream>

void foo(std::size_t max) {
    using namespace std::ranges;

    for (int i : views::iota_view(0, max)) {
        std::cout << i << ' ';
        // or any other work
    }
}

See

This question: Neatest way to loop over a range of integers or more details.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
0

I will do this way :

#include <iostream>
#include <vector>

void 
range (std::vector< int > & ranger)
{
    int n = 0;

    while (n < ranger.size())
    {
        ranger[n] = n;
        n += 1;
    }
}

int main ()
{
    std::vector< int > ranger(5);
    
    range(ranger);
    
    for ( auto value : ranger )
    {
        std::cout << value << ' ';
    }

    return 0;
}

Or, if you really want to return the array :

std::vector< int > 
range (int max)
{
    std::vector< int > ranger(max);

    int n = 0;

    while (n < ranger.size())
    {
        ranger[n] = n;
        n += 1;
    }

    return ranger;
}

int main ()
{
    auto ranger = range(5);
    
    for ( auto value : ranger )
    {
        std::cout << value << ' ';
    }

    return 0;
}

And finally you should take a look at std::transform from to get a better approach.