13
#include <iostream>
using namespace std;

int main() {

    int rows = 10;
    int cols = 9;
    int opt[rows][cols] = {0};

         for (int i = 0; i < rows; ++i) {
            for (int j = 0; j < cols; ++j) {
                std::cout << opt[i][j] << " ";
            }
             std::cout << "\n";
         }

    return 0;
}

Output:

0 32767 1887606704 10943 232234400 32767 1874154647 10943 -1 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 

I'm using gcc 6.3, in https://www.codechef.com/ide

I'm expecting the first row to be all zeros. Shouldn't that be the case?

EDIT: I tested with const variables for rows and cols, and then it initialized to all zeroes. I feel this should throw a compile error instead of exhibiting this incorrect (and potentially dangerous) behavior.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
dev_nut
  • 2,476
  • 4
  • 29
  • 49
  • 12
    This `int opt[rows][cols]` is not valid C++ - array sizes must be compile-time constants, not variables. –  Aug 31 '18 at 16:33
  • 6
    Variable length arrays are not supported in C++. If you change `rows` and `columns` to be `const` the problem is fixed. – imreal Aug 31 '18 at 16:36
  • 6
    @dev_nut Please don't readd the C tag. The question has nothing to do with C... – HolyBlackCat Aug 31 '18 at 16:37
  • C or C++? Please pick one tag – Mad Physicist Aug 31 '18 at 16:37
  • While [variable length arrays are an extension in C++](https://stackoverflow.com/q/21273829/1708801) gccs docs for VLA does not mention issues w/ initialization. – Shafik Yaghmour Aug 31 '18 at 16:37
  • 8
    I rolled back, because someone changed the variables to const in the original code, which makes the whole question nonsensical. Didn't intend to roll back tags. – dev_nut Aug 31 '18 at 16:38
  • As far as I know, in C99 VLA can't be initialized that way. Not sure how the gcc extension behave in C++, though. – Bob__ Aug 31 '18 at 16:40
  • 1
    Interestingly, testing on wandbox, the gcc 4.9.x family all produce all zeros. Everything above that does not and below says the array cannot be initialized. It's probably a bug – NathanOliver Aug 31 '18 at 16:40
  • Also 1D VLAs seem to be initialized properly. I suggest filing a bug report. – HolyBlackCat Aug 31 '18 at 16:42
  • 1
    Even in standard C, you cannot initialize a variable size array — I'm astonished the code compiles at all (but it must be a GCC extension). – Jonathan Leffler Aug 31 '18 at 16:44
  • 1
    Compiled as C99 code (replacing cout with printf), GCC gives the error `error: variable-sized object may not be initialized`. –  Aug 31 '18 at 16:44
  • @JonathanLeffler Concur. I'm amazed gcc went to such lengths in the first place. just compiling C11 code with clang, VLAs are supported, but specified initialization is likewise *not*. gcc really went the extra mile to support their non-standard extension in C++. – WhozCraig Aug 31 '18 at 16:44
  • just use malloc! – cat Aug 31 '18 at 20:38
  • just use calloc! The `calloc` function sets memory to zeros. The `malloc` function does not alter the memory; you get what you get. – Thomas Matthews Aug 31 '18 at 23:34

3 Answers3

13

If we look at the gcc 4.9 release notes it looks like they added support for initializating VLA with the expectation VLA would be supported in a future version of C++:

G++ supports C++1y variable length arrays. G++ has supported GNU/C99-style VLAs for a long time, but now additionally supports initializers and lambda capture by reference. In C++1y mode G++ will complain about VLA uses that are not permitted by the draft standard, such as forming a pointer to VLA type or applying sizeof to a VLA variable. Note that it now appears that VLAs will not be part of C++14, but will be part of a separate document and then perhaps C++17.

We can see it live that before 4.9 complains we can't initialize a VLA

error: variable-sized object 'opt' may not be initialized  
     int opt[rows][cols] = {0};  
                             ^

but in 4.9.1 and after it stops complaining and it does not have the same bug we see in more recent versions.

So it looks like a regression.

Note that clang refuses to allow initialization of a VLA (which they support as an extension) see a live example. Which make sense since C99 does not allow initialization of VLA:

The type of the entity to be initialized shall be an array of unknown size or an object type that is not a variable length array type.

gcc Bug 69517

gcc bug report :SEGV on a VLA with excess initializer elements has a comment that provides some background on this feature:

(In reply to Jakub Jelinek from comment #16)

The bug here is in G++ accepting a VLA initializer with more elements than there is room for in the VLA, and then trashing the stack at runtime with the extra elements. It is a regression with respect to GCC 4.9.3 which implements C++ VLAs as specified in n3639 (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3639.html). This is documented in GCC 4.9 changes (https://gcc.gnu.org/gcc-4.9/changes.html) which highlights the feature using the following example:

  void f(int n) {
    int a[n] = { 1, 2, 3 }; // throws std::bad_array_length if n < 3
    ...

VLAs were subsequently removed from C++, and also partially (but not completely) removed from G++, which causes C++ programs developed and tested with G++ 4.9 to break when ported to a later version.

C++ VLAs will be safer to use with the patch referenced in comment #9. It patch had to be reverted from GCC 6.0 because it caused problems in Java. Java has been removed and I plan/hope to resubmit the patch for GCC 8. (I wanted to do it for GCC 7 but didn't get to it.)

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
5

This appears to be a GCC bug, and the desired behavior is most likely that this shouldn't compile. C99 supports variable-length arrays, but refuses to initialize them: C initializers need to know their type at compile-time, but the type of a variable-length array can't be complete at compile-time.

In GCC, C++ gets variable-length arrays as an extension from its C99 support. Therefore, the behavior governing variable-length array initialization in C++ isn't established by a standard. Clang refuses to initialize a variable-length array even in C++.

Note that even = {0} is technically sort of dangerous (if it worked at all): if rows and cols are 0, you'll be overflowing. Memset is probably your best option.

zneak
  • 134,922
  • 42
  • 253
  • 328
  • 1
    Since it's C++, `= {}` should work. But in reality it gives me `internal compiler error`. :/ – HolyBlackCat Aug 31 '18 at 16:48
  • Note that inside a function, C allows non-constant initializers in non-static (but fixed size) arrays. Also, neither standard C nor standard C++ allows zero as an array dimension; that too is a GCC extension. – Jonathan Leffler Aug 31 '18 at 16:49
  • 2
    @HolyBlackCat, how do you infer that `= {}` should work? `{}` needs to have the same type as the thing it's initializing, and that type is unknown. – zneak Aug 31 '18 at 16:50
  • @zneak Well, it works for 1D VLAs. *"needs to have the same type as the thing it's initializing"* Not sure I understand. It's a braced-init-list, so I expect it to have no type. And the type of the thing we're initializing is known, it's `int[rows][cols]` (which seems to be a 'variably modified type', another GCC extension). – HolyBlackCat Aug 31 '18 at 17:04
  • @HolyBlackCat, it does not work in C, and it's not standard C++, and it arguably doesn't really work in non-standard C++ at this point either. It is true that I mixed up implementation details ([Clang gives a type to initializer lists](https://godbolt.org/z/R6hAHA)), but C braced initializers need to have knowledge of the type that they are initializing to support designated initializers, at the very least. I don't think that I need to clarify why I'm calling an incomplete type "unknown". – zneak Aug 31 '18 at 17:15
1

I posted this question to understand what's wrong with my code or gcc. But, this is how I would do it in C++. Use vectors instead of arrays for variable length array requirements.

#include <iostream>
#include <vector>

int main() {

    int rows = 10;
    int cols = 9;

    std::vector<std::vector<int>> opt(rows, std::vector<int>(cols, 0));

         for (int i = 0; i < rows; ++i) {
            for (int j = 0; j < cols; ++j) {
                std::cout << opt[i][j] << " ";
            }
             std::cout << "\n";
         }

    return 0;
}

Output:

0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
dev_nut
  • 2,476
  • 4
  • 29
  • 49
  • 2
    Or even better, use a single `std::vector` of size `rows * cols`. – HolyBlackCat Aug 31 '18 at 17:05
  • Sure, that would be more cache coherent. I like the double indexing just because it makes more intuitive when dealing with 2D matrics, tables, etc. – dev_nut Aug 31 '18 at 17:07