子类化pollfd结构体大小不符无编译警告,如何检测该错误?
问题:派生类转基类指针未触发编译警告的检测方法
我写了个用来简化struct pollfd初始化的辅助类,尝试在子类pollin里加额外成员extra时,发现代码编译完全没警告,但运行时出现了预期内的错误。我本来以为不同大小的类指针转换至少需要强制类型转换,想问问用g++ 9.4.0编译时,有没有能检测出这个错误的编译标志?
代码示例
#include <poll.h> #include <vector> struct pollin : public pollfd { int extra; ///< 派生类新增的成员 pollin(int infd) { fd = infd; events = POLLIN; } }; int main(int ac, char **av) { std::vector<pollin> fds; fds.emplace_back(5); fds.emplace_back(6); poll(fds.data(), fds.size(), 0); return 0; }
错误运行行为(strace输出)
poll([{fd=5, events=POLLIN}, {fd=0, events=POLLPRI|POLLOUT}], 2, 0) = 2 ([{fd=5, revents=POLLNVAL}, {fd=0, revents=POLLOUT}])
解答
这个问题本质是数组切片错误:std::vector<pollin>存储的是大小更大的派生类对象,当你把fds.data()隐式转换成pollfd*传给poll()时,poll()会按pollfd的大小来遍历数组,导致第二个对象的内容读取错位(读到了前一个对象的extra成员和当前对象的部分数据)。
针对g++ 9.4.0,可通过以下编译标志检测这类问题:
-Wclass-memaccess:专门检测对类对象的原始内存访问越界风险,当派生类数组指针隐式转为基类指针并传递给期望基类数组的函数时,会触发警告。-Wall:开启常规的大部分警告,但-Wclass-memaccess在g++ 9中未默认包含在-Wall里,需单独指定。-Wcast-align:若派生类与基类对齐要求不同,会警告指针转换的对齐问题,但针对本次的大小错位,-Wclass-memaccess更精准。
编译命令示例:
g++ -Wclass-memaccess -Wall your_code.cpp -o your_program
另外,更彻底的解决方式是调整设计:
- 直接使用
std::vector<pollfd>作为容器,用辅助函数或工厂方法来初始化pollfd对象; - 让
pollin作为构造辅助类,提供返回pollfd实例的接口,而非继承pollfd。
内容的提问来源于stack exchange,提问作者evil otto
相关产品推荐
相关产品推荐

