Cin*_*lia 64 c string malloc pointers c-strings
令人惊讶的简单/愚蠢/基本问题,但我不知道:假设我想让我的函数的用户返回一个C字符串,其长度我不知道在函数的开头.我可以在开始时仅在长度上放置上限,并且根据处理,尺寸可能缩小.
问题是,分配足够的堆空间(上限)然后在处理期间终止字符串是否有任何问题?即如果我在分配的内存中间插入'\ 0',(a.)是否free()仍能正常工作,并且(b.)'\ 0'之后的空格变得无关紧要?一旦'\ 0'被添加,内存是否会被返回,或者它是否位于占用空间直到free()被调用?将这个悬空空间留在那里通常是不好的编程风格,为了节省一些前期编程时间,在调用malloc之前计算必要的空间?
为了给出这个上下文,假设我想删除连续的重复项,如下所示:
输入"你好oOOOo !!" - >输出"Helo oOo!"
...以及下面的一些代码显示了我如何预先计算操作产生的大小,有效地执行两次处理以使堆大小正确.
char* RemoveChains(const char* str)
{
if (str == NULL) {
return NULL;
}
if (strlen(str) == 0) {
char* outstr = (char*)malloc(1);
*outstr = '\0';
return outstr;
}
const char* original = str; // for reuse
char prev = *str++; // [prev][str][str+1]...
unsigned int outlen = 1; // first char auto-counted
// Determine length necessary by mimicking processing
while (*str) {
if (*str != prev) { // new char encountered
++outlen;
prev = *str; // restart chain
}
++str; // step pointer along input
}
// Declare new string to be perfect size
char* outstr = (char*)malloc(outlen + 1);
outstr[outlen] = '\0';
outstr[0] = original[0];
outlen = 1;
// Construct output
prev = *original++;
while (*original) {
if (*original != prev) {
outstr[outlen++] = *original;
prev = *original;
}
++original;
}
return outstr;
}
Run Code Online (Sandbox Code Playgroud)
Ton*_*roy 51
如果我将'\ 0'粘贴到已分配内存的中间,那么
(a.)free()仍能正常工作,并且
是.
(b.)'\ 0'之后的空格变得无关紧要?一旦'\ 0'被添加,内存是否会被返回,或者它是否会占用空间直到调用free()?
要看.通常,当您分配大量堆空间时,系统首先分配虚拟地址空间 - 当您向页面写入时,会分配一些实际物理内存来备份它(当您的操作系统具有虚拟内存时,可能会在以后交换到磁盘)支持).众所周知,虚拟地址空间的浪费分配与实际物理/交换存储器之间的这种区别允许稀疏阵列在这样的OS上具有合理的存储器效率.
现在,这个虚拟寻址和分页的粒度是内存页面大小 - 可能是4k,8k,16k ......?大多数操作系统都有一个功能,您可以调用它来查找页面大小.所以,如果你做了很多小的分配,那么四舍五入到页面大小是浪费的,如果相对于你真正需要使用的内存量有一个有限的地址空间,那么取决于上述方式的虚拟寻址将无法扩展(例如,具有32位寻址的4GB RAM).另一方面,如果你有一个64位的进程运行,比如说32GB的RAM,并且正在进行相对较少的这种字符串分配,那么你可以使用大量的虚拟地址空间,并且可以获得更大的页面大小.吨相当多.
但是 - 请注意在整个缓冲区中写入然后在某个早期点(在这种情况下,一次写入内存将具有后备内存并最终可能在交换中)之间的区别,而不是只有一个只写入的大缓冲区到第一位然后终止(在这种情况下,后备存储器仅分配给四舍五入到页面大小的已用空间).
值得指出的是,在许多操作系统上,堆内存可能不会返回到操作系统,直到进程终止:相反,malloc/free库会在需要扩展堆时通知操作系统(例如sbrk()在UNIX或VirtualAlloc()Windows上使用) ).从这个意义上说,free()内存对于您的进程来说是免费的,但是对于其他进程来说不能免费使用.某些操作系统会对此进行优化 - 例如,使用独特且可独立释放的内存区域进行非常大的分配.
将这个悬空空间留在那里通常是不好的编程风格,为了节省一些前期编程时间,在调用malloc之前计算必要的空间?
同样,这取决于您正在处理的分配数量.如果相对于您的虚拟地址空间/ RAM有很多相关内容 - 您希望明确地让内存库知道并非实际需要使用所有最初请求的内存realloc(),或者您甚至可以strdup()根据实际情况更紧密地分配新块需求(然后free()是原始的) - 取决于你的malloc /免费库实现可能会更好或更糟,但很少有应用程序会受到任何差异的显着影响.
有时你的代码可能在一个库里,你无法猜测调用应用程序将管理多少个字符串实例 - 在这种情况下,最好提供从不太糟糕的较慢行为...所以倾向于缩小内存块以缩小拟合字符串数据(一组额外的操作,因此不影响大O效率),而不是浪费原始字符串缓冲区的未知比例(在病态情况下 - 在任意大的分配之后使用零个或一个字符).作为性能优化,如果未使用的空间> =已用空间 - 调整到味道,或者使其可调用,则可能只会为返回内存而烦恼.
你评论另一个答案:
因此,归结为判断realloc是否需要更长时间,还是预处理大小确定?
如果性能是您的首要任务,那么是 - 您想要分析.如果你不受CPU约束,那么作为一般规则采取"预处理"命中并进行正确大小的分配 - 只有更少的碎片和混乱.如果你必须为某些函数编写一个特殊的预处理模式 - 这是一个额外的"表面",用于存储错误和代码.(实现你自己时,通常需要这种权衡决定asprintf()从snprintf(),但至少你可以信任snprintf()的记载和不亲自必须保持它采取行动).
cni*_*tar 35
一旦'\ 0'被添加,内存是否会被返回,或者它是否会占用空间直到调用free()?
什么都没有什么神奇之处\0.realloc如果要"缩小"已分配的内存,则必须调用.否则记忆将只是坐在那里直到你打电话free.
如果我将"\ 0"粘贴到已分配内存的中间,(a.)free()是否仍能正常工作
无论你在内存中 做什么,free如果你传递完全相同的指针,它将始终正常工作malloc.当然,如果你在外面写字,所有的赌注都会被取消.
Nav*_*een 11
\0从只是一个或多个字符malloc和free角度来看,他们不关心你把数据存储在存储器.所以free仍然会工作,你是否添加\0在中间或不添加\0的.分配的额外空间仍然存在,一旦添加\0到内存中,它就不会返回到进程.我个人更愿意只分配所需的内存量而不是在某个上限分配,因为这只会浪费资源.
malloc只是分配了一块内存..它可以根据你的需要使用,并从初始指针位置自由调用...在中间插入'\ 0'没有任何后果......
具体来说,malloc不知道你想要什么类型的内存(它返回一个void指针)..
我们假设你希望从0x10到0x19分配10个字节的内存.
char * ptr = (char *)malloc(sizeof(char) * 10);
Run Code Online (Sandbox Code Playgroud)
在第5个位置(0x14)插入空值不会释放内存0x15 ...
但是从0x10中释放出10个字节的整个块.