我知道向量存在,但这只是为了练习。我试图增加堆上数组的大小。已经提出了一个与此相关的任务,但我仍然找不到我的错误。
结构是这样的: 堆栈上有 1 个数组,包含 2 个指向 int 数组的指针。这 2 个 int 数组位于堆上。程序检查第一个数组中是否有空空间,如果没有,则将先前存在的元素复制到第二个数组>删除第一个>创建第一个大小大1的元素>将元素复制回来>然后添加最后一个元素。
问题在于添加新元素。错误提示“写入 arr[0] 时缓冲区溢出”。
为什么最后一个元素不在数组边界内? (也在我的代码中,我将 element = 0 视为空。)
#include <iostream>
using namespace std;
int main()
{
cout << "We are trying to create Vector like mechanism.\n \n" << endl;
int* arr[2]{ nullptr }; // declared an array which contains ptrs
arr[0] = new int[3] {}; // making 1st array on heap
for (int i{1}; i > 0; i++) {
char c1; // choice 1
cout << "Do you want to add elements : "; cin >> c1;
if (c1 == 'y' || c1 == 'Y') {
int e; // element to be entered
bool a{false}; // this is a checkbox that the element is succesfully entered or not
int n = sizeof(arr[0]) / sizeof(int); // num of elements in arr[0]
for (int j{}; j < n; j++) {
if ((arr[0])[j] == 0) { // to check wether the arrays have vacancy or not
cout << "Enter the element to be entered : "; cin >> e;
(arr[0])[j] = e;
a = true;
break;
}
}
if (a == false) {
// copy 1 to 2 -> delete 1 > create new at 1 > copy 2 to 1 > add new element to 1 > delete 2
arr[1] = new int[n]; // creating 2nd array
for (int k{}; k < n; k++) { // copying the elements
(arr[1])[k] = (arr[0])[k];
}
delete[] arr[0]; // deleting 1st
arr[0] = new int[n + 1] {}; // recreating 1st
for (int l{}; l < n; l++) { // copying elements back to 1st
(arr[0])[l] = (arr[1])[l];
}
for (int t{}; t < n + 1; t++) {
cout << (arr[0])[t] << " ";
}
delete[] arr[1]; // deleting 2nd array
cout << "Enter the element to be entered : "; cin >> e;
(arr[0])[n + 1] = e; // inputing the new element
a = true;
}
}
else if (c1 == 'n' || c1 == 'N') {
cout << "OK, no new element was entered." << endl;
break;
}
else {
cout << "Enter a valid choice. ie.( Y/y for YES and N/n for NO)" << endl;
}
}
delete[] arr[0]; delete[] arr[1];
}
从问题中的代码来看,OP还没有研究过类。
当他这样做时,他会发现下面的函数与我在评论中引用的视频中描述的成员函数非常相似。
Arthur O'Dwyer 在 CppCon 2019 的精彩演讲中解释了您需要了解的所有内容,并提供了“自己动手”向量类的源代码:回到基础知识:RAII 和零规则.
令我印象深刻的是,相对较新的 C++ 程序员有兴趣了解动态分配的数组。诚然,这是一个有点高级的话题。此外,在生产代码中,您不应该重新发明轮子。
std::vector
班做得比你可能做得更好。
也就是说,学习它是如何完成的是值得的。迟早,所有 C++ 程序员都会尝试这一点。
OP中的代码与O'Dwyer教授的内容相去甚远,我没有尝试“纠正”它。此外,我不会展示如何使用 RAII 类来完成此操作,这是 O'Dwyer 演讲的主题。相反,我尝试坚持使用 OP 已经理解的功能类型。
下面的程序中有很多解释性的注释。特别注意功能
my_vec_resize
。它解释了必须使用的谨慎顺序来安全地增加(或减少)数组的大小。
// main.cpp
#include <algorithm> // copy, equal, min, swap
#include <cstddef> // size_t
#include <iostream> // cin, cout
struct my_vec
{
int* data{ nullptr };
std::size_t size{};
};
my_vec my_vec_construct(std::size_t const n)
{
// Analogous to "constructor" function of a class.
my_vec v;
v.data = new int[n] {}; // could throw `std::bad_alloc`
v.size = n;
return v;
}
void my_vec_destruct(my_vec& v)
{
// Analogous to "destructor" function of a class.
delete[] v.data;
v.data = nullptr;
v.size = 0u;
}
my_vec my_vec_clone(my_vec const& source)
{
// Return a "deep" copy of the argument `source`.
// Analogous to "copy constructor" function of a class.
my_vec target{ my_vec_construct(source.size) };
// Use the Standard Library function `std::copy`, to copy the elements.
// It has three arguments:
// 1. pointer to beginning of source data
// 2. pointer to "one-beyond-the-last" source data
// 3. pointer to target
// These pointers can also be "iterators," but that's another subject.
std::copy(source.data, source.data + source.size, target.data);
return target;
}
void swap(my_vec& a, my_vec& b) noexcept
{
using std::swap;
swap(a.data, b.data);
swap(a.size, b.size);
}
void my_vec_assign(my_vec const& source, my_vec& target)
{
// Use the "copy-and-swap" idiom to assign a copy of argument `source`
// to the second argument `target`.
// Analogous to "copy-assignement" operator of a class.
//
// Note: the "cloning" operation could throw `std::bad_alloc`.
// When it does, we leave `target` unalterred.
my_vec tmp{ my_vec_clone(source) };
swap(tmp, target);
my_vec_destruct(tmp); // avoid memory leaks!
}
void my_vec_resize(my_vec& v, std::size_t const new_size)
{
// Analogous to "resize" function of class `std::vector`.
// It is possible that the allocation will fail. That is why
// we attempt the allocation first, before doing anything else.
// If it fails, we leave the original vector unalterred.
//
// When operator `new` fails, it will throw a `std::bad_alloc`.
// That will interrupt this function, and probably cause the
// program to abort. Otherwise, when the allocation works, we
// continue execution below, where data elements are copied.
auto p{ new int[new_size] {} }; // elements are initialized to 0.
// Allocation succeeded. Copy the elements.
// We use `std::min`, in case the `new_size` is smaller
// than the old size.
std::size_t size_to_copy{ std::min(v.size, new_size) };
std::copy(v.data, v.data + size_to_copy, p);
// Important: avoid memory leaks. Delete the existing array.
// Be sure to use the array form of operator `delete`.
delete[] v.data;
// Now, you can set `v.data` to point to the new array.
v.data = p;
p = nullptr; // optional, `p` is a "local" variable that will be discarded anyway.
// Don't forget to update the size.
v.size = new_size;
}
void my_vec_grow(my_vec& v) {
// Double the size of v.
std::size_t new_size{ v.size == 0u ? 2u : v.size + v.size };
my_vec_resize(v, new_size);
}
bool my_vec_empty(my_vec const& v) noexcept {
return v.size == 0u;
}
bool operator==(my_vec const& a, my_vec const& b) noexcept {
// `std::equal` has the same three arguments as `std::copy`.
return a.size == b.size
&& std::equal(a.data, a.data + a.size, b.data);
}
bool operator!=(my_vec const& a, my_vec const& b) noexcept {
return !(a == b);
}
std::ostream& operator<<(std::ostream& ost, my_vec const& v)
{
ost.put('[');
if (!my_vec_empty(v)) {
ost << v.data[0u];
for (std::size_t i{ 1u }; i < v.size; ++i) {
ost << ',' << v.data[i];
}
}
ost.put(']');
return ost;
}
int main()
{
// Constructor sets all elements to 0.
my_vec a{ my_vec_construct(2) };
std::cout << "Post construction - a: " << a << "\n\n";
// Assign a couple of values
a.data[0] = 7;
a.data[1] = 11;
std::cout << "Post assignment - a: " << a << "\n\n";
// Try cloning.
auto b{ my_vec_clone(a) };
std::cout << "Clone - b: " << b << "\n\n";
// Resize b.
my_vec_resize(b, 4);
std::cout << "Post resize - b: " << b << "\n\n";
// Check whether a == b.
std::cout << "Check equality (a == b): " << a << (a == b ? " == " : " != ") << b << "\n\n";
// Try assignment (a is target)
my_vec_assign(b, a);
std::cout << "Post assignment (a = b) - a: " << a << "\n\n";
// Check whether a == b.
std::cout << "Check equality (a == b): " << a << (a == b ? " == " : " != ") << b << "\n\n";
// IMPORTANT: Don't leak memory.
my_vec_destruct(a);
my_vec_destruct(b);
std::cout << "Post destruct - a: " << a << ", b: " << b << "\n\n";
return 0;
}
// end file: main.cpp
Post construction - a: [0,0]
Post assignment - a: [7,11]
Clone - b: [7,11]
Post resize - b: [7,11,0,0]
Check equality (a == b): [7,11] != [7,11,0,0]
Post assignment (a = b) - a: [7,11,0,0]
Check equality (a == b): [7,11,0,0] == [7,11,0,0]
Post destruct - a: [], b: []