繁体   English   中英

如果在 class 内的构造函数 < 2 中启动空数组 [],为什么会检测到堆栈粉碎

[英]Why is stack smashing detected if an empty array[] is initiated in the constructor < 2 inside a class

我遇到了这个错误...... 在此处输入图像描述

...用这个最小的可执行代码来重现错误和...

#include <iostream>

class arrayClass
{
private:
   int _elements;
   int _array[];

public:
    arrayClass()
        : _elements(32)
    {
        //If i is smaller than 2 the "Stack Smashing Detected" error shows up, why is that?
        //If i is 2 or more, no error appears
        //(f.e. int i = 0 or int i = 1 doesn't work, int i = 2 or higher works - why is that?)
        for(int i = 0; i < _elements; ++i){
            _array[i] = 0;
        }
    }
    int get(int index){
        return _array[index];
    }
};

int main()
{
    arrayClass arr;
    std::cout << arr.get(2) << std::endl;

    return 0;
}

...无论我是使用初始化列表还是使用 fe32 或任何数字的属性本身来启动 _elements 都没有关系。

如果除了 arrayClass() 构造函数之外,我还传递 int 值来构造arrayClass(int) ,则不会出现错误。

如果我只用一个值构造 arrayClas(int),它也可以只与第二个插槽一起使用。

所以我的问题是:为什么我不能启动默认array[]的第一个和第二个数组槽?

或者反过来,为什么可以将一个空array[]分配给没有值的 class 而不是 fe _array[32] 没有错误但分配array[0] = 0; array[1] = 0; ?

(是的,我知道向量,出于各种原因,我需要使用 arrays)

因为你从来没有为数组分配 memory ,所以一切都是未定义的行为。 我什至不知道没有大小说明符的int _array[]会评估什么。 我稍后会查一下。

将您的构造代码更改为“新”数组。 并有一个析构函数来删除它。

class arrayClass
{
private:
   int _elements;
   int* _array;

public:
    arrayClass()
        : _elements(32)
    {
        _array = new int[_elements];
        memset(_array, '\0', sizeof(array[0])*_elements);
    }
    int get(int index){
        return _array[index];
    }

    ~arrayClass()
    {
        delete [] _array;
    }
};

或者,如果您可以拥有固定数量的元素,请在声明数组时明确调整数组的大小:

class arrayClass
{
private:
   int _array[32];

public:
    arrayClass()
        : _array() // this will zero-init the array
    {
    }
    int get(int index){
        return _array[index];
    }
};

int _array[]; 是一个灵活的数组,在标准 C++ 中是不允许的。 它不分配 memory 所以当你访问数组中的任何元素时,你有未定义的行为。

我知道向量,出于各种原因,我需要使用 arrays

实际上,有效的理由很少,所以我认为你提到的各种理由都是人为的。 如果需要将数据传递给 function,如void func(int*, size_t elements); ,您仍然可以使用std::vector<int> 只需将其data()size()作为 arguments 传递。

在 C++ 中,您通常应该在这种情况下使用std::vector<int>

例子:

#include <iostream>
#include <vector>

class arrayClass
{
private:
   std::vector<int> _array;

public:
    arrayClass(size_t s = 32)
        : _array(s)
    {}

    size_t size() const {
        return _array.size();
    }

    int get(size_t index) const {
        return _array[index];
    }
};

int main()
{
    arrayClass arr;
    std::cout << arr.get(10) << std::endl;

    return 0;
}

另一种方法是,如果您的arrayClass具有固定数量的元素:

#include <array>

class arrayClass
{
private:
   std::array<int, 32> _array;

public:
    arrayClass()
        : _array{}
    {}

    size_t size() const {
        return _array.size();
    }

    int get(size_t index){
        return _array[index];
    }
};

如果std::vector消耗的额外空间(通常是 4 或 8 个字节)是一个真正的问题,您可以制作一个类似的 class ,它只存储指向分配的 memory 的指针和大小。 它可能看起来像这样(但没有像vector那样增长/收缩的能力):

#include <iostream>

#include <algorithm>
#include <memory>
#include <type_traits>

template<typename T, std::enable_if_t<std::is_default_constructible_v<T>>* = nullptr>
class arrayClass {
public:
    using value_type = T;

    arrayClass(size_t size = 32) :
        _size(size),
        _array(std::make_unique<T[]>(_size))
    {}

    // copy constructor
    arrayClass(const arrayClass& rhs) :
        _size(rhs._size),
        _array(std::make_unique<T[]>(_size))
    {
        static_assert(std::is_copy_assignable_v<T>, "T must be copy assignable");
        std::copy(rhs._array.get(), rhs._array.get() + _size, _array.get());
    }

    arrayClass(arrayClass&&) = default; // move constructor

    // copy assignment operator    
    arrayClass& operator=(const arrayClass& rhs) {
        *this = arrayClass(rhs); // copy construct and move assign
        return *this;
    }

    arrayClass& operator=(arrayClass&&) = default; // move assignment operator

    // accessing element at index
    T& operator[](size_t index) { return _array[index]; }
    const T& operator[](size_t index) const { return _array[index]; }

    // bounds checking access to element
    T& at(size_t idx) {
        if(idx >= _size) 
           throw std::out_of_range(std::to_string(idx) + ">=" + std::to_string(_size));
        return _array[idx];
    }
    const T& at(size_t idx) const {
        if(idx >= _size)
           throw std::out_of_range(std::to_string(idx) + ">=" + std::to_string(_size));
        return _array[idx];
    }

    size_t size() const { return _size; }

    // support for iterating over the elements in the array
    const T* cbegin() const { return _array.get(); }
    const T* cend() const { return _array.get() + _size; }
    const T* begin() const { return cbegin(); }
    const T* end() const { return cend(); }
    T* begin() { return _array.get(); }
    T* end() { return _array.get() + _size; }   

private:
   size_t _size;
   std::unique_ptr<T[]> _array;
};

using intArray = arrayClass<int>;

int main() {
    try {
        intArray arr1(10);
        // the added iterator support makes range-based for-loops possible:
        for(int& v : arr1) {
            static int i=0;
            v = ++i;
        }

        intArray arr2;
        arr2 = arr1; // copy assign
        for(size_t i=0; i < arr2.size(); ++i) {
            std::cout << arr2[i] << '\n';       // access using operator[] works too
        }    

        std::cout << arr2.at(10) << '\n'; // throws out_of_range exception
    }
    catch(const std::out_of_range& ex) {
        std::cerr << ex.what() << '\n';
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM