Является ли массив 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]:
Элементы массива хранятся смежно, что означает, что если a
a 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
? Компилятор решит другую вещь по соображениям производительности (но я вижу, что это маловероятно).