
在上一节中,我们讨论了 C 语言程序中“内存对齐”的概念和原因。归根结底,就是效率最大化。毕竟,高效率是C语言程序的一个重要特点。
内存对齐很简单
看看这个面试问题
在浏览外文网站时,发现了一个关于“内存对齐”的面试题。原提问者完全没有这个面试题的概念,连谷歌都忍不住了。题目是这样的:
memset_16aligned() 函数需要一个 16 字节对齐的指针作为参数,否则它会崩溃。a) 你能分配 1024 字节的内存并将其对齐 16 字节吗?b) 分配后,传给memset_16aligned()函数执行,释放这块内存。
{
无效*内存;
无效*ptr;
// 在这里回答 a)
memset_16aligned(ptr, 0, 1024);
// 在这里回答 b)
}
看看这个面试问题
解析
题目要求传给memset_16aligned()函数的ptr参数是16字节对齐的,这很容易实现,只要ptr的地址值是16的整数倍。进一步分析问题,我们也可以发现ptr应该指向一个1024字节的内存,所以相关的C语言代码可以写成如下:
相关C语言代码
第一步是分配足够大的内存。由于内存必须是16字节对齐的,为了以防万一c语言动态申请内存,我们额外分配了16字节,方便调整ptr指针的值。在这 16 个连续数字中,至少有一个数字必须能被 16 整除,因此在前 16 个字节的某处,必须有一个 16 字节对齐的地址。
必须有一个 16 字节对齐的地址
下一步是将 void 指针转换为 char 指针,这是为了尽量避免 void 指针上的指针到指针运算。然后将 16 添加到转换后的指针。
假设 malloc() 函数分配的内存从 0x800001 开始,显然不是 16 字节对齐的。现在将 16 添加到起始地址得到 0x800011,现在我想四舍五入到 16 字节边界,所以我可以将最后 4 位归零,即 & ~0x0F。此时我们得到0x800010,显然满足16字节对齐。
读者可以尝试对其他地址进行上述操作,看看能否得到16字节对齐的地址。
最后一步,释放分配的内存就像调用 free() 函数一样简单。但需要注意的是,传递给free()函数的地址必须是malloc()函数返回的地址c语言动态申请内存,即mem,而不是ptr,否则C语言程序会崩溃。
否则C语言程序会崩溃
题外话
可能有读者知道他们正在使用的系统中 malloc() 函数的内部实现。可能它返回的地址一定是16字节对齐的(或者8字节对齐、4字节对齐等),所以看来已经不需要了。ptr,直接用mem就好了。
但是,这是一个不可靠且不可移植的实现,因为其他平台中的 malloc() 实现可能具有不同的最小对齐方式。作为 C 程序员,除非有某些限制,否则您应该始终尝试编写可移植的 C 代码。
其他问题
这个采访问题也引起了一些争论。我认为更有趣的一点是这个观点:“主题需要分配[1024]字节!” 也就是说,这个观点强调1024字节,而我们上面的C语言代码分配了1024字节以上。
我推测面试官此时应该有两个意思。
其实上面的C语言代码就是把标题理解为“分配一块足以容纳1024字节数据的内存”。如果面试官真的强调1024字节,那么这个问题就更有趣了。我推测面试是在这个时候。官应该有两个意思。
一是要求我们分配1024字节的内存,并将这块内存对齐到16字节。但是这样我们最终得到的可用内存实际上在 1008 到 1024 之间。
然后我们需要自定义一个内存分配器,内存分配器返回的内存起始地址必须是16字节对齐的。这样,我们在内存分配器内部的可能实现是基于上面的 C 语言代码示例,但是我们会将多余的字节隐藏在模块内部。
给它一个赞,我们走吧
欢迎在评论区讨论和提问。这些文章都是手写的和原创的。每天最简单的C语言、linux等嵌入式开发介绍,喜欢我的文章请关注一波,可以看到最新的更新和以前的文章。
禁止任何未经许可的复制。
请登录后发表评论
注册
社交帐号登录