c语⾔中⾸地址是什么意思_这个C语⾔⾯试题看起来很懵,如何分配⼀段16字节对齐的内存?...
上⼀节我们讨论了C语⾔程序中“内存对齐”的概念以及原因,其实归根结底都是为了效率最⼤化,毕竟⾼效率是C语⾔程序的⼀个重要特性。
内存对齐很简单
来看看这个⾯试题
浏览外⽂⽹站时,我发现了⼀个关于“内存对齐”的⾯试题⽬,原题主对这⼀个⾯试题完全没有概念,即使⾕歌之也没有办法。题⽬是这样的:
memset_16aligned() 函数需要⼀个 16 字节对齐的指针作为参数,否则就会崩溃。a) 你能分配 1024 字节内存,并且将其 16 字节对齐吗?b) 分配后,将其传递给 memset_16aligned() 函数执⾏后,释放这段内存。
{ void *mem; void *ptr; // answer a) here memset_16aligned(ptr, 0, 1024); // answer b) here }
来看看这个⾯试题
解析
题⽬要求传递给 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 字节对齐的地址。
前 16 个字节的某处,必定有⼀个 16 字节对齐的地址
下⼀步是将 void 指针转换为 char 指针,这是为了尽量避免对 void 指针指向指针算术。然后对转换后的指针加上 16。
假设 malloc() 函数分配的内存起始地址为 0x800001,显然未 16 字节对齐。现在对起始地址加 16,得到 0x800011,现在我想四舍五⼊到 16 字节边界,所以可以将最后的 4 位置 0,也即 & ~0x0F。此时我们得到了 0x800010,这个地址显然满⾜ 16 字节对齐。嵌入式系统是什么意思
读者可以⾃⼰尝试对其他地址执⾏上述操作,观察是否能够得到 16 字节对齐的地址。
最后⼀步,释放分配的内存很简单,只需调⽤ free() 函数就可以了。但是应该注意,传递给 free() 函数的必须是 malloc() 函数返回的地址,也即 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等嵌⼊式开发,喜欢我的⽂章就关注⼀波吧,可以看到最新更新和之前的⽂章哦。
未经许可,禁⽌转载。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论