是 C 样式数组到 std::array 转换对于数组完全安全

Is a C-Style array to std::array transition completely safe for arrays?

本文关键字:数组 安全 于数组 转换 样式 std array      更新时间:2023-10-16

第一次提问者:)是否可以在不破坏代码的情况下将全局 c 样式数组转换为 std::arrays?我正在做一个项目,其中包括反编译旧游戏的源代码。我们已经设法重构了大部分反汇编/反编译输出。由于它是自动的,因此仍然有这样的部分

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

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

以及其他类型的疯狂指针算法,尚未手动重构。但是我们想对(大概(正确更改为数组的部分使用边界检查。

(忽略斜体文本,我保留它只是为了评论的一致性(到目前为止,我发现追逐每个数组存在一个问题:sizeof(class containing array)会改变。例如,这可能会在某些周期中破坏代码 某类某阵列[100]; 例如 (sizeof(somearray[0]( == 50( 为真 int 指针 = (int(somearray; 指针 += 100 ((someclass(pointer(->doSomething((;.因为pointer +=100不会指向第二个元素,而是指向第一个元素中的某个地方,甚至是零元素,我不确定(不要忘记它是自动反编译的代码,因此很丑陋(。

我正在考虑将每个全局数组更改为 std::array 以及访问数组的每个实例而无需[]运算符来array._Elems

如果我在这样的代码中将全局数组更改为 std::arrays,是否可能会出现任何问题?

编辑你对大小不变是对的。我在测试功能中遇到错误。所以我将扩展这个问题:

将每个 c 样式数组更改为 std::array 是否安全?

编辑我们当前的代码实际上只能在调试模式下运行,因为它不会移动变量。发布模式基本上在程序开始时崩溃。

编辑由于这个问题似乎有些混乱,让我澄清一下:除了 T elems [N] 之外,是否可以保证数组中没有其他成员?我可以指望拥有吗

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

并确保 PTR 指向varname[1][0],而不管实施细节如何?虽然可以保证数组是连续的,但我不确定这一点。该标准包含一个实现,但我不确定这是一个示例实现还是每个实现都应该遵循迭代器的实际定义,const_iterator是唯一特定于实现的东西,因为只有那些有词实现定义(我手头没有最新的规范,所以可能还有其他一些差异(。

对于一维数组,这可能适用于所有情况,2D 情况更棘手:

原则上,std::array <>模板可以只包含数组本身,因为它的长度参数是一个不需要存储的编译时变量。但是,您的 STL 实现可能已经选择存储它或它需要的任何其他数据。因此,虽然 '&a[n

] == &a[0] + n' 适用于任何 std::array,但表达式 '&a[n][0] == &a[0][0] + n*arrayWidth' 可能不适用于 'std::array <std::array,>'。

您可能仍然想检查 'sizeof(std::array <int,>( == 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 一个array<T, N>然后它服从所有0 <= n < N的身份&a[n] == &a[0] + n

和 [class.mem 9.2 p20]:

指向标准布局结构对象的指针,使用 reinterpret_cast 指向其初始成员(或者如果该成员是 位域,然后是它所在的单元(,反之亦然。[ 注意:因此,在 标准布局结构对象,但不是在其开头,根据需要 以实现适当的对齐。—尾注 ]

无论如何,这取决于编译器和 STL 实现。但是反向代码也取决于编译器。为什么假设int a; int b[50];将按该顺序在内存中定位a然后b数组,如果该声明不是structclass的一部分,则不会在另一个顺序中定位?出于性能原因,编译器会决定其他事情(但我认为这不太可能(。