上一节我们讨论了C语言程序中“内存对齐”的概念以及原因,其实归根结底都是为了效率最大化,毕竟高效率是C语言程序的一个重要特性。
![3d86fd8b77ff3c4763a2cc9d9b59cfea.png](https://i-blog.csdnimg.cn/blog_migrate/323a53819d8f72b225a69123c3528960.jpeg)
内存对齐很简单
来看看这个面试题
浏览外文网站时,我发现了一个关于“内存对齐”的面试题目,原题主对这一个面试题完全没有概念,即使谷歌之也没有办法。题目是这样的:
memset_16aligned() 函数需要一个 16 字节对齐的指针作为参数,否则就会崩溃。a) 你能分配 1024 字节内存,并且将其 16 字节对齐吗?b) 分配后,将其传递给 memset_16aligned() 函数执行后,释放这段内存。
{ void *mem; void *ptr; // answer a) here memset_16aligned(ptr, 0, 1024); // answer b) here }
![832d754f4137a14aaa9a71de0496e5f3.png](https://i-blog.csdnimg.cn/blog_migrate/e1e6ba60b73af8549898d6487508f212.jpeg)
来看看这个面试题
解析
题目要求传递给 memset_16aligned() 函数的 ptr 参数是 16 字节对齐的,这很好实现,只要保证 ptr 的地址值是 16 的整数倍就可以了。进一步分析问题,还能够发现,ptr 应该指向一段 1024 字节的内存,因此相关的C语言代码可以这样写:
{ void *mem = malloc(1024+16); void *ptr = ((char *)mem+16) & ~(char *)0x0F; memset_16aligned(ptr, 0, 1024); free(mem);}
第一步就是分配一块足够大的内存,由于内存必须是 16 字节对齐的,以防万一,我们多分配了 16 字节,便于调整 ptr 指针的值。16 个连续数字里,必定至少有一个数能够被 16 整除,因此在前 16 个字节的某处,必定有一个 16 字节对齐的地址。
![5b99bc6f027be8f45c43a93d25193eb8.png](https://i-blog.csdnimg.cn/blog_migrate/c38a3895146069f629c48a5ed99ceb47.jpeg)
前 16 个字节的某处,必定有一个 16 字节对齐的地址
下一步是将 void 指针转换为 char 指针,这是为了尽量避免对 void 指针指向指针算术。然后对转换后的指针加上 16。
假设 malloc() 函数分配的内存起始地址为 0x800001,显然未 16 字节对齐。现在对起始地址加 16,得到 0x800011,现在我想四舍五入到 16 字节边界,所以可以将最后的 4 位置 0,也即 & ~0x0F。此时我们得到了 0x800010,这个地址显然满足 16 字节对齐。
读者可以自己尝试对其他地址执行上述操作,观察是否能够得到 16 字节对齐的地址。
最后一步,释放分配的内存很简单,只需调用 free() 函数就可以了。但是应该注意,传递给 free() 函数的必须是 malloc() 函数返回的地址,也即 mem,而不能是 ptr,否则C语言程序就会崩溃。
![036ad8a1fe67f1e803fd9c4333f5c80d.png](https://i-blog.csdnimg.cn/blog_migrate/9d59bea188b0d04a29c4b7be6e81a197.jpeg)
否则C语言程序就会崩溃
题外话
可能有读者知道自己使用的系统中 malloc() 函数的内部实现,可能它返回的地址必定是 16 字节对齐的(或者 8 字节对齐,4 字节对齐等等),那么似乎就不再需要 ptr,直接使用 mem 就可以了。
然而,这是不可靠的也是不可移植的实现,因为其他平台中的 malloc() 实现可能具有不同的最小对齐。作为C语言程序员,除非有某些限制,否则总是应该尝试写出可移植的C语言代码。
其他问题
这个面试题目也引发了一些争论,我觉得比较有意思的是这样的一个观点:“题目要求分配的是 [1024] 个字节!”也即这个观点强调 1024 字节,而我们上面的C语言代码分配的不止 1024 个字节。
![8bbf42b2340e6a0868a72bc347c4880f.png](https://i-blog.csdnimg.cn/blog_migrate/b014631c3e166dc6081ddc99ef52ac2b.jpeg)
我揣测此时面试官应该有两种意思
其实,上面的C语言代码是将题目理解成“分配一款足以容纳 1024 字节数据的内存”了,如果面试官真的强调了 1024 字节,那么问题就更加有趣了,我揣测此时面试官应该有两种意思。
一是要求我们分配 1024 字节的内存,并对这段内存做 16 字节对齐处理。可是这样我们最终得到的可用内存实际上是 1008 到 1024 之间的大小。
再就是要求我们自定义一个内存分配器,该内存分配器返回的内存起始地址必定是 16 字节对齐的。这样一来,我们在内存分配器内部可能的实现就是基于上面的C语言代码示例了,只不过,我们会把多出的字节隐藏在模块内部了。
![ba3d6be9d208d2d99d194144fb7c3f26.png](https://i-blog.csdnimg.cn/blog_migrate/83bae32e8783a5a702b9f3d2145a3371.jpeg)
点个赞再走吧
欢迎在评论区一起讨论,质疑。文章都是手打原创,每天最浅显的介绍C语言、linux等嵌入式开发,喜欢我的文章就关注一波吧,可以看到最新更新和之前的文章哦。
未经许可,禁止转载。