0

I want to have a function which takes a positive integer, then declares an array, initializes it and print it. The following code works with a GCC compiler but it does not work with an MSVC compiler. I get the error

Error (active) E0028 expression must have a constant value. The value of parameter "Length" (declared at line 5) cannot be used as a constant

  1. What is a nice way to do this with an MSVC compiler? and
  2. Is there any nice reason for this difference?

My code:

#include <iostream>

using namespace std;

void Print(const int Length)
{
    int Array[Length];
    for (int i = 0; i <= Length - 1; i++)
    {
        Array[i] = i;
        cout << Array[i];
    }
}

int main()
{
    const int L = 5;
    Print(L);
    return 0;
}
user4581301
  • 33,082
  • 7
  • 33
  • 54
Hosein Rahnama
  • 125
  • 1
  • 8
  • 6
    That is an extension to the language provided by gcc and doesn't work in c++ in general. It's not just MSVC being picky. The usual recommendation here would be to use `std::vector Array(Length);` instead. – François Andrieux Apr 24 '19 at 18:30
  • Possible duplicate of [Array initialization use const variable in C++](https://stackoverflow.com/questions/18996258/array-initialization-use-const-variable-in-c) – Paul92 Apr 24 '19 at 18:31
  • 1
    @Paul92 Not quite. That question is about a `const` not being treated as a `constexpr` due to it's initialization. – François Andrieux Apr 24 '19 at 18:33
  • 2
    Related but not quite duplicate : [Why aren't variable-length arrays part of the C++ standard?](https://stackoverflow.com/questions/1887097/why-arent-variable-length-arrays-part-of-the-c-standard) I'm sure there's an actual duplicate out there though. – François Andrieux Apr 24 '19 at 18:35
  • @FrançoisAndrieux: Can you tell me, why this is not allowed in standard C++? – Hosein Rahnama Apr 24 '19 at 18:40
  • Note that a constant parameter may be passed in with different values. GCC has probably done some static analysis on the source so that the value of Length is known at compile time. MSVC does not analyze the source across function, so MSVC complains. – Bruce Shen Apr 24 '19 at 18:42
  • 1
    @H.R. My comment contains a link to that exact question. – François Andrieux Apr 24 '19 at 18:44
  • 1
    [Here is an example of what can go wrong with Variable Length Arrays](https://stackoverflow.com/questions/1847789/segmentation-fault-on-large-array-sizes). – user4581301 Apr 24 '19 at 18:44
  • @BruceShen it's not static analysis, but a gcc extension called _variable-length arrays_ (VLAs). Standard C99 supports them, but C++ doesn't. An alternative is the (also non-portable) `alloca()` function for dynamic stack allocation. – TheOperator Apr 24 '19 at 18:45
  • @FrançoisAndrieux: So I don't understand why this one is a case of array with variable length! I mean the parameter `Length` is declared `const`. Can you elaborate on that in an answer please. :) – Hosein Rahnama Apr 24 '19 at 18:47
  • 1
    @H.R. `const` is not `constexpr`. A constant expression means a value known at compile time. You could be providing user input to your function, a `const` function argument is not necessarily a constant expression. – François Andrieux Apr 24 '19 at 18:49
  • 1
    @H.R. `const` and _constant expression_ (`constexpr`) are two different concepts. The former means that a variable cannot be changed, the latter means that its value is known at compile time. – TheOperator Apr 24 '19 at 18:49
  • 1
    `void Print(const int Length)` can be called with any number that can fit in an `int`. The fact that right now you are calling it only once with constants does not eliminate the possibility that it CAN be called twice, once with 10 and another time with 4,000,000,000. the 10 and the 4,000,000,000 can even be constants, but that doesn't matter. The fact that `Length` can be different means it's not constant enough. – user4581301 Apr 24 '19 at 18:51
  • @H.R. I Don't understand the downvote either. Your question is worded correctly, and that's definitely not a bad question, if I've seen any. – MartinVeronneau Apr 24 '19 at 19:01
  • Can the downvoter explain why they think this question's bad? – MartinVeronneau Apr 24 '19 at 19:45

2 Answers2

0

As it was pointed out in the comments, you should definitely use std::vector<int>.

If you want the array to live only inside your function Print, you can declare a dynamic array on the stack using new. Be mindfull of memory usage, though, as Print could be called with a big number, and you would get a stack overflow (again, use vector to avoid that).

#include <iostream>

using namespace std;

void Print(const int Length)
{
    int *Array = new int[Length];
    for (int i = 0; i < Length; i++)
    {
        Array[i] = i;
        cout << Array[i];
    }
    delete [] Array;
}

int main()
{
    const int L = 5;
    Print(L);
    return 0;
}

EDIT : Here's the vector-based correct solution :

#include <iostream>
#include <vector>

using namespace std;

void Print(const int Length)
{
    vector<int> Array;
    Array.resize(Length);
    for (int i = 0; i < Length; i++)
    {
        Array[i] = i;
        cout << Array[i];
    }
}

int main()
{
    const int L = 5;
    Print(L);
    return 0;
}
MartinVeronneau
  • 1,296
  • 7
  • 24
  • Questions consisting only of code are considered incomplete. Please explain how your solution fixes the problem. – François Andrieux Apr 24 '19 at 18:34
  • 4
    Using `new` for dynamic allocation is no longer recommended. This solution works but it is nowhere near as robust as using a standard container. Edit : There is a bug due to using `i <= Length` instead of `i < Length`. – François Andrieux Apr 24 '19 at 18:34
  • Indeed, I didn't spot the bug, since it was in the OP's original question. Edit : `i <= Length-1`. That works. I wouldn't have done it this way, but that works. – MartinVeronneau Apr 24 '19 at 18:36
  • 4
    No need for `if(Array)`. `new` throws an exception on failure. – user4581301 Apr 24 '19 at 18:40
  • 1
    @MartinVéronneau Watch out using `Length - 1`. Size are often unsigned (like containers' `.size()` members) and applying `-1` to it when it's empty will cause it to wrap around the the maximum representable value for that type. It works here, beause `Length` is `signed` but it's a risky habit to adopt. – François Andrieux Apr 24 '19 at 19:02
  • Indeed you're right. Like I said earlier, I wouldn't have done it this way. I edited the code per your suggestion, but I was just trying to stay close to the OP's code. But that's definitely a good habit to pick up! – MartinVeronneau Apr 24 '19 at 19:08
0

If you really want a dynamically allocated, fixed size array, use an std::unique_ptr instead of an std::vector.

#include <iostream>
#include <memory>

void Print(const int Length){
    std::unique_ptr<int[]> Array = std::make_unique<int[]>(Length);
    for (int i = 0; i < Length; ++i){
        Array[i] = i;
        std::cout << Array[i];
    }
    Array.reset();
}
Pierce Griffiths
  • 733
  • 3
  • 15