You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言Vector实现erase元素后大小与遍历输出不符问题排查

问题:C实现类C++ Vector时erase操作后遍历出现重复元素

我尝试“回归基础”,用C语言实现一个类C++的Vector,采用void*存储数据。但在删除元素时遇到问题:多次执行erase操作后,Vector的size值显示正确,但遍历输出时会多一个元素(出现重复的J)。

相关函数实现

typedef void* vector_iterator;

vector_iterator vector_begin(vector* vec) {
    return vec->data;
}

vector_iterator vector_end(vector* vec) {
    return ((unsigned char*)vec->data) + ((vec->element_size * (vec->size+1))); // "past the last element"
}

void vector_erase(vector* vec, vector_iterator iterator) {
    assert(iterator >= vector_begin(vec));
    assert(iterator < vector_end(vec));
    assert(((uintptr_t)iterator - (uintptr_t)vector_begin(vec)) % vec->element_size == 0);
    unsigned char* dest = (unsigned char*)iterator;
    unsigned char* src = dest + vec->element_size; // src is the element erased element + 1, since we want to pull all objects forward
    size_t bytes_to_copy= (unsigned char*)vector_end(vec) - (unsigned char*)src - vec->element_size;
    memcpy(dest, src, bytes_to_copy); // copy all elements from (iterator +1) forward
    vec->size--;
}

vector_iterator vector_iterator_offset(vector_iterator iterator,vector* vec, ptrdiff_t offset) {
    return (unsigned char*)iterator + (vec->element_size * offset);
}

使用方式

vector_erase(vec, vector_iterator_offset(vector_begin(vec),vec,2)); // erase the second element

测试代码及输出

vector* vec = vector_create_capacity(sizeof(char), 10);
//test for push back
for(char i = 'A'; i < 'A'+10; ++i) {
    vector_push_back(vec, &i);
}

//...//

fprintf(stdout, "Size: %zu\n",vector_size(vec));
fflush(stdout);

vector_erase(vec, vector_iterator_offset(vector_begin(vec),vec,2)); // erase 'C'
fprintf(stdout, "Size: %zu\n",vector_size(vec));
fflush(stdout);

vector_erase(vec, vector_iterator_offset(vector_begin(vec),vec,2)); // erase 'D'
fprintf(stdout, "Size: %zu\n",vector_size(vec));
fflush(stdout);

vector_erase(vec, vector_iterator_offset(vector_begin(vec),vec,2)); // erase 'E'
fprintf(stdout, "Size: %zu\n",vector_size(vec));
fflush(stdout);

it = vector_begin(vec);
for(;it != vector_end(vec); (it = vector_iterator_offset(it, vec, 1))) {
    char data = *(char*)it;
    fprintf(stdout,"%c\n", data);
    fflush(stdout);
}
fprintf(stdout,"%zu",vector_size(vec));
fflush(stdout);
//8 printed letters instead of 7 with double 'J'?
vector_destroy(vec);

输出结果

A
B
F
G
H
I
J
J

问题原因及修复方案

问题出在vector_end的实现和erase函数的字节计算两处:

  1. vector_end的错误:
    C++风格的end迭代器应该指向最后一个元素的下一个位置,正确计算应为data + element_size * size。你当前的代码写成了element_size * (size+1),导致end的位置多偏移了一个元素的大小,遍历循环会多执行一次,读到内存中未被更新的旧数据(也就是之前的最后一个元素J)。

修正后的vector_end:

vector_iterator vector_end(vector* vec) {
    return ((unsigned char*)vec->data) + (vec->element_size * vec->size); // 正确指向最后一个元素的下一位
}
  1. erase函数中bytes_to_copy的计算错误:
    原代码中bytes_to_copy的计算依赖错误的vector_end,再额外减去element_size,会导致需要复制的字节数少了一个元素的大小,前移操作不完整。正确的计算应该是从src到正确end位置的总字节数,也就是:
size_t bytes_to_copy = (unsigned char*)vector_end(vec) - (unsigned char*)src;

或者用元素数量计算更直观:

// 计算当前迭代器对应的元素索引
size_t idx = ((uintptr_t)iterator - (uintptr_t)vec->data) / vec->element_size;
// 需要前移的元素数量:总元素数 - 当前索引 - 1
size_t elements_to_move = vec->size - idx - 1;
size_t bytes_to_copy = elements_to_move * vec->element_size;

同时修正这两处后,遍历循环会正确终止,erase操作也会完整前移后续元素,就不会出现重复的J了。

内容的提问来源于stack exchange,提问作者user3001150

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 15:54:56