【问题标题】:Warning: argument 1 value ‘18...615’ exceeds maximum object size警告:参数 1 值“18...615”超过最大对象大小
【发布时间】:2022-07-20 16:37:34
【问题描述】:

我最近了解了链接时间优化in a C++ Weekly episode。我认为在我的代码中使用 LTO 是个好主意,所以我决定使用标志 -flto-fno-fat-lto-objects。不幸的是,我开始收到此警告

data_array.hpp:29:50: warning: argument 1 value ‘18446744073709551615’ exceeds maximum object size 9223372036854775807 [-Walloc-size-larger-than=]
   29 |                 m_data = m_size == 0 ? nullptr : new T[m_size];
      |                                                  ^
data_array.hpp: In function ‘main’:
/usr/include/c++/11/new:128:26: note: in a call to allocation function ‘operator new []’ declared here
  128 | _GLIBCXX_NODISCARD void* operator new[](std::size_t) _GLIBCXX_THROW (std::bad_alloc)
      |                          ^

我在 Ubuntu 20.04 上使用 g++(版本 g++ (Ubuntu 11.1.0-1ubuntu1~20.04) 11.1.0)。

我正在使用的实际代码是here,但是它太大了,我为这篇文章准备了一个工作示例(见下文),它重新创建了原始上下文。

问题

  • 有人可以帮我理解为什么会出现这个警告吗?我认为,当代码合并时,编译器认为new 是用未初始化的值调用的。但这不是真的:数组的内存总是分配有m_size 初始化的,无论是正好为0(由空构造函数确保)还是用户提供的其他值,都应该初始化(如下面的代码所示)。

  • 只有在初始化data_array<data_array<int>> 类型的对象时才会出现警告。内部的data_array<int> 应该用m_size=0 初始化,因为这是空构造函数的作用。但是在初始化data_array<int> 类型的对象时不会出现警告。为什么?

  • 有人能告诉我这是否会导致未定义的行为,即使 m_size 实际初始化了?我通常会努力以一种从未发出警告的方式编写代码。我应该忽略这个警告,还是添加一个简单地使其静音的标志?如果没有,我该如何解决?

重现代码

首先,包含警告中的行的类是这个 C 数组的自定义实现。

#pragma once
#include <algorithm>

template <typename T>
struct data_array {
public:
    data_array() noexcept = default;
    data_array(const std::size_t n) noexcept { m_size = n; alloc_data(); }
    ~data_array() noexcept { clear(); }
    void clear() noexcept {
        delete[] m_data;
        m_size = 0;
        m_data = nullptr;
    }
    void resize(std::size_t new_size) noexcept {
        if (new_size != m_size or m_data == nullptr) {
            delete[] m_data;
            m_size = new_size;
            alloc_data();
        }
    }
    std::size_t size() const noexcept { return m_size; }
    T& operator[] (const std::size_t i) noexcept { return m_data[i]; }
    const T& operator[] (const std::size_t i) const noexcept { return m_data[i]; }

private:
    void alloc_data() noexcept {
        m_data = m_size == 0 ? nullptr : new T[m_size];
    }
protected:
    T *m_data = nullptr;
    std::size_t m_size = 0;
};

(我将代码压缩了一点,我通常不会这样编码)。 包含 main 的文件如下:

#include <iostream>

#include "data_array.hpp"

class complicated_object_base {
public:
    std::size_t get_size1() const noexcept { return m_vec1.size(); }
    void set_size1(std::size_t size) noexcept { m_vec1.resize(size); }
private:
    std::vector<std::size_t> m_vec1;
};

class complicated_object : public complicated_object_base {
public:
    std::size_t get_size2() const noexcept { return m_vec2.size(); }
    void set_size2(std::size_t size) noexcept { m_vec2.resize(size); }
private:
    std::vector<std::size_t> m_vec2;
};

class my_class {
public:
    my_class(const complicated_object& co) noexcept
        : m_co(co),
          m_data(m_co.get_size1()),
          m_data2(m_co.get_size1())
    { }
    ~my_class() noexcept { }
    void initialize() noexcept {
        for (std::size_t i = 0; i < m_data.size(); ++i) {
            m_data2[i] = i;
            m_data[i].resize(m_co.get_size2());
            for (std::size_t j = 0; j < m_data[i].size(); ++j) {
                m_data[i][j] = 3;
            }
        }
    }
    std::size_t get_sum() const noexcept {
        std::size_t S = 0;
        for (std::size_t i = 0; i < m_data.size(); ++i) {
            S += m_data2[i];
            for (std::size_t j = 0; j < m_data[i].size(); ++j) {
                S += m_data[i][j];
            }
        }
        return S;
    }
private:
    const complicated_object& m_co;
    data_array<data_array<std::size_t>> m_data;
    data_array<std::size_t> m_data2;
};

int main() {
    complicated_object co;
    co.set_size1(100);
    co.set_size2(234);
    my_class mc(co);
    mc.initialize();
    std::cout << mc.get_sum() << '\n';
}

生成文件

CXXFLAGS = -pipe -std=c++17 -fPIC -fopenmp -flto -fno-fat-lto-objects -O3 -Wall -Wextra -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wunused -Woverloaded-virtual -Wpedantic -Wconversion -Wsign-conversion -Wnull-dereference -Wdouble-promotion -Wformat=2 -Wduplicated-cond -Wduplicated-branches -Wlogical-op -Wuseless-cast -Wrestrict -UDEBUG -DNDEBUG -fstrict-aliasing -D_REENTRANT -fPIC
LFLAGS = -fPIC -O3 -flto -fno-fat-lto-objects -DNDEBUG -UDEBUG -Wl,-O3
main: main.o
    g++ $(LFLAGS) -o main main.o
main.o: main.cpp data_array.hpp
    g++ $(CXXFLAGS) -c main.cpp
clean:
    rm -f main *.o

【问题讨论】:

  • 您是否从 MWE 收到同样的警告?这里没有警告:godbolt.org/z/vbn6hhfvE
  • 顺便说一句,M 代表 Minimal,当警告在 m_data = m_size == 0 ? nullptr : new T[m_size]; 行中时,不需要更多代码来重新创建警告
  • @463035818_is_not_a_number 它确实重现。 godbolt.org/z/9fdvxqn7e
  • @463035818_is_not_a_number 该警告仅在使用 LTO 时出现。没有它就没有警告。此外,如果没有 complicated_object 类中的继承,我无法重新创建警告。这激发了(相当)长的“​​M”WE
  • @Nimrod 谢谢。我将编译器更改为12.1,它没有出现。也许 11.1 有错误?

标签: c++ c++17 compiler-optimization


【解决方案1】:

TLDR:您需要将 data_array 的默认构造函数修复为:

struct data_array {
public:
    data_array() m_data(nullptr), m_size(0)
    {
    }

否则,当您实例化 my_class 时,这会发生在其构造函数初始化列表中。

m_data(m_co.get_size1()),

起初看起来不错,直到我发现 m_data 是一个数组数组。

data_array<data_array<std::size_t>> m_data;

因此,在my_class 构造函数中m_data 的第一次初始大小调整中,每个单独的data_array 实例都有一个指针和一个具有未定义内容 的大小成员。一旦这些实例中的任何一个尝试被破坏(或在这些内部实例上调用任何其他成员函数),您就会有未定义的行为。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-28
    • 1970-01-01
    • 1970-01-01
    • 2021-08-23
    • 1970-01-01
    • 2021-06-15
    • 2020-01-11
    相关资源
    最近更新 更多