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

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

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

или

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

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

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

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

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

Edit Вы были правы в том, что размер не меняется. У меня была ошибка в функциях тестирования. Поэтому я разберу вопрос:

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

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

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

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

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

Ответы

Ответ 1

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

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

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

Ответ 2

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

/// @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; 
              ^

Ответ 3

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

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

[array.overview 23.3.2.1 p1]:

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

и [class.mem 9.2 p20]:

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

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