IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> GCC9.4 memset() clearing an object of type with no trivial copy-assignment [-Werror=class-memaccess] -> 正文阅读

[系统运维]GCC9.4 memset() clearing an object of type with no trivial copy-assignment [-Werror=class-memaccess]

编译环境

lm@lm:~$ cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04.4 LTS (Focal Fossa)"
PRETTY_NAME="Ubuntu 20.04.4 LTS"
HOME_URL="https://www.ubuntu.com/"


lm@lm:~$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:hsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 9.4.0-1ubuntu1~20.04.1' --with-bugurl=file:///usr/share/doc/gcc-9/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,gm2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-9 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-9-Av3uEd/gcc-9-9.4.0/debian/tmp-nvptx/usr,hsa --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1)

编译问题

./src/Control.cc:128:62: error: ‘void* memset(void*, int, size_t)’ clearing an object of type ‘Context_Struct’ {aka ‘struct Context_Struct’} with no trivial copy-assignment; use assignment or value-initialization instead [-Werror=class-memaccess]
  128 |     memset(&createInfo, 0, sizeof(Context_Struct));
      |                                                              ^
In file included from /repos/src/Trans.h:5,
                 from ./inter/Control.h:13,
                 from ./inter/Control.cc:1:
./src/inter/interfaceType.h:146:20: note: ‘Context_Struct’ {aka ‘struct Context_Struct’} declared here
  146 |     typedef struct Context_Struct
      |                    ^~~~~~~~~~~~~~~~~~
cc1plus: all warnings being treated as errors

关键源码

部分关键源码如下:

125   void Control::handleCContext()
126   {
127      Context_Struct createInfo;
128      memset(&createInfo, 0, sizeof(Context_Struct));
129      
130      ......
147
148   }

从编译错误提示可以很明显看出是 memset(&createInfo, 0, sizeof(Context_Struct)); 这一行有问题。

寻找问题

关于GCC warning 解决办法可以出门左转查看: GCC 使用指南(gcc 9.4 compiling、warning 解决办法)

这里直接查看 GCC 9.4 官网的编译选项 3.5 Options Controlling C++ Dialect 原文摘录如下:

-Wclass-memaccess (C++ and Objective-C++ only)
?
? ? Warn when the destination of a call to a raw memory function such as memset or memcpy is an object of class type, and when writing into such an object might bypass the class non-trivial or deleted constructor or copy assignment, violate const-correctness or encapsulation, or corrupt virtual table pointers. Modifying the representation of such objects may violate invariants maintained by member functions of the class. For example, the call to memset below is undefined because it modifies a non-trivial class object and is, therefore, diagnosed. The safe way to either initialize or clear the storage of objects of such types is by using the appropriate constructor or assignment operator, if one is available.
?
? ? ? std::string str = “abc”;
? ? ? memset (&str, 0, sizeof str);
?
? ? The -Wclass-memaccess option is enabled by -Wall. Explicitly casting the pointer to the class object to void * or to a type that can be safely accessed by the raw memory function suppresses the warning.

附上中文翻译结果:

-Wclass-memaccess(仅限 C++ 和 Objective-C++)
?
? ? 当调用原始内存函数(如 memset 或 memcpy)的目标是类类型的对象时发出警告,并且当写入此类对象时可能会绕过类非平凡或已删除的构造函数或复制赋值,违反 const 正确性或封装或损坏的虚拟表指针。修改此类对象的表示可能会违反由类的成员函数维护的不变量。例如,下面对 memset 的调用是未定义的,因为它修改了一个非平凡的类对象,因此被诊断出来。初始化或清除此类对象存储的安全方法是使用适当的构造函数或赋值运算符(如果可用)。
?
? ? ? std::string str = “abc”;
? ? ? memset (&str, 0, sizeof str);
?
? ? -Wclass-memaccess 选项由 -Wall 启用。将指向类对象的指针显式转换为 void * 或原始内存函数可以安全访问的类型会抑制警告。

解决方案

知道了问题所在,解决问题的方式就比较清楚了。

方案一

最简单粗暴的方式,就是忽略编译告警的方式,即通过在编译选项中添加 -Wno-error=class-memaccess 方式。
具体修改方式为:

  1. 找到引起告警的产品代码源文件所在的 makefile 文件(或者 cmake);
  2. 查看编译选项(一般是makefile文件)xx_xx_CPPFLAGS(x_xx是最终会生成的 lib 库)
  3. 添加或者追加编译选项 xx_xx_CPPFLAGS := -Wno-error=class-memaccess
  4. 重新编译产品代码

当然,使用此种方案,会在编译的 log 中看到下面的 warning 提示信息:

./src/Control.cc: In member function ‘void Control::handleContext(CallData*, const CreateData*, CreatedData*)’:
./inter/Control.cc:128:62: warning: ‘void* memset(void*, int, size_t)’ clearing an object of type ‘Context_Struct’ {aka ‘struct Context_Struct’} with no trivial copy-assignment; use assignment or value-initialization instead [-Wclass-memaccess]
  128 |     memset(&createInfo, 0, sizeof(Context_Struct));
      |                                                              ^
In file included from ./src/inter/Trans.h:5,
                 from ./inter/Control.h:13,
                 from ./inter/Control.cc:1:
./src/inter/interfaceType.h:146:20: note: ‘Context_Struct’ {aka ‘struct Context_Struct’} declared here
  146 |     typedef struct Context_Struct
      |                    ^~~~~~~~~~~~~~~~~~

方案二

GCC 从 4.3 开始就支持 C++11,而我们的编译选项中也添加有 C++11 的编译选项。通过查看 C++11 Support in GCC 中的 Initializer lists 示例我们看到有更加简单的初始化方式:

 int* e {};           // initialization to zero / null pointer
 int** pp {};         // initialized to null pointer
 
 struct S {
    // no initializer-list constructors
    S(int, double, double);       // #2
    S();                          // #3
    // ...
  };
  S s1 = { 1, 2, 3.0 };          // ok: invoke #2
  S s2 { 1.0, 2, 3 };            // error: narrowing
  S s3 { };                      // ok: invoke #3

  struct S2 {
    int m1;
    double m2,m3;
  };
  S2 s21 = { 1, 2, 3.0 };       // ok
  S2 s22 { 1.0, 2, 3 };         // error: narrowing
  S2 s23 {};                    // ok: default to 0,0,0

通过了解上面的示例,我想屏幕前的你应该知道怎么修改代码也是可以实现下面两行代码的功能。

127      Context_Struct createInfo;
128      memset(&createInfo, 0, sizeof(Context_Struct));

最后我们修改后的代码如下:

127      Context_Struct createInfo{};

综上两种解决方案,建议优先考虑方案方案二,其次再考虑方案一。

对象初始值设零

在后期的代码移植中,发现了非声明初始化的情况。即在 *.h 中声明该变量,在对应的 .cc 文件中使用前需要将该对象的初始值设为零。
部分关键代码如下所示:

152   memset(&createInfo_m, 0, sizeof(Context_Struct));
153   createInfo_m.value = 3;
154   createInfo_m.LenCode = l_code;

此时编译错误一般为:

Trans.cc:152:77: error: ‘void* memset(void*, int, size_t)’ clearing an object of type ‘Context_Struct’ {aka ‘struct Context_Struct’} with no trivial copy-assignment; use assignment or value-initialization instead [-Werror=class-memaccess]
 152  |        memset(&createInfo_m, 0, sizeof(Context_Struct));
      |                                                                             ^
In file included from ./src/inter/Trans.cc:1:
./src/interType.h:126:20: note: ‘Context_Struct’ {aka ‘struct Context_Struct’} declared here
  126 |     typedef struct Context_Struct
      |                    ^~~~~~~~~~~~~~~~~~~~~~~~
cc1plus: all warnings being treated as errors

通过查看 cppreference.com 中的 Zero initialization 章节,发现,针对上面的场景,可以使用下面的两种方式来进行对象的初始值设为零(0)操作。

方法一

152   //memset(&createInfo_m, 0, sizeof(Context_Struct));
153   createInfo_m = Context_Struct{};
154   createInfo_m.value = 3;
155   createInfo_m.LenCode = l_code;

方法二

152   //memset(&createInfo_m, 0, sizeof(Context_Struct));
153   createInfo_m = {};
154   createInfo_m.value = 3;
155   createInfo_m.LenCode = l_code;

尝试过方法一和方法二,都是可以了。推荐方法一,显示会更加清晰一些。

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-05-08 08:30:52  更:2022-05-08 08:35:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 17:19:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码