Является ли переход массива C-Style в std:: array полностью безопасным для массивов?

первый раз вопрос :) Можно ли преобразовать глобальные массивы c-стиля в массивы std::без нарушения кода? Я работаю над проектом, который состоит из декомпиляции исходного кода старой игры. Нам уже удалось реструктурировать большую часть разборки/выход декомпиляции. Поскольку это автоматически, есть еще такие разделы, как

  int a;
  int b[50];
  *(&a + 100) = xxx;

или

  int b[50];
  int a;
  *(&a - 100) = xxx;

и другие типы сумасшедшей арифметики указателя, которые еще должны быть рефакторинг вручную. Но мы хотели бы использовать проверку границ для разделов, которые были (предположительно) правильно изменены на массивы.

(игнорировать текст курсивом, я держу его только для согласованности в комментариях)я нашел одну проблему до сих пор с chaning каждого массива:sizeof(class containing array) изменится. Это может нарушить код в некоторых циклах, например someclass somearray[100]; //например(sizeof (somearray[0]) == 50) имеет значение true инт указатель = (int)somearray; указатель += 100 ((someclass)указатель) - >doSomething(); ., потому что pointer +=100 не будет указывать на второй элемент, но где-то внутри первого или даже нулевого, я не уверен (не забывайте, что это автоматически декомпилированный код, отсюда уродство).

Я думаю изменить каждый глобальный массив на std:: array и каждый экземпляр доступа к массиву без [] оператор array._Elems.

есть ли какие-либо проблемы это может возникнуть, если я должен изменить глобальные массивы на std:: arrays в коде, таком как этот?

редактировать Ты был прав насчет того, что размер не меняется. У меня была ошибка в функции проверки. Поэтому я расширю вопрос:

безопасно ли менять каждый массив c-style на std:: array?

редактировать Наш текущий код фактически выполняется только в режиме отладки, так как он не перемещает переменные. Выпуск падает режим в основном на старте программы.

редактировать Поскольку существует некоторая путаница в том, о чем этот вопрос, Позвольте мне уточнить: есть ли какая-то гарантия, что в массиве нет другого члена, кроме t elems [N] ? Могу ли я рассчитывать на

array<array<int,10>, 10> varname;
int* ptr = &varname[0][0];
ptr += 10

и убедитесь, что ptr указывает на varname[1][0] независимо от деталей реализации? Хотя гарантируется, что массив является непрерывным, я не уверен в этом. Стандарт содержит реализацию, но я не уверен, что это пример реализации или фактическое определение, которого должна придерживаться каждая реализация с iterator и const_iterator, которые являются единственными вещами, специфичными для реализации, так как только у них есть слова реализация-определено (у меня нет последней спецификации под рукой, поэтому могут быть некоторые другие различия).

3 ответов


для одномерных массивов, это может работать во всех случаях, 2D случае сложнее:

в принципе, шаблон std::array может состоять только из самого массива, поскольку его аргумент length является переменной времени компиляции, которую не нужно хранить. Однако ваша STL-реализация, возможно, решила сохранить ее в любом случае или любые другие данные, которые ей нужны. Итак, в то время как '&a[n] == &a[0] + n 'выполняется для любого массива std::, выражение' &a[n][0] == &a[0][0] + Н*arrayWidth' не на 'с std::массив '.

тем не менее вы можете проверить, является ли 'sizeof(std::array ) == sizeof(int) * 100' с вашей STL-реализацией. Если это так, то должно быть безопасно заменить даже 2D-массивы.


интересно, как эта замена должна работать даже в коде, полном арифметики указателя.

/// @file array_eval.cpp
#include <iostream>
#include <array>
#include <algorithm>


int main() {
    auto dump = [](const int& n){std::cout << n << " ";};

#ifdef DO_FAIL
    std::array<int, 10> arr;
#else    
    int arr[10];
#endif

    // this does not work for std::arrays
    int* p = arr; 

    std::for_each(p, p+10, dump);
    std::cout << std::endl;
    return 0;
}

и

g++ -Wall -pedantic -std=c++11 -DDO_FAIL array_eval.cpp 

конечно, не удается:

array_eval.cpp: In function ‘int main()’:
array_eval.cpp:17:14: error: cannot convert ‘std::array<int, 10ul>’ to ‘int*’ in initialization
     int* p = arr; 
              ^

это зависит от реализации STL. Я имею в виду, стандарт не мешает реализовать std::array использование большего количества членов или резервирование большего объема памяти действительно необходимо (например, для отладки), но я думаю, что очень маловероятно найти один std::array реализация не просто использовать T elem[N]; элемент данных.

если мы предположим, что реализация std:: array включает только одно поле для хранения данных и выделяет только необходимую память (не более), int v[100]; и где данные хранятся в array<int, 100> v; будет иметь тот же макет, так как от стандартного:

[массив.обзор 23.3.2.1 p1]:

элементы массива хранятся смежно, что означает, что если a is Ан array<T, N> тогда он подчиняется идентичности &a[n] == &a[0] + n для всех 0 <= n < N.

и [класс.mem 9.2 p20]:

указатель на объект структуры стандартного макета, соответствующим образом преобразованный с помощью а reinterpret_cast, указывает на его первоначальный член (или если этот член бит-поле, затем к единице, в которой оно находится) и наоборот. [ Примечание: поэтому там может быть безымянный обивка внутри объект структуры стандартной компоновки, но не в его начале, как это необходимо для достижения соответствующего согласования. - конец Примечания ]

в любом случае, это зависит от компилятора и реализации STL. Но обратный код тоже зависит от компилятора. Почему вы предполагаете int a; int b[50]; найти a и затем массив b в памяти в этом порядке, а не в другом, если эти объявления не являются частью struct или class? Компилятор решил бы другое по соображениям производительности (но я вижу, что это маловероятно).