突破编程_C++_面试(基础知识(8))

面试题20:什么内存对齐

以结构体为例来说明内存对齐:
结构体对齐是编译器在内存中布局结构体成员时遵循的一种规则。对齐的目的是提高内存访问效率,减少因内存访问不对齐而引发的性能下降或硬件异常。
在大多数系统中,数据对齐通常是按字节进行的,并且某些类型的数据(如整数和浮点数)需要按特定的对齐要求进行存储。例如,一个 4 字节的整数可能需要存储在 4 字节对齐的地址上。
编译器通常会在结构体成员之间插入填充字节以确保正确的对齐。填充字节的具体数量取决于成员的类型、大小以及编译器的对齐规则。
结构体对齐的规则可以因编译器、目标平台和编译器设置的不同而有所差异。然而,有一些常见的对齐规则:
(1)默认对齐规则:编译器通常会按照成员的顺序和它们的大小来布局结构体。它会尝试将每个成员对齐到其类型所需的对齐边界上。
(2)指定对齐:可以使用 #pragma pack 指令或 [[gnu::packed]] 属性(取决于编译器)来指定结构体的对齐方式。例如,使用 #pragma pack(1) 可以告诉编译器按照 1 字节对齐来布局结构体,从而消除所有的填充字节。
(3)最大对齐规则:在某些情况下,编译器可能会根据结构体中最大对齐要求的成员来对齐整个结构体。这意味着结构体的大小可能是其最大对齐成员的整数倍。
(4)平台和编译器特定的对齐:不同的硬件平台和编译器可能有不同的默认对齐规则。例如,某些平台可能要求双字( double-word )类型(如 double 或 long long )按 4 字节或 8 字节对齐。
如下为样例代码:

#include <iostream>struct A
{int64_t val1;char val2;int val3;int val4;
};int main() {A a = { 1,0x02,3,4 };size_t len = sizeof(A);char* tmp = new char[len] {};memcpy(tmp,&a, len);delete[] tmp;tmp = nullptr;return 0;
}

对于 struct A ,具体的分配空间方式如下:
(1)为第一个成员 val1 分配空间,其起始地址跟结构的起始地址相同(恰好偏移量 0 且为 sizeof(int64_t) 的倍数),该成员变量占用 sizeof(int64_t)=8 个字节。
(2)为第二个成员 val2 分配空间,这时下一个能够分配的地址对于结构的起始地址的偏移量为8,是 sizeof(char) 的倍数,因此把 val2 存放在偏移量为 8 的地方,该成员变量占用 sizeof(char)=1 个字节。
(3)为第三个成员 val3 分配空间,这时下一个能够分配的地址对于结构的起始地址的偏移量为 9 ,不是 sizeof(int)=4 的倍数,为了知足对齐方式对偏移量的约束问题,编译器会自动填充 3 个字节,此时分配的地址对于结构的起始地址的偏移量为12,该成员变量占用 sizeof(int)=4 个字节。
(4)为第四个成员 val4 分配空间,这时下一个能够分配的地址对于结构的起始地址的偏移量为 16 ,正好是 sizeof(int)=4 的倍数,因此把 val4 存放在偏移量为 16 的地方,该成员变量占用 sizeof(int)=4 个字节。
(5)这时整个结构的成员变量已经都分配了空间,总的占用的空间大小为:8+1+3+4+4=20,不是结构中占用最大空间的类型所占用的字节数( sizeof(double)=8 )的倍数,所以编译器还要填充 4 个字节,因此整个结构的大小为:sizeof(A)=8+1+3+4+4+3=20。
如下为调试中打印出来的内存字节(变量 tmp ):

-		tmp,24	0x00000281e0a73900 "\x1\0\0\0\0\0\0\0\x2ÌÌÌ\x3\0\0\0\x4\0\0\0ÌÌÌÌ"	char[24][0]	1 '\x1'	char[1]	0 '\0'	char[2]	0 '\0'	char[3]	0 '\0'	char[4]	0 '\0'	char[5]	0 '\0'	char[6]	0 '\0'	char[7]	0 '\0'	char[8]	2 '\x2'	char[9]	-52 'Ì'	char[10]	-52 'Ì'	char[11]	-52 'Ì'	char[12]	3 '\x3'	char[13]	0 '\0'	char[14]	0 '\0'	char[15]	0 '\0'	char[16]	4 '\x4'	char[17]	0 '\0'	char[18]	0 '\0'	char[19]	0 '\0'	char[20]	-52 'Ì'	char[21]	-52 'Ì'	char[22]	-52 'Ì'	char[23]	-52 'Ì'	char

注意其中 [9] 至 [11] 以及 [20] 至 [23] 是编译器填充的字节。
使用 #pragma pack(n) 可以指定一个对齐值 n,它告诉编译器按照 n 字节的边界进行对齐。这意味着编译器不会在成员之间插入更多的填充字节,除非这样做是为了确保下一个成员按照其类型所需的对齐要求进行存储。
例如,如果需要让结构体的成员紧密排列,不插入任何填充字节,可以使用 #pragma pack(1):

#include <iostream>#pragma pack(push, 1) // 保存当前对齐设置,并设置对齐为1字节  
struct A
{int64_t val1;char val2;int val3;int val4;
};
#pragma pack(pop) // 恢复之前的对齐设置  int main() {A a = { 1,0x02,3,4 };size_t len = sizeof(A);char* tmp = new char[len] {};memcpy(tmp,&a, len);// A 的大小将是 17 字节,而不是默认的可能是 24 字节(取决于平台和编译器)return 0;
}

需要注意的是,过度使用 #pragma pack 并将其设置得很小(如 1 )可能会导致内存访问性能下降,因为数据可能不会按照处理器最优的方式对齐。此外,某些硬件平台可能要求特定的对齐方式,因此在使用 #pragma pack 时需要谨慎考虑其对性能和可移植性的影响。

面试题21:结构体初始化由哪些方式

(1)默认初始化
当声明一个局部结构体变量时,如果没有显式地初始化它,那么它的成员将进行默认初始化。(通常是随机值,取决于内存的内容):

struct A
{int64_t val1;int64_t val2;
} ;A a;

变量 a 的值为:

a = {val1=-3689348814741910324 val2=-3689348814741910324 }

(2)聚合初始化
聚合初始化使用花括号 {} 来初始化结构体的成员。:

struct A
{int64_t val1;int64_t val2;
} ;A a = {1, 2}; 

变量 a 的值为:

a = {val1=1 val2=2 }

(3)指定成员初始化
在C++11及以后的版本中,可以使用指定成员初始化来明确指定要初始化的成员。这个特性适用于仅需要初始化部分成员变量:

struct A
{int64_t val1;int64_t val2;
} ;A a = { a.val1 = 1 };

变量 a 的值为:

a = {val1=1 val2=0 }

(4)构造函数初始化:
如果结构体有定义构造函数,那么可以使用构造函数来初始化结构体的成员。构造函数可以接收参数,并可以使用这些参数来初始化成员变量:

struct A
{A(int64_t val1In, int64_t val2In):val1(val1In), val2(val2In) {}int64_t val1;int64_t val2;
} ;A a(1, 2);

变量 a 的值为:

a = {val1=1 val2=2 }

(5)列表初始化:
列表初始化是 C++11 新标准中引入的一种新的初始化语法,它结合了聚合初始化和指定成员初始化的特点:

struct A
{int64_t val1;int64_t val2;
};A a{ 1, 2 };

变量 a 的值为:

a = {val1=1 val2=2 }

面试题22:如何将结构体作为函数的参数传递

使用结构体作为函数参数时,可以选择 按值传递 或 按引用传递 。按值传递会导致结构体的复制,这可能会消耗更多的资源,特别是当结构体很大时。按引用传递则避免了复制,但需要注意的是,如果再函数体内修改了引用的结构体,那么原始的结构体也会被修改。如果不希望原始结构体被修改,可以使用常量引用(这是最常用的结构体传参方式):

#include <iostream>
#include <string>using namespace std;struct Student
{string name;	//姓名    uint32_t age;	//年龄    double score;	//分数
};// 使用常量结构体引用作为函数参数
void printStudent(const Student& st)
{printf("name = %s, age = %u, score = %lf\n", st.name.c_str(), st.age, st.score);
}int main()
{Student st = { "zhangsan",10,98.5 };printStudent(st);return 0;
}

上面代码的输出为:

name = zhangsan, age = 10, score = 98.500000

面试题23:什么是结构体的位字段

结构体中的位字段变量( bit-field )是一种特殊的成员,用于在结构体中存储固定位数的数据。位域变量允许控制每个成员在内存中的确切位数,这在某些特定应用场景(如硬件编程、网络通信等)中非常有用,因为它可以实现更紧凑的数据存储和更精确的内存布局。
位域变量的声明方式是在结构体的成员声明中指定一个冒号和一个整数,这个整数表示该成员应该占用的位数。如下为样例代码:

#include <iostream>
#include <string>
#include <tuple>using namespace std;struct A {uint32_t val1 : 1; // 占用 1 个位  uint32_t val2 : 2; // 占用 2 个位  uint32_t val3 : 3; // 占用 3 个位   // 注意:编译器可能会插入填充位以满足内存对齐要求 ,不同编译器的 sizeof(A) 可能不一样
};int main() {A a;// 设置位域变量的值  a.val1 = 1; // 二进制:1 a.val2 = 2; // 二进制:10  a.val3 = 4; // 二进制:100  printf("val1 = %u, val2 = %u, val3 = %u\n", a.val1, a.val2, a.val3);printf("sizeof(A) = %u\n", sizeof(A));return 0;
}

上面代码的输出为:

val1 = 1, val2 = 2, val3 = 4
sizeof(A) = 4

注意,上面结构体 A 的理论大小为 6 个字节 ,不到 1 个字节,但是经过内存对齐,结构体 A 的实际大小为 4 个字节。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/672656.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pydantic了解学习

文章目录 什么是pydantic安装pydanticpydantic主要特性数据验证数据解析和序列化简化数据处理 什么是pydantic Pydantic是一个Python库&#xff0c;用于数据解析和验证。通过定义类模型并设定类型注解&#xff0c;Pydantic可以确保我们在处理数据时&#xff0c;数据的格式和类…

Java学习15-- 面向对象学习3. 对象的创建分析【★】

&#xff08;本章看不懂多读几遍&#xff0c;弄懂后再往下章看&#xff09; 面向对象学习3. 对象的创建分析 Java Memory Structure: 如上图所示&#xff1a; 主要分为Stack和Heap Memory 其中Stack主要放method包括main 程序从main开始所以main最先进入Stack&#xff0c;等…

【Langchain+Streamlit】打造一个旅游问答AI

利用LangchainStreamlit打造一个交互简单的旅游问答AI机器人&#xff0c;如果你有openai账号,可以按照如下的网址直接体验&#xff0c;如果你没有的话可以站内私信博主要一下临时key体验一下&#xff1a; 产品使用传送门—— http://101.33.225.241:8501/ 这里有演示效果和代码…

蓝桥杯(Web大学组)2023省赛真题3:收集帛书碎片

需要实现&#xff1a; 1.将二维数组转为一维数组&#xff1b; 2.数组去重 一、将二维数组转为一维数组&#xff1a; 二、数组去重&#xff1a; function collectPuzzle(...puzzles) {// console.log(puzzles);// console.log(...puzzles);// TODO:在这里写入具体的实现逻辑/…

论文阅读-Examining Zero-Shot Vulnerability Repair with Large Language Models

1.本文主旨&#xff1a; 这篇论文探讨了使用大型语言模型&#xff08;LLM&#xff09;进行零射击漏洞修复的方法。人类开发人员编写的代码可能存在网络安全漏洞&#xff0c;新兴的智能代码补全工具是否能帮助修复这些漏洞呢&#xff1f;在本文中&#xff0c;作者研究了大型语言…

最新话费充值系统源码,附带系统安装教程

搭建教程 亲测环境&#xff1a;PHP7.0MySQL5.6 PHP扩展安装&#xff1a;sg11 数据库配置文件路径&#xff1a;/config/database.php 伪静态设置为thinkphp 后台地址&#xff1a;/admin 账号密码&#xff1a;admin/123456

ASP.NET Core MVC 控制查询数据表后在视图显示

如果是手动写代码&#xff0c;不用VS自带的一些控件&#xff0c;那比较简单的方式就是把查询的数据集&#xff0c;逐条赋给对象模型&#xff0c;再加到List&#xff0c;最后在控制加到 ViewBag&#xff0c;视图循环显示ViewBag变量 控制器代码 List<Users> list new Li…

node.js后端+小程序前端+mongoDB(增删改查)

前言 今天我对比了以下node.js的express与python的fastAPI&#xff0c;我决定我还是出一期关于node.jsmangoDB小程序的小案例吧。 不是python的fastAPI不好用&#xff0c;因为fastAPI是python较新的技术&#xff0c;我不敢果断发出教学文章&#xff08;这件事情还是留着给pyt…

[机器学习]K-means——聚类算法

一.K-means算法概念 二.代码实现 # 0. 引入依赖 import numpy as np import matplotlib.pyplot as plt # 画图依赖 from sklearn.datasets import make_blobs # 从sklearn中直接生成聚类数据# 1. 数据加载 # 生成&#xff08;n_samples&#xff1a;样本点&#xff0c;centers&…

PostgreSQL不停机迁移数据

通常涉及到数据迁移&#xff0c;常规操作都是停服务更新。不停机迁移数据是相对比较高级的操作。 不停机数据迁移在本质上&#xff0c;可以视作由三个操作组成&#xff1a; 复制&#xff1a;将目标表从源库逻辑复制到宿库。改读&#xff1a;将应用读取路径由源库迁移到宿库上…

从零开始手写mmo游戏从框架到爆炸(零)—— 导航

从今天开始我们尝试从零开始写一个mmo的游戏。主要技术还是netty。参考了网上很多的大神的框架&#xff0c;本来希望基于ioGame或者vert.x等来直接写功能的&#xff0c;觉得从零开始更有意义&#xff0c;而且咱们也不需要太NB的底层功能&#xff0c;够用就行。 下面是导航&…

HarmonyOS开发工具DevEco Studio安装以及汉化

HUAWEI DevEco Studio 面向HarmonyOS应用及元服务开发者提供的集成开发环境(IDE)&#xff0c; 助力高效开发。 应用内共享HSP开发 支持在Stage模型和模块化编译方式下开发HSP&#xff0c;以及共享HSP给应用内其他模块使用;支持运行态共享HSP。Code Linter代码检查 支持ArkTS/T…

幻兽帕鲁转移/迁移游戏存档之后,无法迁移角色存档,进入游戏需要重新建角色问题(已解决),服务器到服务器之间的存档转移

很多朋友在迁移幻兽帕鲁游戏存档到服务器的时候&#xff0c;可能会遇到一个问题&#xff0c;就是迁移完成后&#xff0c;进入到游戏会发现又需要从头开始&#xff0c;重新新建角色。 其实这个问题也很好解决&#xff0c;因为Palworld服务端有两种&#xff0c;一种是有APPID&…

SpringFramework实战指南(六)

SpringFramework实战指南(六) 4.4 基于 配置类 方式管理 Bean4.4.1 完全注解开发理解4.4.2 实验一:配置类和扫描注解4.4.3 实验二:@Bean定义组件4.4.4 实验三:高级特性:@Bean注解细节4.4.5 实验四:高级特性:@Import扩展4.4.6 实验五:基于注解+配置类方式整合三层架构组…

vue项目集成booststrap

1.首先安装bootstrap npm install bootstrap 我安装的是4.3的版本 2.在main.js中引用bootstrap import bootstrap/dist/css/bootstrap.css import bootstrap/dist/css/bootstrap.min.css import bootstrap/dist/js/bootstrap.js import bootstrap/dist/js/bootstrap.min.…

(Python)字典列表数据本地存储工具

前言 一个简单的实现简便 "列表字典" 数据存储本地。 适合不会SQL但又想实现数据存储本地的同学。 操作使用都非常简单。 文件只做了简单的加密处理&#xff0c;如果需要复杂加密的同学可以修改加密函数。 温馨提示&#xff1a; 1.使用前&#xff0c;在项目目录…

CentOS7如何安装宝塔面板并实现固定公网地址远程访问

文章目录 一、使用官网一键安装命令安装宝塔二、简单配置宝塔&#xff0c;内网穿透三、使用固定公网地址访问宝塔 宝塔面板作为建站运维工具&#xff0c;适合新手&#xff0c;简单好用。当我们在家里/公司搭建了宝塔&#xff0c;没有公网IP&#xff0c;但是想要在外也可以访问内…

使用Spring AOP实现对外接口的日志自动打印

文章目录 一、引言二、使用AOP实现日志打印三、logback单独打印api调用信息 一、引言 相信我们都有过这样的经历&#xff0c;在提供第三方对外接口时&#xff0c;为了防止推诿扯皮&#xff0c;我们会在自己接口被调用时日志打印一下第三方的调用参数&#xff0c;再在业务逻辑结…

数据结构第十一天(栈)

目录 前言 概述 源码&#xff1a; 主函数&#xff1a; 运行结果&#xff1a; ​编辑 前言 今天简单的实现了栈&#xff0c;主要还是指针操作&#xff0c;soeasy! 友友们如果想存储其他内容&#xff0c;只需修改结构体中的内容即可。 哈哈&#xff0c;要是感觉不错&…

[React] ref属性

简介 ref 即 reference &#xff0c;是 React 提供给我们的安全访问 DOM 元素或者某个组件实例的句柄。 组件被调用时会新建一个该组件的实例&#xff0c;而 ref 就会指向这个实例。它可以是一个回调函数&#xff0c;这个回调函数会在组件被挂载后立即执行。 为了防止内存泄漏…