上篇聊了递归有人后台问我struct这么简单也值得写一篇文章值。而且我可以很负责任地说struct在面试里考得比你想象的频繁得多。特别是机器人方向你写传感器数据结构、定义机器人状态、封装控制指令……哪一样离得开struct讲个真事。前阵子面试一个应届生我让他定义一个激光雷达点的数据结构。他写了struct Point { float x; float y; float z; int intensity; };我问他这个结构体占多少字节他想都没想16字节。三个float各4字节一个int 4字节加起来16。我说你确定用sizeof跑一下看看。他跑了一下——16。嗯这次蒙对了。但我追问了一句如果把int放到第一个字段顺序变一下还是16字节吗他犹豫了。这就是struct在面试里最爱考的点内存对齐。很多人用了好几年struct从来没搞明白为什么结构体的大小不等于各字段大小之和。struct是什么不只是把几个变量打包在一起课本上对struct的定义通常是一种可以包含多个不同类型数据的数据结构。没错但太笼统了。struct的本质是什么在内存中开辟一块连续的空间把相关的变量按顺序放进去。注意关键词连续、按顺序。这意味着struct里的字段不是随意摆放的它们的排列方式直接影响内存占用和访问效率。在机器人开发里struct用得特别多。比如你要定义一个传感器数据结构struct IMUData { double timestamp; // 时间戳 float acc_x, acc_y, acc_z; // 加速度 float gyro_x, gyro_y, gyro_z; // 角速度 bool is_valid; // 数据是否有效 };这个结构体把IMU传感器的所有数据打包在一起传递的时候只需要传一个IMUData不用传七八个散落的变量。但问题来了这个结构体到底占多少字节内存对齐面试中最容易算错的题这就是刚才那个候选人踩坑的地方。编译器在放置结构体字段时不是简单地一个挨一个放。它会让每个字段的起始地址对齐到某个整数倍——这个倍数通常是字段本身的大小。比如double要放在8的整数倍地址上float要放在4的整数倍地址上。如果上一个字段结束后下一个字段的对齐位置还没到编译器就会在中间填充padding。拿刚才的IMUData来算timestampdouble8字节从偏移0开始acc_x, acc_y, acc_z三个float各4字节从偏移8开始共12字节gyro_x, gyro_y, gyro_z三个float各4字节从偏移20开始共12字节is_validbool1字节从偏移32开始到这儿总共33字节。但整个结构体的大小要对齐到最大字段double8字节的整数倍所以会填充到40字节。实际是多少你可以用sizeof(IMUData)验证一下。面试的时候面试官特别喜欢让你手算结构体大小。如果你不知道内存对齐的规则算出来的数一定不对。有个小技巧把占用空间大的字段放在前面小的放在后面可以减少padding节省内存。// 优化前40字节 struct IMUData_v1 { bool is_valid; // 1字节 7字节padding double timestamp; // 8字节 float acc_x, acc_y, acc_z; float gyro_x, gyro_y, gyro_z; }; // 优化后36字节 struct IMUData_v2 { double timestamp; // 8字节 float acc_x, acc_y, acc_z; // 12字节 float gyro_x, gyro_y, gyro_z; // 12字节 bool is_valid; // 1字节 3字节padding };在机器人开发里如果你的传感器数据量很大比如一帧点云几十万个点每个点结构体省几个字节总体内存差距就很可观了。还有一种更激进的做法是用#pragma pack(1)强制取消对齐让结构体按字面大小排列。不过这种写法在嵌入式开发里才常见普通机器人开发不建议随便用因为某些CPU架构访问未对齐数据会直接报错。struct和class到底什么区别面试高频题struct和class有什么区别标准答案是默认访问权限不同。struct默认是publicclass默认是private。就这一个区别。在功能上它们几乎完全一样——都能有成员函数、都能继承、都能有构造函数。但实际开发中大家有个约定俗成的习惯struct用来表示纯数据class用来表示有行为的对象。比如你定义一个传感器数据用structstruct LidarScan { std::vectorfloat ranges; std::vectorfloat angles; double timestamp; };但如果你定义一个传感器驱动有初始化、读取、校准等行为用classclass LidarDriver { public: bool initialize(); LidarScan readScan(); void calibrate(); private: int fd; std::string port; };这不是语法要求是代码风格。但在面试里如果你能说出这个约定面试官会觉得你有工程素养。我见过有人把所有东西都写成class连个纯数据包也搞一堆getter/setter。说实话没必要。struct就是为纯数据准备的简单直接。struct在机器人开发里的实战经验在机器人开发里struct有几个特别常见的用法。第一定义消息类型。ROS2里的msg文件编译出来就是struct。你也可以手动定义struct Twist { Vector3 linear; Vector3 angular; };第二定义配置参数。机器人的各种参数打包成一个struct传给初始化函数struct RobotConfig { double max_speed; double wheel_radius; double wheel_base; int control_freq; };这样比传一堆散落的参数清晰多了。第三定义状态。机器人的运行状态、错误码用struct比用一堆全局变量干净struct RobotState { enum Mode { IDLE, RUNNING, ERROR }; Mode mode; int error_code; std::string error_msg; };面试的时候如果你能举出这些实际场景比只说struct可以包含不同类型的数据有说服力得多。给正在准备面试的你一点建议struct看着简单但面试考得细。我建议你做三件事第一搞清楚内存对齐的规则能手算常见结构体的大小。这个几乎每场面试都会问。第二知道struct和class的区别不只是语法层面还有使用习惯上的约定。第三去看看ROS2的message定义看看人家是怎么用struct组织传感器数据的。理解为什么这么设计比知道怎么用更重要。如果这篇文章对你有帮助欢迎点赞、在看、转发三连。 你的支持是我持续更新的最大动力。「机器人软件开发面试·从入门到精通」连载系列上一篇第8篇 递归函数——运动学求解中的常见思路 下一篇预告第10篇 联合体与枚举——状态机设计中的常用工具有任何问题欢迎评论区留言我会尽量回复。
结构体struct面试必考:内存对齐、和class的区别、机器人里的实战用法
上篇聊了递归有人后台问我struct这么简单也值得写一篇文章值。而且我可以很负责任地说struct在面试里考得比你想象的频繁得多。特别是机器人方向你写传感器数据结构、定义机器人状态、封装控制指令……哪一样离得开struct讲个真事。前阵子面试一个应届生我让他定义一个激光雷达点的数据结构。他写了struct Point { float x; float y; float z; int intensity; };我问他这个结构体占多少字节他想都没想16字节。三个float各4字节一个int 4字节加起来16。我说你确定用sizeof跑一下看看。他跑了一下——16。嗯这次蒙对了。但我追问了一句如果把int放到第一个字段顺序变一下还是16字节吗他犹豫了。这就是struct在面试里最爱考的点内存对齐。很多人用了好几年struct从来没搞明白为什么结构体的大小不等于各字段大小之和。struct是什么不只是把几个变量打包在一起课本上对struct的定义通常是一种可以包含多个不同类型数据的数据结构。没错但太笼统了。struct的本质是什么在内存中开辟一块连续的空间把相关的变量按顺序放进去。注意关键词连续、按顺序。这意味着struct里的字段不是随意摆放的它们的排列方式直接影响内存占用和访问效率。在机器人开发里struct用得特别多。比如你要定义一个传感器数据结构struct IMUData { double timestamp; // 时间戳 float acc_x, acc_y, acc_z; // 加速度 float gyro_x, gyro_y, gyro_z; // 角速度 bool is_valid; // 数据是否有效 };这个结构体把IMU传感器的所有数据打包在一起传递的时候只需要传一个IMUData不用传七八个散落的变量。但问题来了这个结构体到底占多少字节内存对齐面试中最容易算错的题这就是刚才那个候选人踩坑的地方。编译器在放置结构体字段时不是简单地一个挨一个放。它会让每个字段的起始地址对齐到某个整数倍——这个倍数通常是字段本身的大小。比如double要放在8的整数倍地址上float要放在4的整数倍地址上。如果上一个字段结束后下一个字段的对齐位置还没到编译器就会在中间填充padding。拿刚才的IMUData来算timestampdouble8字节从偏移0开始acc_x, acc_y, acc_z三个float各4字节从偏移8开始共12字节gyro_x, gyro_y, gyro_z三个float各4字节从偏移20开始共12字节is_validbool1字节从偏移32开始到这儿总共33字节。但整个结构体的大小要对齐到最大字段double8字节的整数倍所以会填充到40字节。实际是多少你可以用sizeof(IMUData)验证一下。面试的时候面试官特别喜欢让你手算结构体大小。如果你不知道内存对齐的规则算出来的数一定不对。有个小技巧把占用空间大的字段放在前面小的放在后面可以减少padding节省内存。// 优化前40字节 struct IMUData_v1 { bool is_valid; // 1字节 7字节padding double timestamp; // 8字节 float acc_x, acc_y, acc_z; float gyro_x, gyro_y, gyro_z; }; // 优化后36字节 struct IMUData_v2 { double timestamp; // 8字节 float acc_x, acc_y, acc_z; // 12字节 float gyro_x, gyro_y, gyro_z; // 12字节 bool is_valid; // 1字节 3字节padding };在机器人开发里如果你的传感器数据量很大比如一帧点云几十万个点每个点结构体省几个字节总体内存差距就很可观了。还有一种更激进的做法是用#pragma pack(1)强制取消对齐让结构体按字面大小排列。不过这种写法在嵌入式开发里才常见普通机器人开发不建议随便用因为某些CPU架构访问未对齐数据会直接报错。struct和class到底什么区别面试高频题struct和class有什么区别标准答案是默认访问权限不同。struct默认是publicclass默认是private。就这一个区别。在功能上它们几乎完全一样——都能有成员函数、都能继承、都能有构造函数。但实际开发中大家有个约定俗成的习惯struct用来表示纯数据class用来表示有行为的对象。比如你定义一个传感器数据用structstruct LidarScan { std::vectorfloat ranges; std::vectorfloat angles; double timestamp; };但如果你定义一个传感器驱动有初始化、读取、校准等行为用classclass LidarDriver { public: bool initialize(); LidarScan readScan(); void calibrate(); private: int fd; std::string port; };这不是语法要求是代码风格。但在面试里如果你能说出这个约定面试官会觉得你有工程素养。我见过有人把所有东西都写成class连个纯数据包也搞一堆getter/setter。说实话没必要。struct就是为纯数据准备的简单直接。struct在机器人开发里的实战经验在机器人开发里struct有几个特别常见的用法。第一定义消息类型。ROS2里的msg文件编译出来就是struct。你也可以手动定义struct Twist { Vector3 linear; Vector3 angular; };第二定义配置参数。机器人的各种参数打包成一个struct传给初始化函数struct RobotConfig { double max_speed; double wheel_radius; double wheel_base; int control_freq; };这样比传一堆散落的参数清晰多了。第三定义状态。机器人的运行状态、错误码用struct比用一堆全局变量干净struct RobotState { enum Mode { IDLE, RUNNING, ERROR }; Mode mode; int error_code; std::string error_msg; };面试的时候如果你能举出这些实际场景比只说struct可以包含不同类型的数据有说服力得多。给正在准备面试的你一点建议struct看着简单但面试考得细。我建议你做三件事第一搞清楚内存对齐的规则能手算常见结构体的大小。这个几乎每场面试都会问。第二知道struct和class的区别不只是语法层面还有使用习惯上的约定。第三去看看ROS2的message定义看看人家是怎么用struct组织传感器数据的。理解为什么这么设计比知道怎么用更重要。如果这篇文章对你有帮助欢迎点赞、在看、转发三连。 你的支持是我持续更新的最大动力。「机器人软件开发面试·从入门到精通」连载系列上一篇第8篇 递归函数——运动学求解中的常见思路 下一篇预告第10篇 联合体与枚举——状态机设计中的常用工具有任何问题欢迎评论区留言我会尽量回复。