Неверные значения при инициализации 2D массива в 0 в gcc - PullRequest
0 голосов
/ 31 августа 2018
#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;
}

Выход:

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 

Я использую gcc 6.3, в https://www.codechef.com/ide

Я ожидаю, что в первом ряду будут все нули. Разве это не так?

РЕДАКТИРОВАТЬ: я проверил с константными переменными для строк и столбцов, а затем он инициализирован для всех нулей. Я чувствую, что это должно вызвать ошибку компиляции вместо того, чтобы демонстрировать это неправильное (и потенциально опасное) поведение.

Ответы [ 3 ]

0 голосов
/ 31 августа 2018

Если посмотреть на примечания к выпуску gcc 4.9 , похоже, что они добавили поддержку инициализации VLA с ожиданием, что VLA будет поддерживаться в будущей версии C ++:

G ++ поддерживает массивы C ++ 1y переменной длины. G ++ долгое время поддерживал VLA в стиле GNU / C99, , но теперь дополнительно поддерживает инициализаторы и лямбда-захват по ссылке. В режиме C ++ 1y G ++ будет жаловаться на использование VLA, которые не разрешены проектом стандарта, такие как формирование указателя на тип VLA или применение sizeof к переменной VLA. Обратите внимание, что теперь кажется, что VLA не будет частью C ++ 14, но будет частью отдельного документа и, возможно, C ++ 17.

Мы можем видеть , что до 4.9 жалуется, что мы не можем инициализировать VLA

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

но в 4.9.1 и после он перестает жаловаться, и у него нет той же ошибки, которую мы видим в более поздних версиях .

Так что это похоже на регрессию.

Обратите внимание, что clang отказывается разрешить инициализацию VLA (, которую они поддерживают как расширение ) , см. Живой пример . Что имеет смысл, поскольку C99 не позволяет инициализировать VLA :

Тип объекта, который должен быть инициализирован, должен быть массивом неизвестного размера или типом объекта , который не является типом массива переменной длины.

gcc Ошибка 69517

gcc отчет об ошибке: SEGV в VLA с избыточным количеством элементов инициализатора содержит комментарий, который предоставляет некоторые сведения об этой функции:

(В ответ на Якуба Елинека из комментария № 16)

Ошибка здесь в том, что G ++ принимает инициализатор VLA с большим количеством элементов, чем есть в VLA, а затем уничтожает стек во время выполнения с дополнительными элементами. Это регрессия в отношении GCC 4.9.3, которая реализует VLA C ++, как указано в n3639 (http://www.open -std.org / jtc1 / sc22 / wg21 / docs / paper / 2013 / n3639.html ) , Это задокументировано в изменениях GCC 4.9 (https://gcc.gnu.org/gcc-4.9/changes.html), которые выделяют функцию, используя следующий пример:

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

VLA впоследствии были удалены из C ++, а также частично (но не полностью) удалены из G ++, что приводит к сбою программ на C ++, разработанных и протестированных с G ++ 4.9, при переносе на более позднюю версию.

C ++ VLA будет безопаснее использовать с патчем, упомянутым в комментарии № 9. Это исправление пришлось вернуть из GCC 6.0, потому что это вызывало проблемы в Java. Java была удалена, и я планирую / надеюсь повторно представить патч для GCC 8. (Я хотел сделать это для GCC 7, но не получил его).

0 голосов
/ 31 августа 2018

Я разместил этот вопрос, чтобы понять, что не так с моим кодом или gcc. Но это то, как я бы сделал это в C ++. Используйте векторы вместо массивов для требований к массиву переменной длины.

#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;
}

Выход:

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 
0 голосов
/ 31 августа 2018

Это похоже на ошибку GCC, и желаемое поведение, скорее всего, не должно компилироваться. C99 поддерживает массивы переменной длины, но отказывается их инициализировать: инициализаторам C необходимо знать их тип во время компиляции, но тип массива переменной длины не может быть завершен во время компиляции.

В GCC C ++ получает массивы переменной длины в качестве расширения от своей поддержки C99. Следовательно, поведение, управляющее инициализацией массива переменной длины в C ++, не установлено стандартом. Clang отказывается инициализировать массив переменной длины даже в C ++.

Обратите внимание, что даже = {0} технически опасен (если он вообще работал): если rows и cols равны 0, вы будете переполнены. Memset, вероятно, ваш лучший вариант.

...