Incorrect values when initializing a 2D array to 0 in gcc
Asked Answered
R

3

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.

Rosanarosane answered 31/8, 2018 at 16:31 Comment(14)
This int opt[rows][cols] is not valid C++ - array sizes must be compile-time constants, not variables.Wenz
Variable length arrays are not supported in C++. If you change rows and columns to be const the problem is fixed.Pomiculture
@Rosanarosane Please don't readd the C tag. The question has nothing to do with C...Thiazine
C or C++? Please pick one tagDante
While variable length arrays are an extension in C++ gccs docs for VLA does not mention issues w/ initialization.Hammer
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.Rosanarosane
As far as I know, in C99 VLA can't be initialized that way. Not sure how the gcc extension behave in C++, though.Petiolule
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 bugVaginitis
Also 1D VLAs seem to be initialized properly. I suggest filing a bug report.Thiazine
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).Cowcatcher
Compiled as C99 code (replacing cout with printf), GCC gives the error error: variable-sized object may not be initialized.Wenz
@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++.Lubricous
just use malloc!Kucik
just use calloc! The calloc function sets memory to zeros. The malloc function does not alter the memory; you get what you get.Bridoon
H
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.)

Hammer answered 31/8, 2018 at 16:52 Comment(0)
A
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.

Antiparticle answered 31/8, 2018 at 16:45 Comment(5)
Since it's C++, = {} should work. But in reality it gives me internal compiler error. :/Thiazine
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.Cowcatcher
@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.Antiparticle
@Antiparticle 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).Thiazine
@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), 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".Antiparticle
R
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 
Rosanarosane answered 31/8, 2018 at 17:0 Comment(2)
Or even better, use a single std::vector<int> of size rows * cols.Thiazine
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.Rosanarosane

© 2022 - 2024 — McMap. All rights reserved.