qt初入门0:结构体中QString用memset导致崩溃分析及QLatin1String简单查看源码

初识Qt,进行开发时遇到一个崩溃问题 简单整理

1:问题描述如下,结构体中QString成员,然后对结构体调用了memset导致问题:

2:问题分析,加断点调试的方式可以明确分析到行数

可以明确看出,初始化时成员变量的值为空的字符串,然后执行memset后,该成员地址无法访问。

3:找源码进行分析一下

3.1.这里首先测试一下string等其他成员操作,没有出现问题。

3.2 分析QString的源码,了解原因(qtbase源码分析)。

简化后源码如下

c++ 复制代码
const QArrayData QArrayData::shared_null[2] = {{ -1, 0, 0, 0, sizeof(QArrayData) }, }
inline QString::QString() noexcept : d(Data::sharedNull()) {}
inline QString::~QString() { if (!d->ref.deref()) Data::deallocate(d); }

class Q_CORE_EXPORT QString
{
public:
    typedef QStringData Data; //基类是 QArrayData
    inline QString() noexcept;
private:
    Data *d; //基类是 QArrayData 控制引用计数
public:
    typedef Data * DataPtr;
    inline DataPtr &data_ptr() { return d; }
};

QString::QString(const QChar *unicode, int size)
{
   if (!unicode) {
        d = Data::sharedNull();
    } else {
        if (size < 0) {
            size = 0;
            while (!unicode[size].isNull())
                ++size;
        }
        if (!size) {
            d = Data::allocate(0);
        } else {
            d = Data::allocate(size + 1);
            Q_CHECK_PTR(d);
            d->size = size;
            memcpy(d->data(), unicode, size * sizeof(QChar));
            d->data()[size] = '\0';
        }
    }
}

第一个分析点(构造函数):

第二个分析点,真正控制QString的数据体,只能指针+数据域

实际真正的底层是QArrayData

c++ 复制代码
struct Q_CORE_EXPORT QArrayData
{
    QtPrivate::RefCount ref; //基类实现原子变量的递增递减
    int size;
    uint alloc : 31;
    uint capacityReserved : 1;
    qptrdiff offset; // in bytes from beginning of header
    void *data() {
        Q_ASSERT(size == 0  || offset < 0 || size_t(offset) >= sizeof(QArrayData));
        return reinterpret_cast<char *>(this) + offset;
    }
    static const QArrayData shared_null[2];
    static QArrayData *sharedNull() noexcept { return const_cast<QArrayData*>(shared_null); }
};

//真正的申请内存
QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment,
        size_t capacity, AllocationOptions options) noexcept
{
    Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData)            && !(alignment & (alignment - 1)));
    if (!(options & RawData) && !capacity) {
        return const_cast<QArrayData *>(&qt_array_empty);
    }

    size_t headerSize = sizeof(QArrayData);

    if (!(options & RawData))
        headerSize += (alignment - Q_ALIGNOF(QArrayData));

    if (headerSize > size_t(MaxAllocSize))
        return nullptr;

    size_t allocSize = calculateBlockSize(capacity, objectSize, headerSize, options);
    QArrayData *header = static_cast<QArrayData *>(::malloc(allocSize));
    if (header) {
        quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1)
                & ~(alignment - 1);

        header->ref.atomic.storeRelaxed(1);
        header->size = 0;
        header->alloc = capacity;
        header->capacityReserved = bool(options & CapacityReserved);
        header->offset = data - quintptr(header);
    }

    return header;
}

//真正的释放内存 根据引用计数控制
void QArrayData::deallocate(QArrayData *data, size_t objectSize, size_t alignment) noexcept
{
    // Alignment is a power of two
    Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData)  && !(alignment & (alignment - 1)));
    Q_UNUSED(objectSize) Q_UNUSED(alignment)
    Q_ASSERT_X(data == 0 || !data->ref.isStatic(), "QArrayData::deallocate",
               "Static data cannot be deleted");
    ::free(data);
}

4:为什么string成员没有问题呢(分析QLatin1String)

这里在看QString源码时看到了QLatin1String类 区分QString进行了简单查看。

首先:QLatin1String作为结构体成员,也没有问题。

4.1 简单查看QLatin1String的源码

c++ 复制代码
class QLatin1String
{
public:
    Q_DECL_CONSTEXPR inline QLatin1String() noexcept : m_size(0), m_data(nullptr) {}
    Q_DECL_CONSTEXPR inline explicit QLatin1String(const char *s) noexcept : m_size(s ? int(strlen(s)) : 0), m_data(s) {}
    Q_DECL_CONSTEXPR explicit QLatin1String(const char *f, const char *l)
        : QLatin1String(f, int(l - f)) {}
    Q_DECL_CONSTEXPR inline explicit QLatin1String(const char *s, int sz) noexcept : m_size(sz), m_data(s) {}
    inline explicit QLatin1String(const QByteArray &s) noexcept : m_size(int(qstrnlen(s.constData(), s.size()))), m_data(s.constData()) {}

    //。。。其他都是基本的函数接口
    Q_DECL_CONSTEXPR const char *latin1() const noexcept { return m_data; }
    Q_DECL_CONSTEXPR int size() const noexcept { return m_size; }
    Q_DECL_CONSTEXPR const char *data() const noexcept { return m_data; }

    Q_DECL_CONSTEXPR bool isNull() const noexcept { return !data(); }
    Q_DECL_CONSTEXPR bool isEmpty() const noexcept { return !size(); }

private:
    int m_size;
    const char *m_data;
};

4.2 简单分析 QLatin1String 就是对char*的简单封装

4.2 细节,没看到有operator= 使用赋值构造也没有问题

这是因为用了浅拷贝,内部没涉及内存的重新申请 所以也没问题。

然后因为demo中x1重新赋值,浅拷贝导致x1中data指向的const char*指向发生变化。

总结:QLatin1String只是对char*的简单封装,不涉及内存的申请,实际还是需要我们控制。

遗留:过程中看到QString中相关的编码相关函数,这块也是一个细节,待整理

相关推荐
用户805533698034 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner4 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz9 天前
QML Hello World 入门示例
qt
xcyxiner12 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner13 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner13 天前
DicomViewer (添加模型类)3
qt
xcyxiner14 天前
DicomViewer (目录调整) 2
qt
xcyxiner14 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00616 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术16 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript