如何在超出大小的情况下向指针数组中添加元素(C++)

8
假设我们创建了动态分配的内存,例如:
int SIZE = 10;
int *p = new int[SIZE];
for(int i = 0; i < SIZE; ++i)
  p[i] = i;

这段代码会将数字0到9赋值给指针数组。
然后我想在数组中加入10、11、12。
我可以这样做吗:

p[10] = 10;
p[11] = 11;
p[12] = 12;

或者我应该怎么做:
delete[] p;
size = 13;
p = new int[SIZE];
for(int i = 0; i < SIZE; ++i)
  p[i] = i;

13
既然这是 C++,为什么不直接使用 vector<int> 呢? - Tanveer Badar
1
这完全没有帮助。我认为如果问题涉及使用数组,那么这个建议作为“另外一个...”的附加答案是好的,但是这个问题非常具体,关于动态数组。SO现在正在赚取它的刻板印象。 - sweenish
1
要调整数组大小,您必须分配一个新数组并将旧元素复制到新数组中,然后删除旧数组。https://dev59.com/CJ_ha4cB1Zd3GeqPuCf7 - mfnx
4
@Sweenish -- 评论区是用来发表评论的,而不是回答。我认为在评论中解释为什么没有使用“vector”是没有问题的。 - PaulMcKenzie
3
只有当内存使用malloc()calloc()realloc()分配时才能这样做。但是在C++中,由于各种原因,不建议使用这些函数,特别是如果用于为具有非平凡构造函数或析构函数的C ++类类型分配内存时,则会导致未定义行为。 - Peter
显示剩余14条评论
3个回答

7
你需要为数组重新分配更大的内存空间。否则,程序将具有未定义的行为。
例如:
int SIZE = 10;
int *p = new int[SIZE];
for(int i = 0; i < SIZE; ++i)
  p[i] = i;

int *tmp = new int[SIZE + 3];

std::copy( p, p + SIZE, tmp );
delete []p;
p = tmp;

p[SIZE++] = 10;
p[SIZE++] = 11;
p[SIZE++] = 12;

或者你可以写成以下三条语句的替代形式。
for ( const int &value : { 10, 11, 12 } ) p[SIZE++] = value;

当然,在这种情况下最好使用标准容器std::vector

实际上,上面的代码类似于以下代码:

#include <vector>

//...

std::vector<int> v( 10 );

for ( int i = 0; i < v.size(); i++ ) v[i] = i;

v.reserve( 13 );
for ( const int &value : { 10, 11, 12 } ) v.push_back( value );

除了所有的内存管理都由向量内部完成外,没有其他不同之处。

1
看到 p[SIZE++] 时感到厌恶,这是不对的吗? - nada
由于我们无论如何都要删除旧数组,因此我更喜欢使用std::move而不是std::copy——虽然对于整数来说这并不相关,但对于复杂类型(如std::string)可能会有很大的区别... - Aconcagua
@Aconcagua 我同意你的观点。 - Vlad from Moscow
嗯,并不一定等价,构造函数可能会决定从一开始就保留更多的元素,这种情况下可能不会发生重新分配。而且,如果可能的话,std::vector 移动... - Aconcagua

3
第二个选项是正确的方式。第一个选项不会总是出现问题,但你正在写入没有为数组分配的内存;它已经被分配给其他内容,而你不知道是什么。有时它会表现得很好,有时它不会。对数组超出其范围进行赋值是未定义行为,我们应该避免这种情况。

第二个选项是错误的,因为它混淆了 sizeSIZE - stark
我认为这只是一个简单的打字错误,但提出来也是好的。 - sweenish

3
在您的第一个示例建议中:
p[10] = 10;
p[11] = 11;
p[12] = 12;

您将覆盖您不拥有的内存,这可能会导致崩溃。您需要重新分配您的原始数组。

const int oldSize = SIZE;
SIZE = 13;
int *newP = new int[SIZE];
memcpy(newP, p, oldSize * sizeof(int));
for (int i = oldSize; i < SIZE; ++i)
{
    newP[i] = i;
}

delete[] p;
p = newP;

你的第二个示例可以工作,但是效率略低,因为每次重新分配时都要重新计算值。在我上面的示例中,你只需要重新计算新值。

另外,你应该看一下std::vector,它专门设计用于此目的(动态大小数组)。

std::vector<int> p;
for (int i = 0; i < 10; ++i)
{
    p.push_back(i);
}

这里的std::vector可以自动管理内存分配,因此你不必担心使用newdelete

4
附注:如果涉及到复杂对象(如std::string),通常情况下会使 memcpy 无效。为了保持一致性,即使在某些类型中允许使用 memcpy,我也会使用 C++ 提供的方式 std::copy,而避免使用 memcpy - Aconcagua
是的,我同意,我回答的原意是为了保留问题的“风格”(即原始指针和手动内存管理),但是是的,应该避免使用memcpy。 - Mark Ingram

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接